diff --git a/.circleci/config.yml b/.circleci/config.yml index b3b97363e..b86b39f75 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,13 +5,8 @@ jobs: docker: - image: cimg/base:current environment: - CC: ccache gcc -m32 - PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig - LIBGME_CFLAGS: -I/usr/include - LIBGME_LDFLAGS: -lgme + CC: ccache gcc CCACHE_COMPRESS: true - WFLAGS: -Wno-unsuffixed-float-constants - GCC81: true #- image: ubuntu:trusty # environment: # CC: ccache gcc -m32 @@ -23,16 +18,11 @@ jobs: # GCC48: true resource_class: large steps: - - run: - name: Add i386 arch - command: sudo dpkg --add-architecture i386 - run: name: Add STJr PPA command: | sudo apt-get -qq update - sudo apt-get -qq -y install dirmngr - sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 0B1702D71499D9C25F986507F240F4449D3B0EC6 - echo "deb http://ppa.launchpad.net/stjr/srb2/ubuntu trusty main" | sudo tee -a /etc/apt/sources.list + sudo apt-get -qq install apt-utils - run: name: Make APT cache folder command: mkdir -p /home/circleci/.cache/apt/archives/partial @@ -49,11 +39,8 @@ jobs: keys: - v1-SRB2-APT - run: - name: Uninstall amd64 SDK - command: sudo apt-get -o Dir::Cache="/home/circleci/.cache/apt" -qq -y --no-install-recommends remove libcurl4-openssl-dev:amd64 - - run: - name: Install i386 SDK - command: sudo apt-get -o Dir::Cache="/home/circleci/.cache/apt" -qq -y --no-install-recommends install git build-essential libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 libcurl4-openssl-dev:i386 libopenmpt-dev:i386 gettext ccache wget gcc-multilib upx openssh-client + name: Install SDK + command: sudo apt-get -o Dir::Cache="/home/circleci/.cache/apt" -qq -y --no-install-recommends install git build-essential libpng-dev libsdl2-mixer-dev libgme-dev libcurl4-openssl-dev libopenmpt-dev libminiupnpc-dev gettext ccache wget gcc-multilib upx openssh-client - run: name: make md5sum command: sudo find /home/circleci/.cache/apt/archives -type f -print0 | sort -z | sudo xargs -r0 md5sum > /home/circleci/.cache/apt_archives.md5 @@ -62,28 +49,19 @@ jobs: paths: - /home/circleci/.cache/apt - checkout - - run: - name: Compile without network support - command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 -j4 - - run: - name: wipe build - command: make -C src LINUX=1 cleandep - - run: - name: rebuild depend - command: make -C src LINUX=1 clean - run: name: make master depend file - command: find make/linux/SDL/deps/ -type f -print0 | sort -z | xargs -r0 cat > make/linux/SDL.deps + command: find make/linux64/SDL/deps/ -type f -print0 | sort -z | xargs -r0 cat > make/linux64/SDL.deps - restore_cache: keys: - - v1-SRB2-{{ .Branch }}-{{ checksum "make/linux/SDL.deps" }} + - v1-SRB2-{{ .Branch }}-{{ checksum "make/linux64/SDL.deps" }} - run: name: Compile - command: make -C src LINUX=1 ERRORMODE=1 -k -j4 + command: make -C src LINUX64=1 ERRORMODE=1 -k -j4 - store_artifacts: path: /home/circleci/SRB2/bin/ destination: bin - save_cache: - key: v1-SRB2-{{ .Branch }}-{{ checksum "make/linux/SDL.deps" }} + key: v1-SRB2-{{ .Branch }}-{{ checksum "make/linux64/SDL.deps" }} paths: - /home/circleci/.ccache diff --git a/.gitattributes b/.gitattributes index c2e507352..9f0850930 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,6 +10,7 @@ /src/Make*.cfg text=auto /src/CMakeLists.txt text=auto *.mk -whitespace text=auto +/comptime.sh text eol=lf # Windows EOL *.cs -crlf -whitespace *.bat -crlf -whitespace diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4e284ce65..b99a850a0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ variables: GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH default: - image: debian:stable-slim + image: git.do.srb2.org:5050/stjr/srb2ci/srb2ci:stable cache: - key: ccache-$CI_PROJECT_PATH_SLUG-$CI_JOB_NAME_SLUG @@ -19,6 +19,11 @@ default: - apt-cache unprotect: true + - key: apk-$CI_JOB_IMAGE + paths: + - apk-cache + unprotect: true + before_script: - - | # debconf @@ -167,6 +172,10 @@ Debian testing GCC: allow_failure: true artifacts: + paths: + - "bin/" + - "src/comptime.h" + expose_as: "testing-gcc" name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-gcc" variables: @@ -185,7 +194,7 @@ Debian testing GCC: - - | # apt_development echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages" - - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev + - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev libminiupnpc-dev - | # apt_development echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K" @@ -261,7 +270,7 @@ Debian stable:amd64: - - | # apt_development echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages" - - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64 + - apt-get install libsdl2-mixer-dev:amd64 libpng-dev:amd64 libcurl4-openssl-dev:amd64 libgme-dev:amd64 libopenmpt-dev:amd64 libminiupnpc-dev:amd64 - | # apt_development echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K" @@ -304,7 +313,7 @@ Debian stable:i386: - - | # apt_development echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages" - - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 + - apt-get install libsdl2-mixer-dev:i386 libpng-dev:i386 libcurl4-openssl-dev:i386 libgme-dev:i386 libopenmpt-dev:i386 libminiupnpc-dev:i386 - | # apt_development echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K" @@ -348,7 +357,7 @@ Debian stable:arm64: - - | # apt_development echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages" - - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64 + - apt-get install libsdl2-mixer-dev:arm64 libpng-dev:arm64 libcurl4-openssl-dev:arm64 libgme-dev:arm64 libopenmpt-dev:arm64 libminiupnpc-dev:arm64 - | # apt_development echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K" @@ -401,6 +410,10 @@ Debian stable Clang: allow_failure: true artifacts: + paths: + - "bin/" + - "src/comptime.h" + expose_as: "clang" name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-clang" variables: @@ -421,7 +434,7 @@ Debian stable Clang: - - | # apt_development echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages" - - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev + - apt-get install libsdl2-mixer-dev libpng-dev libcurl4-openssl-dev libgme-dev libopenmpt-dev libminiupnpc-dev - | # apt_development echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K" @@ -434,45 +447,6 @@ Debian stable Clang: # make echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K" -Debian stable musl: - stage: build - - when: manual - - allow_failure: true - - artifacts: - name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-musl" - - variables: - CC: musl-gcc - LDD: musl-ldd - - script: - - - | - # apt_toolchain - echo -e "\e[0Ksection_start:`date +%s`:apt_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages" - - apt-get install gcc - - | - # apt_toolchain - echo -e "\e[0Ksection_end:`date +%s`:apt_toolchain\r\e[0K" - - - - | - # apt_development - echo -e "\e[0Ksection_start:`date +%s`:apt_development[collapsed=true]\r\e[0KInstalling development packages" - - apt-get install musl-tools - - | - # apt_development - echo -e "\e[0Ksection_end:`date +%s`:apt_development\r\e[0K" - - - - | - # make - echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2" - - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 SDL=0 NOHW=1 NOZLIB=1 NOCURL=1 NOGME=1 NOOPENMPT=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 SDL=0 NOHW=1 NOZLIB=1 NOCURL=1 NOGME=1 NOOPENMPT=1 - - | - # make - echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K" - Debian testing Clang: extends: Debian stable Clang @@ -481,6 +455,10 @@ Debian testing Clang: image: debian:testing-slim artifacts: + paths: + - "bin/" + - "src/comptime.h" + expose_as: "testing-clang" name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-clang" variables: @@ -489,17 +467,132 @@ Debian testing Clang: CFLAGS: -Wno-cast-align -Wno-deprecated-non-prototype LDFLAGS: -Wl,-fuse-ld=gold -Debian testing musl: - extends: Debian stable musl +Alpine 3 GCC: + stage: build - when: manual + when: on_success - image: debian:testing-slim + image: alpine:3 + + allow_failure: true artifacts: - name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-testing-musl" + paths: + - "bin/" + - "src/comptime.h" + expose_as: "Apline-3" + name: "$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-Apline-3" - variables: - CC: musl-gcc - LDD: musl-ldd - LDFLAGS: -Wl,-fuse-ld=gold + before_script: + - - | + # apk_cache + echo -e "\e[0Ksection_start:`date +%s`:apk_cache[collapsed=true]\r\e[0KUpdating APK listing" + - export APK_CACHE_DIR=`pwd`/apk-cache + - mkdir --parents --verbose $APK_CACHE_DIR/ + - ln -sf /etc/apk/cache $APK_CACHE_DIR + - | + # apk_cache + echo -e "\e[0Ksection_end:`date +%s`:apk_cache\r\e[0K" + + - - | + # apk_update + echo -e "\e[0Ksection_start:`date +%s`:apk_update[collapsed=true]\r\e[0KUpdating APK listing" + - apk update + - | + # apk_update + echo -e "\e[0Ksection_end:`date +%s`:apk_update\r\e[0K" + + - - | + # apk_upgrade + echo -e "\e[0Ksection_start:`date +%s`:apk_upgrade[collapsed=true]\r\e[0KUpdating existing packages" + - apk upgrade + - | + # apk_update + echo -e "\e[0Ksection_end:`date +%s`:apk_upgrade\r\e[0K" + + - - | + # apk_common + echo -e "\e[0Ksection_start:`date +%s`:apk_common[collapsed=true]\r\e[0KInstalling common packages" + - apk add make git ccache nasm + - | + # apk_common + echo -e "\e[0Ksection_end:`date +%s`:apk_common\r\e[0K" + + - - | + # ccache_config + echo -e "\e[0Ksection_start:`date +%s`:ccache_config[collapsed=true]\r\e[0KSetting up ccache config" + - mkdir --parents --verbose ~/.ccache/ + - touch ~/.ccache/ccache.conf + - | + # cache.conf + echo Adding ccache configution option + - | + # base_dir + echo base_dir = $PWD | tee -a ~/.ccache/ccache.conf + - | + # cache_dir + echo cache_dir = $PWD/ccache | tee -a ~/.ccache/ccache.conf + - | + # compiler_check + echo compiler_check = content | tee -a ~/.ccache/ccache.conf + - | + # stats_log + echo stats_log = $PWD/ccache_statslog | tee -a ~/.ccache/ccache.conf + - | + # max_size + echo max_size = 50M | tee -a ~/.ccache/ccache.conf + - | + # ccache_config + echo -e "\e[0Ksection_end:`date +%s`:ccache_config\r\e[0K" + + - - | + # cache_reset + echo -e "\e[0Ksection_start:`date +%s`:ccache_reset[collapsed=true]\r\e[0KResetting ccache statistics" + - ccache --zero-stats + - ccache --show-stats + - | + # ccache_reset + echo -e "\e[0Ksection_end:`date +%s`:ccache_reset\r\e[0K" + + script: + - - | + # apk_toolchain + echo -e "\e[0Ksection_start:`date +%s`:apk_toolchain[collapsed=true]\r\e[0KInstalling toolchain packages" + - apk add gcc + - | + # apk_toolchain + echo -e "\e[0Ksection_end:`date +%s`:apk_toolchain\r\e[0K" + + - - | + # apk_development + echo -e "\e[0Ksection_start:`date +%s`:apk_development[collapsed=true]\r\e[0KInstalling development packages" + - apk add musl-dev sdl2_mixer-dev libpng-dev curl-dev libgme-dev libopenmpt-dev miniupnpc-dev + - | + # apk_development + echo -e "\e[0Ksection_end:`date +%s`:apk_development\r\e[0K" + + - - | + # make + echo -e "\e[0Ksection_start:`date +%s`:make[collapsed=false]\r\e[0KCompiling SRB2" + - make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 NOEXECINFO=1 || make --directory=src --keep-going CCACHE=1 ERRORMODE=1 NONX86=1 NOEXECINFO=1 + - | + # make + echo -e "\e[0Ksection_end:`date +%s`:make\r\e[0K" + + after_script: + - - | + # apk_clean + echo -e "\e[0Ksection_start:`date +%s`:apk_clean[collapsed=true]\r\e[0KCleaning of unneeded APK packages" + - apk cache clean + - | + # apk_clean + echo -e "\e[0Ksection_end:`date +%s`:apk_clean\r\e[0K" + + - - | + # ccache_stats + echo -e "\e[0Ksection_start:`date +%s`:ccache_stats[collapsed=true]\r\e[0Kccache statistics:" + - ccache --show-stats --verbose + - ccache --show-log-stats --verbose + - | + # ccahe_stats + echo -e "\e[0Ksection_end:`date +%s`:ccache_stats\r\e[0K" diff --git a/CMakeLists.txt b/CMakeLists.txt index 80a3bdcd6..8803620e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ option(SRB2_CONFIG_ERRORMODE "Compile C code with warnings treated as errors." O option(SRB2_CONFIG_DEBUGMODE "Compile with PARANOIA, ZDEBUG, RANGECHECK and PACKETDROP defined." OFF) option(SRB2_CONFIG_MOBJCONSISTANCY "Compile with MOBJCONSISTANCY defined." OFF) option(SRB2_CONFIG_PACKETDROP "Compile with PACKETDROP defined." OFF) +option(SRB2_CONFIG_EXECINFO "Enable stack trace dump support." ON) option(SRB2_CONFIG_ZDEBUG "Compile with ZDEBUG defined." OFF) # SRB2_CONFIG_PROFILEMODE is probably superceded by some CMake setting. option(SRB2_CONFIG_PROFILEMODE "Compile for profiling (GCC only)." OFF) diff --git a/comptime.bat b/comptime.bat index 0c7ea06d6..c03f088b2 100644 --- a/comptime.bat +++ b/comptime.bat @@ -1,6 +1,7 @@ @echo off set BRA=Unknown set REV=illegal +set GL1=Dummy copy nul: /b +%1\comptime.c tmp.$$$ > nul move tmp.$$$ %1\comptime.c > nul @@ -13,8 +14,9 @@ goto filwri :gitrev set GIT=%2 if "%GIT%"=="" set GIT=git -for /f "usebackq" %%s in (`%GIT% rev-parse --abbrev-ref HEAD`) do @set BRA=%%s -for /f "usebackq" %%s in (`%GIT% rev-parse HEAD`) do @set REV=%%s +for /f "tokens=* usebackq" %%s in (`%GIT% rev-parse --abbrev-ref HEAD`) do @set BRA=%%s +for /f "tokens=* usebackq" %%s in (`%GIT% rev-parse HEAD`) do @set REV=%%s +for /f "tokens=* usebackq" %%s in (`%GIT% log -1 --format^=%%f`) do @set GL1=%%s set REV=%REV:~0,8% goto filwri @@ -30,3 +32,4 @@ echo // by the %0 batch file >> %1\comptime.h echo // >> %1\comptime.h echo const char* compbranch = "%BRA%"; >> %1\comptime.h echo const char* comprevision = "%REV%"; >> %1\comptime.h +echo const char* compnote = "%GL1%"; >> %1\comptime.h diff --git a/comptime.sh b/comptime.sh index ce771f631..3338ecc35 100755 --- a/comptime.sh +++ b/comptime.sh @@ -12,24 +12,26 @@ version() { // const char* compbranch = "$1"; const char* comprevision = "$2"; +const char* compnote = "$3"; EOF } versiongit() { gitbranch="$(git rev-parse --abbrev-ref HEAD)" gitversion="$(git rev-parse HEAD | cut -c -8)" - version "$gitbranch" "$gitversion"; + gitsubject="$(git log -1 --format=%f)" + version "$gitbranch" "$gitversion" "$gitsubject"; exit 0 } versionsvn() { svnrevision="$(svnversion -n "$1")" - version "Subversion" "r$svnrevision"; + version "Subversion" "r$svnrevision" "dummy"; exit 0 } versionfake() { - version "Unknown" "illegal"; + version "Unknown" "illegal" "dummy"; } compversion() { diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg index 680778623..152cafe21 100644 --- a/extras/conf/udb/Includes/SRB222_linedefs.cfg +++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg @@ -10,6 +10,45 @@ udmf prefix = "(0)"; } + 6 + { + title = "Sector Set Portal"; + prefix = "(6)"; + arg0 + { + title = "Target sector tag"; + type = 13; + } + arg1 + { + title = "Portal type"; + type = 11; + enum + { + 0 = "Link to portal with same tag"; + 1 = "Copy portal from second tag"; + 2 = "Skybox portal"; + 3 = "Plane portal"; + 4 = "Horizon portal"; + 5 = "Copy portal to line"; + 6 = "Interactive portal (unimplemented)"; + 7 = "Link to sector with second tag"; + 8 = "Link to object with second tag"; + } + } + arg2 + { + title = "Affected planes"; + type = 11; + enum = "floorceiling"; + } + arg3 + { + title = "Misc"; + tooltip = "For type 0 portal: specifies whether the line belongs to the sector viewed\nthrough the portal (1) or the sector in which the portal is seen (0).\nFor type 1 portal: specifies the sector tag of the portal to copy.\nFor type 7 portal: specifies the sector tag to make a portal to.\nFor type 8 portal: specifies the object tag to make a portal to."; + } + } + 7 { title = "Sector Flat Alignment"; diff --git a/libs/miniupnpc/.gitattributes b/libs/miniupnpc/.gitattributes new file mode 100644 index 000000000..513fc8491 --- /dev/null +++ b/libs/miniupnpc/.gitattributes @@ -0,0 +1,26 @@ +/*.cmake text=auto +/*.py text=auto +/*.rc -crlf -whitespace +/*.sh text eol=lf +/*.txt text=auto +/CMakeLists.txt text=auto +/LICENSE text=auto +/Makefile text=auto +/Makefile.mingw text=auto +/include/*.h text=auto +/java/*.bat -crlf -whitespace +/java/*.java text=auto +/java/*.sh text eol=lf +/man3/miniupnpc.3 eol=lf +/msvc/*.sln -crlf -whitespace +/msvc/*.vbs -crlf -whitespace +/msvc/*.vcproj -crlf -whitespace +/msvc/*.vcxproj* -crlf -whitespace +/src/*.c text=auto +/src/*.h text=auto +/testdesc/*.values text=auto +/testdesc/*.xml text=auto +/testreplyparse/*.namevalue text=auto +/testreplyparse/*.txt text=auto +/testreplyparse/*.xml text=auto +MANIFEST.in eol=lf diff --git a/libs/miniupnpc/CMakeLists.txt b/libs/miniupnpc/CMakeLists.txt index 082b653a9..65eb89d08 100644 --- a/libs/miniupnpc/CMakeLists.txt +++ b/libs/miniupnpc/CMakeLists.txt @@ -1,172 +1,308 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) -project (miniupnpc C) -set (MINIUPNPC_VERSION 1.5) -set (MINIUPNPC_API_VERSION 8) +project (miniupnpc + VERSION 2.2.5 + DESCRIPTION "UPnP IGD client lightweight library" + HOMEPAGE_URL https://miniupnp.tuxfamily.org/ + LANGUAGES C) -if (NOT CMAKE_BUILD_TYPE) - if (WIN32) - set (DEFAULT_BUILD_TYPE MinSizeRel) - else (WIN32) - set (DEFAULT_BUILD_TYPE RelWithDebInfo) - endif(WIN32) - set (CMAKE_BUILD_TYPE ${DEFAULT_BUILD_TYPE} CACHE STRING - "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." - FORCE) -endif() +set (MINIUPNPC_API_VERSION 17) option (UPNPC_BUILD_STATIC "Build static library" TRUE) option (UPNPC_BUILD_SHARED "Build shared library" TRUE) -if (NOT WIN32) - option (UPNPC_BUILD_TESTS "Build test executables" TRUE) -endif (NOT WIN32) +option (UPNPC_BUILD_TESTS "Build test executables" TRUE) +option (UPNPC_BUILD_SAMPLE "Build sample executables" TRUE) option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE) - -mark_as_advanced (NO_GETADDRINFO) - -if (NO_GETADDRINFO) - add_definitions (-DNO_GETADDRINFO) -endif (NO_GETADDRINFO) - -if (NOT WIN32) - add_definitions (-DMINIUPNPC_SET_SOCKET_TIMEOUT) -else (NOT WIN32) - add_definitions (-D_WIN32_WINNT=0x0501) # XP or higher for getnameinfo and friends -endif (NOT WIN32) - -if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") - add_definitions (-DMACOSX -D_DARWIN_C_SOURCE) -endif () - -# Set compiler specific build flags -if (CMAKE_COMPILER_IS_GNUC) - # Set our own default flags at first run. - if (NOT CONFIGURED) - - if (NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") - set (_PIC -fPIC) - endif (CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") - - set (CMAKE_C_FLAGS "${_PIC} -Wall $ENV{CFLAGS}" # CMAKE_C_FLAGS gets appended to the other C flags - CACHE STRING "Flags used by the C compiler during normal builds." FORCE) - set (CMAKE_C_FLAGS_DEBUG "-g -DDDEBUG" - CACHE STRING "Flags used by the C compiler during debug builds." FORCE) - set (CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG" - CACHE STRING "Flags used by the C compiler during release builds." FORCE) - set (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG" - CACHE STRING "Flags used by the C compiler during release builds." FORCE) - set (CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG" - CACHE STRING "Flags used by the C compiler during release builds." FORCE) - - endif (NOT CONFIGURED) -endif () - -configure_file (${CMAKE_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_BINARY_DIR}/miniupnpcstrings.h) -include_directories (${CMAKE_BINARY_DIR}) - -set (MINIUPNPC_SOURCES - igd_desc_parse.c - miniupnpc.c - minixml.c - minisoap.c - miniwget.c - upnpc.c - upnpcommands.c - upnpreplyparse.c - upnperrors.c - connecthostport.c - portlistingparse.c -) - -if (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") - set (MINIUPNPC_SOURCES ${MINIUPNPC_SOURCES} minissdpc.c) -endif (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") - -if (WIN32) - set_source_files_properties (${MINIUPNPC_SOURCES} PROPERTIES - COMPILE_DEFINITIONS STATICLIB - COMPILE_DEFINITIONS MINIUPNP_EXPORTS - ) -endif (WIN32) - -if (WIN32) - find_library (WINSOCK2_LIBRARY NAMES ws2_32 WS2_32 Ws2_32) - find_library (IPHLPAPI_LIBRARY NAMES iphlpapi) - set (LDLIBS ${WINSOCK2_LIBRARY} ${IPHLPAPI_LIBRARY} ${LDLIBS}) -#elseif (CMAKE_SYSTEM_NAME STREQUAL "Solaris") -# find_library (SOCKET_LIBRARY NAMES socket) -# find_library (NSL_LIBRARY NAMES nsl) -# find_library (RESOLV_LIBRARY NAMES resolv) -# set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS}) -endif (WIN32) +option (UPNPC_NO_INSTALL "Disable installation" FALSE) if (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED) message (FATAL "Both shared and static libraries are disabled!") -endif (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED) +endif () + +include(GNUInstallDirs) + +# Interface library for compile definitions, flags and option +add_library(miniupnpc-private INTERFACE) + +if (NO_GETADDRINFO) + target_compile_definitions(miniupnpc-private INTERFACE NO_GETADDRINFO) +endif () + +if (NOT WIN32) + target_compile_definitions(miniupnpc-private INTERFACE + MINIUPNPC_SET_SOCKET_TIMEOUT + _BSD_SOURCE _DEFAULT_SOURCE) + if (NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES ".*BSD" AND NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS") + # add_definitions (-D_POSIX_C_SOURCE=200112L) + target_compile_definitions(miniupnpc-private INTERFACE _XOPEN_SOURCE=600) + endif () + if (CMAKE_SYSTEM_NAME STREQUAL "NetBSD") + target_compile_definitions(miniupnpc-private INTERFACE _NETBSD_SOURCE) + endif () +else () + target_compile_definitions(miniupnpc-private INTERFACE _WIN32_WINNT=0x0501) # XP or higher for getnameinfo and friends +endif () + +if (APPLE) + target_compile_definitions(miniupnpc-private INTERFACE _DARWIN_C_SOURCE) +endif () + +# Set compiler specific build flags +if (CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + target_compile_options(miniupnpc-private INTERFACE -Wall) +endif () + +# Suppress noise warnings +if (MSVC) + target_compile_definitions(miniupnpc-private INTERFACE _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS) +endif() + +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h) +target_include_directories(miniupnpc-private INTERFACE $) + +set (MINIUPNPC_SOURCES + src/igd_desc_parse.c + src/miniupnpc.c + src/minixml.c + src/minisoap.c + src/minissdpc.c + src/miniwget.c + src/upnpcommands.c + src/upnpdev.c + src/upnpreplyparse.c + src/upnperrors.c + src/connecthostport.c + src/portlistingparse.c + src/receivedata.c + src/addr_is_reserved.c + ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h +) + +if (WIN32) + target_link_libraries(miniupnpc-private INTERFACE ws2_32 iphlpapi) +elseif (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + target_link_libraries(miniupnpc-private INTERFACE socket nsl resolv) + find_library (SOCKET_LIBRARY NAMES socket) + find_library (NSL_LIBRARY NAMES nsl) + find_library (RESOLV_LIBRARY NAMES resolv) + set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS}) +elseif (HAIKU) + target_link_libraries(miniupnpc-private INTERFACE network) + find_library (SOCKET_LIBRARY NAMES network) + find_library (NSL_LIBRARY NAMES network) + find_library (RESOLV_LIBRARY NAMES network) + set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS}) +endif () + if (UPNPC_BUILD_STATIC) - add_library (upnpc-static STATIC ${MINIUPNPC_SOURCES}) - set_target_properties (upnpc-static PROPERTIES OUTPUT_NAME "miniupnpc") - target_link_libraries (upnpc-static ${LDLIBS}) - set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-static) - set (UPNPC_LIBRARY_TARGET upnpc-static) -endif (UPNPC_BUILD_STATIC) + add_library (libminiupnpc-static STATIC ${MINIUPNPC_SOURCES}) + target_include_directories (libminiupnpc-static PUBLIC + $ + $) + if (NOT UPNPC_BUILD_SHARED) + add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-static) + endif() + set_target_properties (libminiupnpc-static PROPERTIES EXPORT_NAME miniupnpc) + if (WIN32 AND NOT MINGW) + set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "libminiupnpc") + else() + set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "miniupnpc") + endif() + target_link_libraries (libminiupnpc-static PRIVATE miniupnpc-private) + target_include_directories(libminiupnpc-static INTERFACE $) + target_compile_definitions(libminiupnpc-static PUBLIC MINIUPNP_STATICLIB) + + if (NOT UPNPC_NO_INSTALL) + install (TARGETS miniupnpc-private EXPORT miniupnpc-private) + + install (EXPORT miniupnpc-private + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc" + NAMESPACE miniupnpc::) + + install (TARGETS libminiupnpc-static + EXPORT libminiupnpc-static + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + install (EXPORT libminiupnpc-static + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc" + NAMESPACE miniupnpc::) + endif() + + if (UPNPC_BUILD_SAMPLE) + add_executable (upnpc-static src/upnpc.c) + target_link_libraries (upnpc-static PRIVATE libminiupnpc-static) + target_include_directories(upnpc-static PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if (NOT UPNPC_NO_INSTALL) + install (TARGETS upnpc-static + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + endif () +endif () if (UPNPC_BUILD_SHARED) - add_library (upnpc-shared SHARED ${MINIUPNPC_SOURCES}) - set_target_properties (upnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc") - set_target_properties (upnpc-shared PROPERTIES VERSION ${MINIUPNPC_VERSION}) - set_target_properties (upnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION}) - target_link_libraries (upnpc-shared ${LDLIBS}) - set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-shared) - set (UPNPC_LIBRARY_TARGET upnpc-shared) -endif (UPNPC_BUILD_SHARED) + add_library (libminiupnpc-shared SHARED ${MINIUPNPC_SOURCES}) + target_include_directories (libminiupnpc-shared PUBLIC + $ + $) + add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-shared) + set_target_properties (libminiupnpc-shared PROPERTIES EXPORT_NAME miniupnpc) + set_target_properties (libminiupnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc") + set_target_properties (libminiupnpc-shared PROPERTIES VERSION ${PROJECT_VERSION}) + set_target_properties (libminiupnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION}) + target_link_libraries (libminiupnpc-shared PRIVATE miniupnpc-private) + target_compile_definitions(libminiupnpc-shared PRIVATE MINIUPNP_EXPORTS) + + target_include_directories(libminiupnpc-shared INTERFACE $) + if (WIN32) + target_link_libraries(libminiupnpc-shared INTERFACE ws2_32 iphlpapi) + endif() + + if (NOT UPNPC_NO_INSTALL) + install (TARGETS libminiupnpc-shared + EXPORT libminiupnpc-shared + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + install (EXPORT libminiupnpc-shared + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc" + NAMESPACE miniupnpc::) + endif() + + if (UPNPC_BUILD_SAMPLE) + add_executable (upnpc-shared src/upnpc.c) + target_link_libraries (upnpc-shared PRIVATE libminiupnpc-shared) + target_include_directories(upnpc-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if (NOT UPNPC_NO_INSTALL) + install (TARGETS upnpc-shared + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + endif () + + add_executable (listdevices src/listdevices.c) + target_link_libraries (listdevices PRIVATE libminiupnpc-shared) + target_include_directories(listdevices PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + if (NOT UPNPC_NO_INSTALL) + install (TARGETS listdevices + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() +endif () if (UPNPC_BUILD_TESTS) - add_executable (testminixml testminixml.c minixml.c igd_desc_parse.c) - target_link_libraries (testminixml ${LDLIBS}) + add_library(miniupnpc-tests INTERFACE) + target_link_libraries(miniupnpc-tests INTERFACE miniupnpc-private) + target_compile_definitions(miniupnpc-tests INTERFACE MINIUPNP_STATICLIB) - add_executable (minixmlvalid minixmlvalid.c minixml.c) - target_link_libraries (minixmlvalid ${LDLIBS}) + add_executable (testminixml src/testminixml.c src/minixml.c src/igd_desc_parse.c) + target_include_directories (testminixml PRIVATE + $) + target_link_libraries (testminixml PRIVATE miniupnpc-tests) - add_executable (testupnpreplyparse testupnpreplyparse.c - minixml.c upnpreplyparse.c) - target_link_libraries (testupnpreplyparse ${LDLIBS}) + add_executable (minixmlvalid src/minixmlvalid.c src/minixml.c) + target_link_libraries (minixmlvalid PRIVATE miniupnpc-tests) - add_executable (testigddescparse testigddescparse.c - igd_desc_parse.c minixml.c miniupnpc.c miniwget.c minissdpc.c - upnpcommands.c upnpreplyparse.c minisoap.c connecthostport.c - portlistingparse.c + add_executable (testupnpreplyparse src/testupnpreplyparse.c + src/minixml.c src/upnpreplyparse.c) + target_include_directories (testupnpreplyparse PRIVATE + $) + target_link_libraries (testupnpreplyparse PRIVATE miniupnpc-tests) + + add_executable (testigddescparse src/testigddescparse.c + src/igd_desc_parse.c src/minixml.c src/miniupnpc.c src/miniwget.c src/minissdpc.c + src/upnpcommands.c src/upnpreplyparse.c src/minisoap.c src/connecthostport.c + src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c ) - target_link_libraries (testigddescparse ${LDLIBS}) + target_include_directories (testigddescparse PRIVATE + $) + target_link_libraries (testigddescparse PRIVATE miniupnpc-tests) - add_executable (testminiwget testminiwget.c - miniwget.c miniupnpc.c minisoap.c upnpcommands.c minissdpc.c - upnpreplyparse.c minixml.c igd_desc_parse.c connecthostport.c - portlistingparse.c + add_executable (testminiwget src/testminiwget.c + src/miniwget.c src/miniupnpc.c src/minisoap.c src/upnpcommands.c src/minissdpc.c + src/upnpreplyparse.c src/minixml.c src/igd_desc_parse.c src/connecthostport.c + src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c ) - target_link_libraries (testminiwget ${LDLIBS}) + target_include_directories (testminiwget PRIVATE + $) + target_link_libraries (testminiwget PRIVATE miniupnpc-tests) + + add_executable (testaddr_is_reserved src/testaddr_is_reserved.c + src/addr_is_reserved.c + ) + target_link_libraries (testaddr_is_reserved PRIVATE miniupnpc-tests) + + add_executable (testportlistingparse src/testportlistingparse.c + src/minixml.c src/portlistingparse.c) + target_include_directories (testportlistingparse PRIVATE + $) + target_link_libraries (testportlistingparse PRIVATE miniupnpc-tests) + + add_executable (minihttptestserver src/minihttptestserver.c) # set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} testminixml minixmlvalid testupnpreplyparse testigddescparse testminiwget) -endif (UPNPC_BUILD_TESTS) + include(CTest) + add_test(NAME validateminixml + COMMAND minixmlvalid) + add_test(NAME validateminiwget + COMMAND ${CMAKE_SOURCE_DIR}/testminiwget.sh) + set_property(TEST validateminiwget + PROPERTY ENVIRONMENT + TESTSERVER=${CMAKE_BINARY_DIR}/minihttptestserver + TESTMINIWGET=${CMAKE_BINARY_DIR}/testminiwget) + add_test(NAME validateupnpreplyparse + COMMAND ${CMAKE_SOURCE_DIR}/testupnpreplyparse.sh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set_property(TEST validateupnpreplyparse + PROPERTY ENVIRONMENT + TESTUPNPREPLYPARSE=${CMAKE_BINARY_DIR}/testupnpreplyparse) + add_test(NAME validateportlistingparse + COMMAND testportlistingparse) + add_test(NAME validateigddescparse1 + COMMAND testigddescparse new_LiveBox_desc.xml new_LiveBox_desc.values + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc) + add_test(NAME validateigddescparse2 + COMMAND testigddescparse linksys_WAG200G_desc.xml linksys_WAG200G_desc.values + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc) + add_test(NAME validateaddr_is_reserved + COMMAND testaddr_is_reserved) +endif () -install (TARGETS ${UPNPC_INSTALL_TARGETS} - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib${LIB_SUFFIX} - ARCHIVE DESTINATION lib${LIB_SUFFIX} -) -install (FILES - miniupnpc.h - miniwget.h - upnpcommands.h - igd_desc_parse.h - upnpreplyparse.h - upnperrors.h - declspec.h - DESTINATION include/miniupnpc -) +configure_file(miniupnpc.pc.in miniupnpc.pc @ONLY) -set (CONFIGURED YES CACHE INTERNAL "") +if (NOT UPNPC_NO_INSTALL) + install (FILES + include/miniupnpc.h + include/miniwget.h + include/upnpcommands.h + include/igd_desc_parse.h + include/upnpreplyparse.h + include/upnperrors.h + include/upnpdev.h + include/miniupnpctypes.h + include/portlistingparse.h + include/miniupnpc_declspec.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniupnpc + ) -# vim: ts=2:sw=2 + install(FILES miniupnpc-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc + ) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/miniupnpc.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) + + install(FILES man3/miniupnpc.3 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man3 + ) + + install(FILES external-ip.sh + TYPE BIN + ) +endif() + +# vim: ts=2:sw=2:expandtab diff --git a/libs/miniupnpc/Changelog.txt b/libs/miniupnpc/Changelog.txt index 70b656498..595b4f93d 100644 --- a/libs/miniupnpc/Changelog.txt +++ b/libs/miniupnpc/Changelog.txt @@ -1,6 +1,343 @@ -$Id: Changelog.txt,v 1.152 2011/07/25 18:02:11 nanard Exp $ +$Id: Changelog.txt,v 1.256 2023/06/11 23:23:29 nanard Exp $ miniUPnP client Changelog. +2023/06/05: + GetListOfPortMappings NewStartPort 0 => 1 + +2023/05/30: + CheckPinholeWorking is optional + add 60x errors from UPnP Device Architecture + +2023/01/04: + cmake: install binaries, man pages and external-ip.sh + +VERSION 2.2.4 : released 2022/10/21 + +2022/02/20: + upnpc: use of @ to replace local lan address + +2021/11/09: + python module : Allow to specify the root description url + +VERSION 2.2.3 : released 2021/09/28 + +2021/08/13: + Change directory structure : include/ and src/ directories. + +VERSION 2.2.2 : released 2021/03/03 + +2021/01/15: + miniupnpcmodule.c: throw an exception in UPnP_discover() + +2020/12/30: + Fix usage of IP_MULTICAST_IF with struct ip_mreqn + +VERSION 2.2.1 : released 2020/12/20 + +2020/11/30: + Add miniupnpc.rc for .dll description + +VERSION 2.2.0 : released 2020/11/09 + +2020/09/24: + Check properly for reserved IP addresses + +2020/09/23: + prevent infinite loop in upnpDiscover() + +2020/02/16: + Add Haiku support + +2019/10/22: + testminiwget.sh can use either "ip addr" or "ifconfig -a + +2019/10/13: + fix UPNP_GetValidIGD() when several devices are found + which are reachable from != local address + +2019/08/24: + Allow Remote Host on upnpc command line + fix error 708 description in strupnperror() + +2019/04/05: + Fix memory leak in upnpreplyparse.c with NewPortListing element + +2019/03/10: + connecthostport.c: Code simplification, error trace fix + +2019/01/23: + set timeout for select() in connecthostport() + +2018/10/31: + miniupnpcmodule.c: check return of WSAStartup() + +2018/07/14: + Fix and improve MSVC project : + Add Dll configurations + improve genminiupnpcstrings.vbs + +2018/06/18: + Fixes for windows 64-bits. + +VERSION 2.1 : released 2018/05/07 + +2018/05/07: + CMake Modernize and cleanup CMakeLists.txt + Update MS Visual Studio projects + +2018/04/30: + listdevices: show devices sorted by XML desc URL + +2018/04/26: + Small fix in miniupnpcmodule.c (python module) + Support cross compiling in Makefile.mingw + +2018/04/06: + Use SOCKET type instead of int (for Win64 compilation) + Increments API_VERSION to 17 + +2018/02/22: + Disable usage of MiniSSDPd when using -m option + +2017/12/11: + Fix buffer over run in minixml.c + Fix uninitialized variable access in upnpreplyparse.c + +2017/05/05: + Fix CVE-2017-8798 Thanks to tin/Team OSTStrom + +2016/11/11: + check strlen before memcmp in XML parsing portlistingparse.c + fix build under SOLARIS and CYGWIN + +2016/10/11: + Add python 3 compatibility to IGD test + +VERSION 2.0 : released 2016/04/19 + +2016/01/24: + change miniwget to return HTTP status code + increments API_VERSION to 16 + +2016/01/22: + Improve UPNPIGD_IsConnected() to check if WAN address is not private. + parse HTTP response status line in miniwget.c + +2015/10/26: + snprintf() overflow check. check overflow in simpleUPnPcommand2() + +2015/10/25: + fix compilation with old macs + fix compilation with mingw32 (for Appveyor) + fix python module for python <= 2.3 + +2015/10/08: + Change sameport to localport + see https://github.com/miniupnp/miniupnp/pull/120 + increments API_VERSION to 15 + +2015/09/15: + Fix buffer overflow in igd_desc_parse.c/IGDstartelt() + Discovered by Aleksandar Nikolic of Cisco Talos + +2015/08/28: + move ssdpDiscoverDevices() to minissdpc.c + +2015/08/27: + avoid unix socket leak in getDevicesFromMiniSSDPD() + +2015/08/16: + Also accept "Up" as ConnectionStatus value + +2015/07/23: + split getDevicesFromMiniSSDPD + add ttl argument to upnpDiscover() functions + increments API_VERSION to 14 + +2015/07/22: + Read USN from SSDP messages. + +2015/07/15: + Check malloc/calloc + +2015/06/16: + update getDevicesFromMiniSSDPD() to process longer minissdpd + responses + +2015/05/22: + add searchalltypes param to upnpDiscoverDevices() + increments API_VERSION to 13 + +2015/04/30: + upnpc: output version on the terminal + +2015/04/27: + _BSD_SOURCE is deprecated in favor of _DEFAULT_SOURCE + fix CMakeLists.txt COMPILE_DEFINITIONS + fix getDevicesFromMiniSSDPD() not setting scope_id + improve -r command of upnpc command line tool + +2014/11/17: + search all : + upnpDiscoverDevices() / upnpDiscoverAll() functions + listdevices executable + increment API_VERSION to 12 + validate igd_desc_parse + +2014/11/13: + increment API_VERSION to 11 + +2014/11/05: + simplified function GetUPNPUrls() + +2014/09/11: + use remoteHost arg of DeletePortMapping + +2014/09/06: + Fix python3 build + +2014/07/01: + Fix parsing of IGD2 root descriptions + +2014/06/10: + rename LIBSPEC to MINIUPNP_LIBSPEC + +2014/05/15: + Add support for IGD2 AddAnyPortMapping and DeletePortMappingRange + +2014/02/05: + handle EINPROGRESS after connect() + +2014/02/03: + minixml now handle XML comments + +VERSION 1.9 : released 2014/01/31 + +2014/01/31: + added argument remoteHost to UPNP_GetSpecificPortMappingEntry() + increment API_VERSION to 10 + +2013/12/09: + --help and -h arguments in upnpc.c + +2013/10/07: + fixed potential buffer overrun in miniwget.c + Modified UPNP_GetValidIGD() to check for ExternalIpAddress + +2013/08/01: + define MAXHOSTNAMELEN if not already done + +2013/06/06: + update upnpreplyparse to allow larger values (128 chars instead of 64) + +2013/05/14: + Update upnpreplyparse to take into account "empty" elements + validate upnpreplyparse.c code with "make check" + +2013/05/03: + Fix Solaris build thanks to Maciej MaƂecki + +2013/04/27: + Fix testminiwget.sh for BSD + +2013/03/23: + Fixed Makefile for *BSD + +2013/03/11: + Update Makefile to use JNAerator version 0.11 + +2013/02/11: + Fix testminiwget.sh for use with dash + Use $(DESTDIR) in Makefile + +VERSION 1.8 : released 2013/02/06 + +2012/10/16: + fix testminiwget with no IPv6 support + +2012/09/27: + Rename all include guards to not clash with C99 + (7.1.3 Reserved identifiers). + +2012/08/30: + Added -e option to upnpc program (set description for port mappings) + +2012/08/29: + Python 3 support (thanks to Christopher Foo) + +2012/08/11: + Fix a memory link in UPNP_GetValidIGD() + Try to handle scope id in link local IPv6 URL under MS Windows + +2012/07/20: + Disable HAS_IP_MREQN on DragonFly BSD + +2012/06/28: + GetUPNPUrls() now inserts scope into link-local IPv6 addresses + +2012/06/23: + More error return checks in upnpc.c + #define MINIUPNPC_GET_SRC_ADDR enables receivedata() to get scope_id + parseURL() now parses IPv6 addresses scope + new parameter for miniwget() : IPv6 address scope + increment API_VERSION to 9 + +2012/06/20: + fixed CMakeLists.txt + +2012/05/29 + Improvements in testminiwget.sh + +VERSION 1.7 : released 2012/05/24 + +2012/05/01: + Cleanup settings of CFLAGS in Makefile + Fix signed/unsigned integer comparaisons + +2012/04/20: + Allow to specify protocol with TCP or UDP for -A option + +2012/04/09: + Only try to fetch XML description once in UPNP_GetValidIGD() + Added -ansi flag to compilation, and fixed C++ comments to ANSI C comments. + +2012/04/05: + minor improvements to minihttptestserver.c + +2012/03/15: + upnperrors.c returns valid error string for unrecognized error codes + +2012/03/08: + make minihttptestserver listen on loopback interface instead of 0.0.0.0 + +2012/01/25: + Maven installation thanks to Alexey Kuznetsov + +2012/01/21: + Replace WIN32 macro by _WIN32 + +2012/01/19: + Fixes in java wrappers thanks to Alexey Kuznetsov : + https://github.com/axet/miniupnp/tree/fix-javatest/miniupnpc + Make and install .deb packages (python) thanks to Alexey Kuznetsov : + https://github.com/axet/miniupnp/tree/feature-debbuild/miniupnpc + +2012/01/07: + The multicast interface can now be specified by name with IPv4. + +2012/01/02: + Install man page + +2011/11/25: + added header to Port Mappings list in upnpc.c + +2011/10/09: + Makefile : make clean now removes jnaerator generated files. + MINIUPNPC_VERSION in miniupnpc.h (updated by make) + +2011/09/12: + added rootdescURL to UPNPUrls structure. + VERSION 1.6 : released 2011/07/25 2011/07/25: @@ -236,7 +573,7 @@ VERSION 1.2 : small modif to make Clang happy :) 2008/07/17: - #define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV... + #define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV... 2008/07/14: include declspec.h in installation (to /usr/include/miniupnpc) @@ -258,7 +595,7 @@ VERSION 1.1 : improved python module error/exception reporting. 2008/04/23: - Completely rewrite igd_desc_parse.c in order to be compatible with + Completely rewrite igd_desc_parse.c in order to be compatible with Linksys WAG200G Added testigddescparse updated python module @@ -281,7 +618,7 @@ VERSION 1.0 : improved make install :) 2007/12/22: - Adding upnperrors.c/h to provide a strupnperror() function + Adding upnperrors.c/h to provide a strupnperror() function used to translate UPnP error codes to string. 2007/12/19: @@ -363,7 +700,7 @@ VERSION 1.0 : upnpc now displays external ip address with -s or -l 2007/04/11: - changed MINIUPNPC_URL_MAXSIZE to 128 to accomodate the "BT Voyager 210" + changed MINIUPNPC_URL_MAXSIZE to 128 to accommodate the "BT Voyager 210" 2007/03/19: cleanup in miniwget.c diff --git a/libs/miniupnpc/LICENSE b/libs/miniupnpc/LICENSE index 2434c86e4..6eff8d268 100644 --- a/libs/miniupnpc/LICENSE +++ b/libs/miniupnpc/LICENSE @@ -1,27 +1,29 @@ -MiniUPnPc -Copyright (c) 2005-2011, Thomas BERNARD +BSD 3-Clause License + +Copyright (c) 2005-2023, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/miniupnpc/MANIFEST.in b/libs/miniupnpc/MANIFEST.in index 54b86f95e..84102e728 100644 --- a/libs/miniupnpc/MANIFEST.in +++ b/libs/miniupnpc/MANIFEST.in @@ -1,5 +1,10 @@ include README -include miniupnpcmodule.c +include VERSION +include LICENSE +include src/miniupnpcmodule.c include setup.py -include *.h -include libminiupnpc.a +include Makefile +include src/*.[ch] +include include/*.h +include *.h.in +include *.sh diff --git a/libs/miniupnpc/Makefile b/libs/miniupnpc/Makefile index 2f46e6d19..a19363ad5 100644 --- a/libs/miniupnpc/Makefile +++ b/libs/miniupnpc/Makefile @@ -1,214 +1,396 @@ -# $Id: Makefile,v 1.81 2011/06/21 15:24:14 nanard Exp $ +# $Id: Makefile,v 1.148 2022/10/19 22:46:06 nanard Exp $ # MiniUPnP Project # http://miniupnp.free.fr/ -# (c) 2005-2011 Thomas Bernard +# https://miniupnp.tuxfamily.org/ +# https://github.com/miniupnp/miniupnp +# (c) 2005-2022 Thomas Bernard # to install use : -# $ PREFIX=/tmp/dummylocation make install +# $ make DESTDIR=/tmp/dummylocation install # or # $ INSTALLPREFIX=/usr/local make install -# or -# make install (will go to /usr/bin, /usr/lib, etc...) -OS = $(shell uname -s) +# or +# $ make install (default INSTALLPREFIX is /usr) +OS = $(shell $(CC) -dumpmachine) +VERSION = $(shell cat VERSION) + +ifneq (, $(findstring darwin, $(OS))) +JARSUFFIX=mac +LIBTOOL ?= $(shell which libtool) +endif +ifneq (, $(findstring linux, $(OS))) +JARSUFFIX=linux +endif +ifneq (, $(findstring mingw, $(OS))$(findstring cygwin, $(OS))$(findstring msys, $(OS))) +JARSUFFIX=win32 +endif + +HAVE_IPV6 ?= yes +export HAVE_IPV6 + +# directories +INCDIR = include +SRCDIR = src +BUILD = build + CC ?= gcc #AR = gar -#CFLAGS = -O -Wall -g -DDEBUG -CFLAGS ?= -O -Wall -DNDEBUG -DMINIUPNPC_SET_SOCKET_TIMEOUT -Wstrict-prototypes -# -DNO_GETADDRINFO +#CFLAGS = -O -g +# to debug : +ASANFLAGS = -fsanitize=address -fsanitize=undefined -fsanitize=leak +#CFLAGS = -g -ggdb -O0 $(ASANFLAGS) -fno-omit-frame-pointer +#CPPFLAGS += -DDEBUG +#LDFLAGS += $(ASANFLAGS) +CFLAGS ?= -O +CFLAGS += -Wall +CFLAGS += -W -Wstrict-prototypes +CFLAGS += -fno-common +CPPFLAGS += -I$(BUILD) +CPPFLAGS += -DMINIUPNPC_SET_SOCKET_TIMEOUT +CPPFLAGS += -DMINIUPNPC_GET_SRC_ADDR +CPPFLAGS += -D_BSD_SOURCE +CPPFLAGS += -D_DEFAULT_SOURCE +ifneq (, $(findstring netbsd, $(OS))) +CPPFLAGS += -D_NETBSD_SOURCE +endif +ifeq (, $(findstring freebsd, $(OS))$(findstring darwin, $(OS))) +#CPPFLAGS += -D_POSIX_C_SOURCE=200112L +CPPFLAGS += -D_XOPEN_SOURCE=600 +endif +#CFLAGS += -ansi +#CPPFLAGS += -DNO_GETADDRINFO + +DEPFLAGS = -MM -MG + +MKDIR = mkdir -p INSTALL = install SH = /bin/sh JAVA = java # see http://code.google.com/p/jnaerator/ -JNAERATOR = jnaerator-0.9.3.jar -#following libs are needed on Solaris -#LDLIBS=-lsocket -lnsl -lresolv +#JNAERATOR = jnaerator-0.9.7.jar +#JNAERATOR = jnaerator-0.9.8-shaded.jar +#JNAERATORARGS = -library miniupnpc +#JNAERATOR = jnaerator-0.10-shaded.jar +#JNAERATOR = jnaerator-0.11-shaded.jar +# https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12/jnaerator-0.12-shaded.jar +JNAERATOR = jnaerator-0.12-shaded.jar +JNAERATORARGS = -mode StandaloneJar -runtime JNAerator -library miniupnpc +#JNAERATORBASEURL = http://jnaerator.googlecode.com/files/ +JNAERATORBASEURL = https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12 + +ifneq (, $(findstring sun, $(OS))$(findstring solaris, $(OS))) + LDLIBS=-lsocket -lnsl -lresolv + CPPFLAGS += -D__EXTENSIONS__ + CFLAGS += -std=c99 +endif # APIVERSION is used to build SONAME -APIVERSION = 8 +APIVERSION = 17 -SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \ - upnpc.c upnpcommands.c upnpreplyparse.c testminixml.c \ - minixmlvalid.c testupnpreplyparse.c minissdpc.c \ - upnperrors.c testigddescparse.c testminiwget.c \ - connecthostport.c portlistringparse.c receivedata.c +SRCS = $(wildcard $(SRCDIR)/*.c) -LIBOBJS = miniwget.o minixml.o igd_desc_parse.o minisoap.o \ +LIBOBJS = $(addprefix $(BUILD)/,miniwget.o minixml.o igd_desc_parse.o minisoap.o \ miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \ - connecthostport.o portlistingparse.o receivedata.o + connecthostport.o portlistingparse.o receivedata.o upnpdev.o \ + addr_is_reserved.o) -ifneq ($(OS), AmigaOS) -CFLAGS := -fPIC $(CFLAGS) -LIBOBJS := $(LIBOBJS) minissdpc.o -endif +BUILDINCLUDES = $(addprefix $(BUILD)/, miniupnpcstrings.h) -OBJS = $(patsubst %.c,%.o,$(SRCS)) +OBJS = $(patsubst $(SRCDIR)/%.c,$(BUILD)/%.o,$(SRCS)) +DEPS = $(patsubst $(SRCDIR)/%.c,$(BUILD)/%.d,$(SRCS)) # HEADERS to install -HEADERS = miniupnpc.h miniwget.h upnpcommands.h igd_desc_parse.h \ - upnpreplyparse.h upnperrors.h miniupnpctypes.h \ - portlistingparse.h \ - declspec.h +CPPFLAGS += -I$(INCDIR) +HEADERS = $(wildcard $(INCDIR)/*.h) # library names -LIBRARY = libminiupnpc.a -ifeq ($(OS), Darwin) - SHAREDLIBRARY = libminiupnpc.dylib - SONAME = $(basename $(SHAREDLIBRARY)).$(APIVERSION).dylib - CFLAGS := -DMACOSX -D_DARWIN_C_SOURCE $(CFLAGS) -else - SHAREDLIBRARY = libminiupnpc.so - SONAME = $(SHAREDLIBRARY).$(APIVERSION) +LIBRARY = $(BUILD)/libminiupnpc.a +ifneq (, $(findstring darwin, $(OS))) + SHAREDLIBRARY = $(BUILD)/libminiupnpc.dylib + SONAME = $(notdir $(basename $(SHAREDLIBRARY))).$(APIVERSION).dylib + CPPFLAGS += -D_DARWIN_C_SOURCE +else +ifeq ($(JARSUFFIX), win32) + SHAREDLIBRARY = $(BUILD)/miniupnpc.dll +else + # Linux/BSD/etc. + SHAREDLIBRARY = $(BUILD)/libminiupnpc.so + SONAME = $(notdir $(SHAREDLIBRARY)).$(APIVERSION) +endif endif -EXECUTABLES = upnpc-static -EXECUTABLES_ADDTESTS = testminixml minixmlvalid testupnpreplyparse \ - testigddescparse testminiwget +EXECUTABLES = $(addprefix $(BUILD)/, upnpc-static listdevices) +EXECUTABLES_ADDTESTS = $(addprefix $(BUILD)/, testminixml minixmlvalid \ + testupnpreplyparse testigddescparse testminiwget testportlistingparse) -TESTMINIXMLOBJS = minixml.o igd_desc_parse.o testminixml.o +TESTMINIXMLOBJS = $(addprefix $(BUILD)/, minixml.o igd_desc_parse.o testminixml.o) -TESTMINIWGETOBJS = miniwget.o testminiwget.o connecthostport.o receivedata.o +TESTMINIWGETOBJS = $(addprefix $(BUILD)/, miniwget.o testminiwget.o connecthostport.o receivedata.o) -TESTUPNPREPLYPARSE = testupnpreplyparse.o minixml.o upnpreplyparse.o +TESTUPNPREPLYPARSE = $(addprefix $(BUILD)/, testupnpreplyparse.o minixml.o upnpreplyparse.o) -TESTIGDDESCPARSE = testigddescparse.o igd_desc_parse.o minixml.o \ +TESTPORTLISTINGPARSE = $(addprefix $(BUILD)/, testportlistingparse.o minixml.o portlistingparse.o) + +TESTADDR_IS_RESERVED = $(addprefix $(BUILD)/, testaddr_is_reserved.o addr_is_reserved.o) + +TESTIGDDESCPARSE = $(addprefix $(BUILD)/, testigddescparse.o igd_desc_parse.o minixml.o \ miniupnpc.o miniwget.o upnpcommands.o upnpreplyparse.o \ minisoap.o connecthostport.o receivedata.o \ - portlistingparse.o + portlistingparse.o addr_is_reserved.o) -ifneq ($(OS), AmigaOS) -EXECUTABLES := $(EXECUTABLES) upnpc-shared -TESTMINIWGETOBJS := $(TESTMINIWGETOBJS) minissdpc.o -TESTIGDDESCPARSE := $(TESTIGDDESCPARSE) minissdpc.o +ifeq (, $(findstring amiga, $(OS))) +ifeq (, $(findstring mingw, $(OS))$(findstring cygwin, $(OS))$(findstring msys, $(OS))) +CFLAGS += -fPIC +endif +EXECUTABLES += $(BUILD)/upnpc-shared +TESTMINIWGETOBJS += $(BUILD)/minissdpc.o +TESTIGDDESCPARSE += $(BUILD)/minissdpc.o +LIBOBJS += $(BUILD)/minissdpc.o endif +LIBDIR ?= lib # install directories -INSTALLPREFIX ?= $(PREFIX)/usr +ifeq ($(strip $(PREFIX)),) +INSTALLPREFIX ?= /usr +else +INSTALLPREFIX ?= $(PREFIX) +endif INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc -INSTALLDIRLIB = $(INSTALLPREFIX)/lib +INSTALLDIRLIB = $(INSTALLPREFIX)/$(LIBDIR) INSTALLDIRBIN = $(INSTALLPREFIX)/bin +INSTALLDIRMAN = $(INSTALLPREFIX)/share/man +PKGCONFIGDIR = $(INSTALLDIRLIB)/pkgconfig FILESTOINSTALL = $(LIBRARY) $(EXECUTABLES) -ifneq ($(OS), AmigaOS) -FILESTOINSTALL := $(FILESTOINSTALL) $(SHAREDLIBRARY) +ifeq (, $(findstring amiga, $(OS))) +FILESTOINSTALL += $(SHAREDLIBRARY) $(BUILD)/miniupnpc.pc endif -.PHONY: install clean depend all check everything \ - installpythonmodule -# validateminixml validateminiwget +.PHONY: install clean depend all check test everything \ + installpythonmodule updateversion all: $(LIBRARY) $(EXECUTABLES) -check: validateminixml validateminiwget +test: check + +check: validateminixml validateminiwget validateupnpreplyparse \ + validateportlistingparse validateigddescparse validateaddr_is_reserved everything: all $(EXECUTABLES_ADDTESTS) -pythonmodule: $(LIBRARY) miniupnpcmodule.c setup.py - python setup.py build +pythonmodule: $(LIBRARY) $(SRCDIR)/miniupnpcmodule.c setup.py + MAKE=$(MAKE) python setup.py build touch $@ installpythonmodule: pythonmodule - python setup.py install + MAKE=$(MAKE) python setup.py install -validateminixml: minixmlvalid - @echo "minixml validation test" - ./minixmlvalid +pythonmodule3: $(LIBRARY) $(SRCDIR)/miniupnpcmodule.c setup.py + MAKE=$(MAKE) python3 setup.py build touch $@ -validateminiwget: testminiwget minihttptestserver testminiwget.sh +installpythonmodule3: pythonmodule3 + MAKE=$(MAKE) python3 setup.py install + +validateminixml: $(BUILD)/minixmlvalid + @echo "minixml validation test" + ./$< + touch $@ + +validateminiwget: testminiwget.sh $(BUILD)/testminiwget $(BUILD)/minihttptestserver @echo "miniwget validation test" - ./testminiwget.sh + ./$< + touch $@ + +validateupnpreplyparse: testupnpreplyparse.sh $(BUILD)/testupnpreplyparse + @echo "upnpreplyparse validation test" + ./$< + touch $@ + +validateportlistingparse: $(BUILD)/testportlistingparse + @echo "portlistingparse validation test" + ./$< + touch $@ + +validateigddescparse: $(BUILD)/testigddescparse + @echo "igd desc parse validation test" + ./$< testdesc/new_LiveBox_desc.xml testdesc/new_LiveBox_desc.values + ./$< testdesc/linksys_WAG200G_desc.xml testdesc/linksys_WAG200G_desc.values + touch $@ + +validateaddr_is_reserved: $(BUILD)/testaddr_is_reserved + @echo "addr_is_reserved() validation test" + ./$< touch $@ clean: - $(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) miniupnpcstrings.h + $(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) $(BUILDINCLUDES) + $(RM) $(EXECUTABLES_ADDTESTS) # clean python stuff - $(RM) pythonmodule validateminixml + $(RM) pythonmodule pythonmodule3 + $(RM) validateminixml validateminiwget validateupnpreplyparse + $(RM) validateigddescparse + $(RM) minihttptestserver + $(RM) testaddr_is_reserved $(RM) -r build/ dist/ #python setup.py clean + # clean jnaerator stuff + $(RM) _jnaerator.* java/miniupnpc_$(OS).jar -install: $(FILESTOINSTALL) - $(INSTALL) -d $(INSTALLDIRINC) - $(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC) - $(INSTALL) -d $(INSTALLDIRLIB) - $(INSTALL) -m 644 $(LIBRARY) $(INSTALLDIRLIB) -ifneq ($(OS), AmigaOS) - $(INSTALL) -m 644 $(SHAREDLIBRARY) $(INSTALLDIRLIB)/$(SONAME) - ln -fs $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIBRARY) +distclean: clean + $(RM) $(JNAERATOR) java/*.jar java/*.class out.errors.txt + +updateversion: include/miniupnpc.h + cp $< $<.bak + sed 's/\(.*MINIUPNPC_API_VERSION\s\+\)[0-9]\+/\1$(APIVERSION)/' < $<.bak > $< + +install: updateversion $(FILESTOINSTALL) + $(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC) + $(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC) + $(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB) + $(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB) +ifeq (, $(findstring amiga, $(OS))) + $(INSTALL) -m 644 $(SHAREDLIBRARY) $(DESTDIR)$(INSTALLDIRLIB)/$(SONAME) + ln -fs $(SONAME) $(DESTDIR)$(INSTALLDIRLIB)/$(notdir $(SHAREDLIBRARY)) + $(INSTALL) -d $(DESTDIR)$(PKGCONFIGDIR) + $(INSTALL) -m 644 $(BUILD)/miniupnpc.pc $(DESTDIR)$(PKGCONFIGDIR) endif - $(INSTALL) -d $(INSTALLDIRBIN) -ifeq ($(OS), AmigaOS) - $(INSTALL) -m 755 upnpc-static $(INSTALLDIRBIN)/upnpc + $(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN) +ifneq (, $(findstring amiga, $(OS))) + $(INSTALL) -m 755 $(BUILD)/upnpc-static $(DESTDIR)$(INSTALLDIRBIN)/upnpc else - $(INSTALL) -m 755 upnpc-shared $(INSTALLDIRBIN)/upnpc + $(INSTALL) -m 755 $(BUILD)/upnpc-shared $(DESTDIR)$(INSTALLDIRBIN)/upnpc endif - $(INSTALL) -m 755 external-ip.sh $(INSTALLDIRBIN)/external-ip + $(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip +ifeq (, $(findstring amiga, $(OS))) + $(INSTALL) -d $(DESTDIR)$(INSTALLDIRMAN)/man3 + $(INSTALL) -m 644 man3/miniupnpc.3 $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3 +ifneq (, $(findstring linux, $(OS))) + gzip -f $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3 +endif +endif + +install-static: updateversion $(FILESTOINSTALL) + $(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC) + $(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC) + $(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB) + $(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB) + $(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN) + $(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip cleaninstall: - $(RM) -r $(INSTALLDIRINC) - $(RM) $(INSTALLDIRLIB)/$(LIBRARY) - $(RM) $(INSTALLDIRLIB)/$(SHAREDLIBRARY) + $(RM) -r $(DESTDIR)$(INSTALLDIRINC) + $(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(LIBRARY) + $(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(SHAREDLIBRARY) -depend: - makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null +$(BUILD)/miniupnpc.pc: VERSION + @$(MKDIR) $(@D) + $(RM) $@ + echo "prefix=$(INSTALLPREFIX)" >> $@ + echo "exec_prefix=\$${prefix}" >> $@ + echo "libdir=\$${exec_prefix}/$(LIBDIR)" >> $@ + echo "includedir=\$${prefix}/include" >> $@ + echo "" >> $@ + echo "Name: miniUPnPc" >> $@ + echo "Description: UPnP IGD client lightweight library" >> $@ + echo "URL: https://miniupnp.tuxfamily.org/" >> $@ + echo "Version: $(VERSION)" >> $@ + echo "Libs: -L\$${libdir} -lminiupnpc" >> $@ + echo "Cflags: -I\$${includedir}" >> $@ + +depend: $(DEPS) $(LIBRARY): $(LIBOBJS) +ifneq (, $(findstring darwin, $(OS))) + $(LIBTOOL) -static -o $@ $? +else $(AR) crs $@ $? +endif $(SHAREDLIBRARY): $(LIBOBJS) -ifeq ($(OS), Darwin) - $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(SONAME) -o $@ $^ +ifneq (, $(findstring darwin, $(OS))) +# $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(SONAME) -o $@ $^ + $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(INSTALLDIRLIB)/$(SONAME) -o $@ $^ else $(CC) -shared $(LDFLAGS) -Wl,-soname,$(SONAME) -o $@ $^ endif -upnpc-static: upnpc.o $(LIBRARY) $(LDLIBS) - $(CC) $(LDFLAGS) -o $@ $^ +$(BUILD)/%.o: $(SRCDIR)/%.c $(BUILD)/%.d + $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< -upnpc-shared: upnpc.o $(SHAREDLIBRARY) $(LDLIBS) - $(CC) $(LDFLAGS) -o $@ $^ +$(DEPS): $(BUILDINCLUDES) -testminixml: $(TESTMINIXMLOBJS) +$(BUILD)/%.d: $(SRCDIR)/%.c + @$(MKDIR) $(@D) + $(CC) $(CPPFLAGS) $(DEPFLAGS) -MT $@ -o $@ $< -testminiwget: $(TESTMINIWGETOBJS) +$(BUILD)/upnpc-static: $(BUILD)/upnpc.o $(LIBRARY) + $(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS) -minixmlvalid: minixml.o minixmlvalid.o +$(BUILD)/upnpc-shared: $(BUILD)/upnpc.o $(SHAREDLIBRARY) + $(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS) -testupnpreplyparse: $(TESTUPNPREPLYPARSE) +$(BUILD)/listdevices: $(BUILD)/listdevices.o $(LIBRARY) -testigddescparse: $(TESTIGDDESCPARSE) +$(BUILD)/testminixml: $(TESTMINIXMLOBJS) -miniupnpcstrings.h: miniupnpcstrings.h.in updateminiupnpcstrings.sh - $(SH) updateminiupnpcstrings.sh +$(BUILD)/testminiwget: $(TESTMINIWGETOBJS) -jar: $(SHAREDLIBRARY) - $(JAVA) -jar $(JNAERATOR) -library miniupnpc miniupnpc.h declspec.h upnpcommands.h upnpreplyparse.h igd_desc_parse.h miniwget.h upnperrors.h $(SHAREDLIBRARY) -package fr.free.miniupnp -o . -jar java/miniupnpc_$(OS).jar -v +$(BUILD)/minixmlvalid: $(addprefix $(BUILD)/, minixml.o minixmlvalid.o) + +$(BUILD)/testupnpreplyparse: $(TESTUPNPREPLYPARSE) + +$(BUILD)/testigddescparse: $(TESTIGDDESCPARSE) + +$(BUILD)/testportlistingparse: $(TESTPORTLISTINGPARSE) + +$(BUILD)/testaddr_is_reserved: $(TESTADDR_IS_RESERVED) + +$(BUILD)/miniupnpcstrings.h: miniupnpcstrings.h.in updateminiupnpcstrings.sh VERSION + @$(MKDIR) $(@D) + $(SH) updateminiupnpcstrings.sh $@ $< + +# ftp tool supplied with OpenBSD can download files from http. +jnaerator-%.jar: + wget $(JNAERATORBASEURL)/$@ || \ + curl -o $@ $(JNAERATORBASEURL)/$@ || \ + ftp $(JNAERATORBASEURL)/$@ + +jar: $(SHAREDLIBRARY) $(JNAERATOR) + $(JAVA) -jar $(JNAERATOR) $(JNAERATORARGS) \ + miniupnpc.h miniupnpc_declspec.h upnpcommands.h upnpreplyparse.h \ + igd_desc_parse.h miniwget.h upnperrors.h $(SHAREDLIBRARY) \ + -package fr.free.miniupnp -o . -jar java/miniupnpc_$(JARSUFFIX).jar -v + +mvn_install: + mvn install:install-file -Dfile=java/miniupnpc_$(JARSUFFIX).jar \ + -DgroupId=com.github \ + -DartifactId=miniupnp \ + -Dversion=$(VERSION) \ + -Dpackaging=jar \ + -Dclassifier=$(JARSUFFIX) \ + -DgeneratePom=true \ + -DcreateChecksum=true + +# make .deb packages +deb: /usr/share/pyshared/stdeb all + (python setup.py --command-packages=stdeb.command bdist_deb) + +# install .deb packages +ideb: + (sudo dpkg -i deb_dist/*.deb) + +/usr/share/pyshared/stdeb: /usr/share/doc/python-all-dev + (sudo apt-get install python-stdeb) + +/usr/share/doc/python-all-dev: + (sudo apt-get install python-all-dev) minihttptestserver: minihttptestserver.o -# DO NOT DELETE THIS LINE -- make depend depends on it. +print-%: + @echo "$* = $($*)" -igd_desc_parse.o: igd_desc_parse.h -miniupnpc.o: miniupnpc.h declspec.h igd_desc_parse.h minissdpc.h miniwget.h -miniupnpc.o: minisoap.h minixml.h upnpcommands.h upnpreplyparse.h -miniupnpc.o: portlistingparse.h miniupnpctypes.h connecthostport.h -miniupnpc.o: receivedata.h -minixml.o: minixml.h -minisoap.o: minisoap.h miniupnpcstrings.h -miniwget.o: miniupnpcstrings.h miniwget.h declspec.h connecthostport.h -miniwget.o: receivedata.h -upnpc.o: miniwget.h declspec.h miniupnpc.h igd_desc_parse.h upnpcommands.h -upnpc.o: upnpreplyparse.h portlistingparse.h miniupnpctypes.h upnperrors.h -upnpcommands.o: upnpcommands.h upnpreplyparse.h portlistingparse.h declspec.h -upnpcommands.o: miniupnpctypes.h miniupnpc.h igd_desc_parse.h -upnpreplyparse.o: upnpreplyparse.h minixml.h -testminixml.o: minixml.h igd_desc_parse.h -minixmlvalid.o: minixml.h -testupnpreplyparse.o: upnpreplyparse.h -minissdpc.o: minissdpc.h miniupnpc.h declspec.h igd_desc_parse.h codelength.h -upnperrors.o: upnperrors.h declspec.h upnpcommands.h upnpreplyparse.h -upnperrors.o: portlistingparse.h miniupnpctypes.h miniupnpc.h -upnperrors.o: igd_desc_parse.h -testigddescparse.o: igd_desc_parse.h minixml.h miniupnpc.h declspec.h -testminiwget.o: miniwget.h declspec.h -connecthostport.o: connecthostport.h -receivedata.o: receivedata.h +ifneq ($(MAKECMDGOALS),clean) +-include $(DEPS) +endif diff --git a/libs/miniupnpc/Makefile.mingw b/libs/miniupnpc/Makefile.mingw index 2b66211a7..8c7f1c1cb 100644 --- a/libs/miniupnpc/Makefile.mingw +++ b/libs/miniupnpc/Makefile.mingw @@ -1,93 +1,181 @@ -# $Id: Makefile.mingw,v 1.16 2011/04/18 17:39:31 nanard Exp $ +# $Id: Makefile.mingw,v 1.35 2022/10/16 05:28:15 nanard Exp $ # Miniupnp project. -# http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ -# (c) 2005-2011 Thomas Bernard +# http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ +# (c) 2005-2020 Thomas Bernard # This Makefile is made for MinGW # -CC = gcc +# To cross compile on a *nix machine : +# make -f Makefile.mingw DLLWRAP=mingw32-dllwrap CC=mingw32-gcc AR=mingw32-ar WINDRES=mingw32-windres +# +SRCDIR = src +INCDIR = include + +CC ?= gcc +SETUP_COMPILER_FLAG?= DLLWRAP = dllwrap -#CFLAGS = -Wall -g -DDEBUG -D_WIN32_WINNT=0X501 -CFLAGS = -Wall -Os -DNDEBUG -D_WIN32_WINNT=0X501 -LDLIBS = -lws2_32 -liphlpapi +WINDRES = windres SH = /bin/sh -# -lwsock32 -# -liphlpapi is used for GetBestRoute() +ZIP = zip +ifeq ($(OS),Windows_NT) +RM = del +else +RM = rm -f +endif + +CFLAGS ?= -Os +CFLAGS += -Wall +CFLAGS += -W -Wstrict-prototypes + +CPPFLAGS += -DNDEBUG -D_WIN32_WINNT=0x501 +CPPFLAGS += -Iinclude +CPPFLAGS += -I. + +# -liphlpapi is needed for GetBestRoute() and GetIpAddrTable() +LDLIBS = -lws2_32 -liphlpapi + PYTHON=\utils\python25\python OBJS=miniwget.o minixml.o igd_desc_parse.o minisoap.o \ + minissdpc.o \ miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \ - connecthostport.o portlistingparse.o receivedata.o -OBJSDLL=$(addprefix dll/, $(OBJS)) + connecthostport.o portlistingparse.o receivedata.o \ + upnpdev.o addr_is_reserved.o +OBJSDLL=$(addprefix dll-, $(OBJS)) winres.o +BINARIES=upnpc-static.exe upnpc-shared.exe \ + listdevices-static.exe listdevices-shared.exe \ + miniupnpc.dll libminiupnpc.a \ + testminixml.exe +ifneq ($(GITHUB_SHA),) +COMMITREF=$(GITHUB_SHA) +else +ifneq ($(CI_COMMIT_SHORT_SHA),) +COMMITREF=$(CI_COMMIT_SHORT_SHA) +else +COMMITREF=$(shell git rev-parse --short HEAD) +endif +endif +DISTFILE:=$(shell echo "miniupnpc-bin-win32-`cat VERSION`-$(COMMITREF).zip") -all: init upnpc-static upnpc-shared testminixml libminiupnpc.a miniupnpc.dll +LIBDIR ?= lib +# install directories +ifeq ($(strip $(PREFIX)),) +INSTALLPREFIX ?= /usr +else +INSTALLPREFIX ?= $(PREFIX) +endif -init: - mkdir dll - echo init > init +.PHONY: all dist clean + +all: $(BINARIES) + +dist: $(DISTFILE) clean: - $(RM) upnpc testminixml *.o - $(RM) dll\*.o + $(RM) miniupnpcstrings.h + $(RM) *.o $(RM) *.exe - $(RM) miniupnpc.dll + $(RM) miniupnpc.dll miniupnpc.lib miniupnpc.dll.def $(RM) libminiupnpc.a + $(RM) $(DISTFILE) + +$(DISTFILE): $(BINARIES) + $(ZIP) $@ *.exe *.dll *.lib *.def *.a LICENSE README Changelog.txt libminiupnpc.a: $(OBJS) $(AR) cr $@ $? pythonmodule: libminiupnpc.a - $(PYTHON) setupmingw32.py build --compiler=mingw32 + $(PYTHON) setupmingw32.py build $(SETUP_COMPILER_FLAG) $(PYTHON) setupmingw32.py install --skip-build + $(PYTHON) setupmingw32.py bdist_wheel --skip-build -miniupnpc.dll: libminiupnpc.a $(OBJSDLL) - $(DLLWRAP) -k --driver-name "$(CC)" \ +miniupnpc.dll: miniupnpc.def $(OBJSDLL) + $(DLLWRAP) -k --driver-name $(CC) \ --def miniupnpc.def \ --output-def miniupnpc.dll.def \ --implib miniupnpc.lib -o $@ \ $(OBJSDLL) $(LDLIBS) miniupnpc.lib: miniupnpc.dll - echo $@ generated with $< -dll/upnpc.o: upnpc.o - echo $@ generated with $< +%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -DMINIUPNP_STATICLIB -c -o $@ $< -.c.o: - $(CC) $(CFLAGS) $(CPPFLAGS) -DSTATICLIB -c -o $@ $< - $(CC) $(CFLAGS) $(CPPFLAGS) -DMINIUPNP_EXPORTS -c -o dll/$@ $< +dll-%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -DMINIUPNP_EXPORTS -c -o $@ $< -upnpc.o: - $(CC) $(CFLAGS) $(CPPFLAGS) -DSTATICLIB -c -o $@ $< - $(CC) $(CFLAGS) $(CPPFLAGS) -c -o dll/$@ $< +%-shared.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< -upnpc-static: upnpc.o libminiupnpc.a +# --enable-stdcall-fixup +%-static.exe: %.o libminiupnpc.a + $(CC) -static -o $@ $^ $(LDLIBS) + +%-shared.exe: %-shared.o miniupnpc.lib $(CC) -o $@ $^ $(LDLIBS) -upnpc-shared: dll/upnpc.o miniupnpc.lib - $(CC) -o $@ $^ $(LDLIBS) +# To make miniupnpcstrings.h from miniupnpcstrings.h.in we either +# use a custom executable (if running make under windows) or use +# sed (if cross compiling from another platform). +ifeq ($(OS),Windows_NT) +wingenminiupnpcstrings.exe: wingenminiupnpcstrings.c + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -wingenminiupnpcstrings: wingenminiupnpcstrings.o +miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings.exe VERSION + .\wingenminiupnpcstrings.exe $< $@ rc_version.h -wingenminiupnpcstrings.o: wingenminiupnpcstrings.c +rc_version.h: miniupnpcstrings.h +else +miniupnpcstrings.h: miniupnpcstrings.h.in VERSION + sed 's|OS_STRING ".*"|OS_STRING "Windows/Mingw32"|' $< | \ + sed 's|MINIUPNPC_VERSION_STRING ".*"|MINIUPNPC_VERSION_STRING "$(shell cat VERSION)"|' > $@ -miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings - -$(SH) updateminiupnpcstrings.sh - -wingenminiupnpcstrings $< $@ +rc_version.h: VERSION + echo "#define LIBMINIUPNPC_DOTTED_VERSION \"$(shell cat VERSION)\"" > $@.tmp + echo "#define LIBMINIUPNPC_MAJOR_VERSION $(shell cat VERSION|cut -d. -f1)" >> $@.tmp + echo "#define LIBMINIUPNPC_MINOR_VERSION $(shell cat VERSION|cut -d. -f2)" >> $@.tmp + echo "#define LIBMINIUPNPC_MICRO_VERSION $(shell cat VERSION|cut -d. -f3)" >> $@.tmp + mv $@.tmp $@ +endif -minixml.o: minixml.c minixml.h miniupnpcstrings.h +miniupnpc.pc: VERSION + $(RM) $@ + echo "prefix=$(INSTALLPREFIX)" >> $@ + echo "exec_prefix=\$${prefix}" >> $@ + echo "libdir=\$${exec_prefix}/$(LIBDIR)" >> $@ + echo "includedir=\$${prefix}/include" >> $@ + echo "" >> $@ + echo "Name: miniUPnPc" >> $@ + echo "Description: UPnP IGD client lightweight library" >> $@ + echo "Version: $(shell cat VERSION)" >> $@ + echo "Libs: -L\$${libdir} -lminiupnpc" >> $@ + echo "Cflags: -I\$${includedir}" >> $@ -upnpc.o: upnpc.c miniwget.h minisoap.h miniupnpc.h igd_desc_parse.h upnpreplyparse.h upnpcommands.h upnperrors.h +winres.o: miniupnpc.rc rc_version.h + $(WINDRES) -D INTERNAL_NAME=\\\"miniupnpc.dll\\0\\\" -i $< -o $@ -miniwget.o: miniwget.c miniwget.h miniupnpcstrings.h connecthostport.h +testminixml.exe: testminixml.o minixml.o igd_desc_parse.o + $(CC) -static -o $@ $^ -minisoap.o: minisoap.c minisoap.h miniupnpcstrings.h +minixml.o: $(SRCDIR)/minixml.c $(SRCDIR)/minixml.h -miniupnpc.o: miniupnpc.c miniupnpc.h minisoap.h miniwget.h minixml.h +upnpc.o: include/miniwget.h $(SRCDIR)/minisoap.h include/miniupnpc.h include/igd_desc_parse.h +upnpc.o: include/upnpreplyparse.h include/upnpcommands.h include/upnperrors.h miniupnpcstrings.h -igd_desc_parse.o: igd_desc_parse.c igd_desc_parse.h +miniwget.o: $(SRCDIR)/miniwget.c include/miniwget.h miniupnpcstrings.h $(SRCDIR)/connecthostport.h -testminixml: minixml.o igd_desc_parse.o testminixml.c +minisoap.o: $(SRCDIR)/minisoap.c $(SRCDIR)/minisoap.h miniupnpcstrings.h -upnpreplyparse.o: upnpreplyparse.c upnpreplyparse.h minixml.h +miniupnpc.o: $(SRCDIR)/miniupnpc.c include/miniupnpc.h $(SRCDIR)/minisoap.h \ + include/miniwget.h $(SRCDIR)/minixml.h $(SRCDIR)/addr_is_reserved.h -upnpcommands.o: upnpcommands.c upnpcommands.h upnpreplyparse.h miniupnpc.h portlistingparse.h +igd_desc_parse.o: $(SRCDIR)/igd_desc_parse.c include/igd_desc_parse.h + +upnpreplyparse.o: $(SRCDIR)/upnpreplyparse.c include/upnpreplyparse.h $(SRCDIR)/minixml.h + +upnpcommands.o: $(SRCDIR)/upnpcommands.c include/upnpcommands.h include/upnpreplyparse.h \ + include/miniupnpc.h include/portlistingparse.h + +minissdpc.o: $(SRCDIR)/minissdpc.c $(SRCDIR)/minissdpc.h $(SRCDIR)/receivedata.h + +upnpdev.o: $(SRCDIR)/upnpdev.c include/upnpdev.h diff --git a/libs/miniupnpc/README b/libs/miniupnpc/README index 12c7fed35..aa8ad2024 100644 --- a/libs/miniupnpc/README +++ b/libs/miniupnpc/README @@ -1,18 +1,20 @@ Project: miniupnp -Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ +Project web page: http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ +github: https://github.com/miniupnp/miniupnp Author: Thomas Bernard -Copyright (c) 2005-2011 Thomas Bernard +Copyright (c) 2005-2023 Thomas Bernard This software is subject to the conditions detailed in the LICENSE file provided within this distribution. -For the comfort of Win32 users, bsdqueue.h is included in the distribution. -Its licence is included in the header of the file. -bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system. -* miniupnp Client * +* miniUPnP Client - miniUPnPc * To compile, simply run 'gmake' (could be 'make' on your system). Under win32, to compile with MinGW, type "mingw32make.bat". +MS Visual C solution and project files are supplied in the msvc/ subdirectory. +The miniupnpc library is available as a static library or as a DLL : +define MINIUPNP_STATICLIB if you want to link against the static library. + The compilation is known to work under linux, FreeBSD, OpenBSD, MacOS X, AmigaOS and cygwin. The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3. @@ -23,7 +25,7 @@ To install the library and headers on the system use : > make install > exit -alternatively, to install in a specific location, use : +alternatively, to install into a specific location, use : > INSTALLPREFIX=/usr/local make install upnpc.c is a sample client using the libminiupnpc. @@ -31,6 +33,7 @@ To use the libminiupnpc in your application, link it with libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h, upnpcommands.h and miniwget.h : - upnpDiscover() +- UPNP_GetValidIGD() - miniwget() - parserootdesc() - GetUPNPUrls() @@ -41,9 +44,10 @@ and -lminiupnpc for the link Discovery process is speeded up when MiniSSDPd is running on the machine. + * Python module * -you can build a python module with 'make pythonmodule' +you can build a python module with 'make pythonmodule' and install it with 'make installpythonmodule'. setup.py (and setupmingw32.py) are included in the distribution. @@ -55,5 +59,34 @@ If you are using libminiupnpc in your application, please send me an email ! For any question, you can use the web forum : -http://miniupnp.tuxfamily.org/forum/ +https://miniupnp.tuxfamily.org/forum/ +Bugs should be reported on GitHub : +https://github.com/miniupnp/miniupnp/issues + +* Linux firewall configuration for UPnP clients * + +Due to how UPnP protocol is designed, unicast responses to UPnP multicast client +requests are not tracked by Linux netfilter. And therefore netfilter executes +default action for them (which is in most cases DROP response packet). + +To workaround this limitation, custom ipset hash table can be used. It is +supported since Linux kernel >= 2.6.39. + +Rules for IPv4: +$ ipset create upnp hash:ip,port timeout 3 +$ iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j SET --add-set upnp src,src --exist +$ iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT +$ iptables -A INPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT + +Rules for IPv6: +$ ipset create upnp6 hash:ip,port timeout 3 family inet6 +$ ip6tables -A OUTPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist +$ ip6tables -A OUTPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist +$ ip6tables -A INPUT -p udp -m set --match-set upnp6 dst,dst -j ACCEPT +$ ip6tables -A INPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j ACCEPT +$ ip6tables -A INPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j ACCEPT + +Detailed description is available on: +https://serverfault.com/a/911286 +https://unix.stackexchange.com/a/444804 diff --git a/libs/miniupnpc/VERSION b/libs/miniupnpc/VERSION index 810ee4e91..21bb5e156 100644 --- a/libs/miniupnpc/VERSION +++ b/libs/miniupnpc/VERSION @@ -1 +1 @@ -1.6 +2.2.5 diff --git a/libs/miniupnpc/apiversions.txt b/libs/miniupnpc/apiversions.txt new file mode 100644 index 000000000..d6a0bc3fc --- /dev/null +++ b/libs/miniupnpc/apiversions.txt @@ -0,0 +1,178 @@ +$Id: apiversions.txt,v 1.10 2018/04/06 10:53:13 nanard Exp $ + +Differences in API between miniUPnPc versions + +API version 17 + change struct UPNPDev + move getHTTPResponse() to miniwget_private.h + updated macro : + #define MINIUPNPC_API_VERSION 17 + +API version 16 + added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr() + updated macro : + #define MINIUPNPC_API_VERSION 16 + +API version 15 + changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice() + to "localport". When 0 or 1, behaviour is not changed, but it can take + any other value between 2 and 65535 + Existing programs should be compatible + updated macro : + #define MINIUPNPC_API_VERSION 15 + +API version 14 +miniupnpc.h + add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice() + upnpDiscoverDevices() + getDevicesFromMiniSSDPD() : + connectToMiniSSDPD() / disconnectFromMiniSSDPD() + requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD() + updated macro : + #define MINIUPNPC_API_VERSION 14 + +API version 13 +miniupnpc.h: + add searchalltype param to upnpDiscoverDevices() function + updated macro : + #define MINIUPNPC_API_VERSION 13 + +API version 12 +miniupnpc.h : + add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices() + functions + updated macros : + #define MINIUPNPC_API_VERSION 12 + +API version 11 + +upnpreplyparse.h / portlistingparse.h : + removed usage of sys/queue.h / bsdqueue.h + +miniupnpc.h: + updated macros : + #define MINIUPNPC_API_VERSION 11 + +====================== miniUPnPc version 1.9 ====================== +API version 10 + +upnpcommands.h: + added argument remoteHost to UPNP_GetSpecificPortMappingEntry() + +miniupnpc.h: + updated macros : + #define MINIUPNPC_VERSION "1.9" + #define MINIUPNPC_API_VERSION 10 + +====================== miniUPnPc version 1.8 ====================== +API version 9 + +miniupnpc.h: + updated macros : + #define MINIUPNPC_VERSION "1.8" + #define MINIUPNPC_API_VERSION 9 + added "unsigned int scope_id;" to struct UPNPDev + added scope_id argument to GetUPNPUrls() + + + +====================== miniUPnPc version 1.7 ====================== +API version 8 + +miniupnpc.h : + add new macros : + #define MINIUPNPC_VERSION "1.7" + #define MINIUPNPC_API_VERSION 8 + add rootdescURL to struct UPNPUrls + + + +====================== miniUPnPc version 1.6 ====================== +API version 8 + +Adding support for IPv6. +igd_desc_parse.h : + struct IGDdatas_service : + add char presentationurl[MINIUPNPC_URL_MAXSIZE]; + struct IGDdatas : + add struct IGDdatas_service IPv6FC; +miniupnpc.h : + new macros : + #define UPNPDISCOVER_SUCCESS (0) + #define UPNPDISCOVER_UNKNOWN_ERROR (-1) + #define UPNPDISCOVER_SOCKET_ERROR (-101) + #define UPNPDISCOVER_MEMORY_ERROR (-102) + simpleUPnPcommand() prototype changed (but is normaly not used by API users) + add arguments ipv6 and error to upnpDiscover() : + struct UPNPDev * + upnpDiscover(int delay, const char * multicastif, + const char * minissdpdsock, int sameport, + int ipv6, + int * error); + add controlURL_6FC member to struct UPNPUrls : + struct UPNPUrls { + char * controlURL; + char * ipcondescURL; + char * controlURL_CIF; + char * controlURL_6FC; + }; + +upnpcommands.h : + add leaseDuration argument to UPNP_AddPortMapping() + add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry() + add UPNP_GetListOfPortMappings() function (IGDv2) + add IGDv2 IPv6 related functions : + UPNP_GetFirewallStatus() + UPNP_GetOutboundPinholeTimeout() + UPNP_AddPinhole() + UPNP_UpdatePinhole() + UPNP_DeletePinhole() + UPNP_CheckPinholeWorking() + UPNP_GetPinholePackets() + + + +====================== miniUPnPc version 1.5 ====================== +API version 5 + +new function : +int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *); +new macro in upnpcommands.h : +#define UPNPCOMMAND_HTTP_ERROR + +====================== miniUPnPc version 1.4 ====================== +Same API as version 1.3 + +====================== miniUPnPc version 1.3 ====================== +API version 4 + +Use UNSIGNED_INTEGER type for +UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(), +UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived() +Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping() + +====================== miniUPnPc version 1.2 ====================== +API version 3 + +added sameport argument to upnpDiscover() +struct UPNPDev * +upnpDiscover(int delay, const char * multicastif, + const char * minissdpdsock, int sameport); + +====================== miniUPnPc Version 1.1 ====================== +Same API as 1.0 + + +====================== miniUPnPc Version 1.0 ====================== +API version 2 + + +struct UPNPDev { + struct UPNPDev * pNext; + char * descURL; + char * st; + char buffer[2]; +}; +struct UPNPDev * upnpDiscover(int delay, const char * multicastif, + const char * minissdpdsock); + diff --git a/libs/miniupnpc/bsdqueue.h b/libs/miniupnpc/bsdqueue.h deleted file mode 100644 index 1fe0599f5..000000000 --- a/libs/miniupnpc/bsdqueue.h +++ /dev/null @@ -1,531 +0,0 @@ -/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */ -/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -#ifdef QUEUE_MACRO_DEBUG -#define _Q_INVALIDATE(a) (a) = ((void *)-1) -#else -#define _Q_INVALIDATE(a) -#endif - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#ifdef SLIST_ENTRY -#undef SLIST_ENTRY -#endif - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List access methods. - */ -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_END(head) NULL -#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = SLIST_FIRST(head); \ - (var) != SLIST_END(head); \ - (var) = SLIST_NEXT(var, field)) - -#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ - for ((varp) = &SLIST_FIRST((head)); \ - ((var) = *(varp)) != SLIST_END(head); \ - (varp) = &SLIST_NEXT((var), field)) - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) { \ - SLIST_FIRST(head) = SLIST_END(head); \ -} - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (0) - -#define SLIST_REMOVE_NEXT(head, elm, field) do { \ - (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if ((head)->slh_first == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->slh_first; \ - \ - while (curelm->field.sle_next != (elm)) \ - curelm = curelm->field.sle_next; \ - curelm->field.sle_next = \ - curelm->field.sle_next->field.sle_next; \ - _Q_INVALIDATE((elm)->field.sle_next); \ - } \ -} while (0) - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for((var) = LIST_FIRST(head); \ - (var)!= LIST_END(head); \ - (var) = LIST_NEXT(var, field)) - -/* - * List functions. - */ -#define LIST_INIT(head) do { \ - LIST_FIRST(head) = LIST_END(head); \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (0) - -#define LIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (0) - -#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -/* - * Tail queue definitions. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * tail queue access methods - */ -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) NULL -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -/* XXX */ -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) -#define TAILQ_EMPTY(head) \ - (TAILQ_FIRST(head) == TAILQ_END(head)) - -#define TAILQ_FOREACH(var, head, field) \ - for((var) = TAILQ_FIRST(head); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_NEXT(var, field)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for((var) = TAILQ_LAST(head, headname); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_PREV(var, headname, field)) - -/* - * Tail queue functions. - */ -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -#define TAILQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ - (elm2)->field.tqe_next->field.tqe_prev = \ - &(elm2)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm2)->field.tqe_next; \ - (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ - *(elm2)->field.tqe_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -/* - * Circular queue definitions. - */ -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue access methods - */ -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -#define CIRCLEQ_END(head) ((void *)(head)) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) -#define CIRCLEQ_EMPTY(head) \ - (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for((var) = CIRCLEQ_FIRST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_NEXT(var, field)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for((var) = CIRCLEQ_LAST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_PREV(var, field)) - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = CIRCLEQ_END(head); \ - (head)->cqh_last = CIRCLEQ_END(head); \ -} while (0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = CIRCLEQ_END(head); \ - if ((head)->cqh_last == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.cqe_next = CIRCLEQ_END(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ - CIRCLEQ_END(head)) \ - (head).cqh_last = (elm2); \ - else \ - (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ - if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ - CIRCLEQ_END(head)) \ - (head).cqh_first = (elm2); \ - else \ - (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/libs/miniupnpc/codelength.h b/libs/miniupnpc/codelength.h deleted file mode 100644 index f11e5e936..000000000 --- a/libs/miniupnpc/codelength.h +++ /dev/null @@ -1,24 +0,0 @@ -/* $Id: codelength.h,v 1.1 2008/10/06 22:04:06 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas BERNARD - * copyright (c) 2005-2008 Thomas Bernard - * This software is subjet to the conditions detailed in the - * provided LICENCE file. */ -#ifndef __CODELENGTH_H__ -#define __CODELENGTH_H__ - -/* Encode length by using 7bit per Byte : - * Most significant bit of each byte specifies that the - * following byte is part of the code */ -#define DECODELENGTH(n, p) n = 0; \ - do { n = (n << 7) | (*p & 0x7f); } \ - while(*(p++)&0x80); - -#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ - if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ - if(n>=16384) *(p++) = (n >> 14) | 0x80; \ - if(n>=128) *(p++) = (n >> 7) | 0x80; \ - *(p++) = n & 0x7f; - -#endif - diff --git a/libs/miniupnpc/connecthostport.h b/libs/miniupnpc/connecthostport.h deleted file mode 100644 index 57e24eb27..000000000 --- a/libs/miniupnpc/connecthostport.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: connecthostport.h,v 1.1 2010/04/04 23:21:03 nanard Exp $ */ -/* Project: miniupnp - * http://miniupnp.free.fr/ - * Author: Thomas Bernard - * Copyright (c) 2010 Thomas Bernard - * This software is subjects to the conditions detailed - * in the LICENCE file provided within this distribution */ -#ifndef __CONNECTHOSTPORT_H__ -#define __CONNECTHOSTPORT_H__ - -/* connecthostport() - * return a socket connected (TCP) to the host and port - * or -1 in case of error */ -int connecthostport(const char * host, unsigned short port); - -#endif - diff --git a/libs/miniupnpc/declspec.h b/libs/miniupnpc/declspec.h deleted file mode 100644 index b804247d2..000000000 --- a/libs/miniupnpc/declspec.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __DECLSPEC_H__ -#define __DECLSPEC_H__ - -#if defined(WIN32) && !defined(STATICLIB) - #ifdef MINIUPNP_EXPORTS - #define LIBSPEC __declspec(dllexport) - #else - #define LIBSPEC __declspec(dllimport) - #endif -#else - #define LIBSPEC -#endif - -#endif - diff --git a/libs/miniupnpc/external-ip.sh b/libs/miniupnpc/external-ip.sh index 965d86b2a..3873bd7e1 100755 --- a/libs/miniupnpc/external-ip.sh +++ b/libs/miniupnpc/external-ip.sh @@ -1,4 +1,4 @@ #!/bin/sh -# $Id: external-ip.sh,v 1.1 2010/08/05 12:57:41 nanard Exp $ +# $Id: external-ip.sh,v 1.2 2017/11/02 15:33:09 nanard Exp $ # (c) 2010 Reuben Hawkins -upnpc -s | grep ExternalIPAddress | sed 's/[^0-9\.]//g' +upnpc -s | sed -n -e 's/^ExternalIPAddress = \([0-9.]*\)$/\1/p' diff --git a/libs/miniupnpc/igd_desc_parse.h b/libs/miniupnpc/include/igd_desc_parse.h similarity index 84% rename from libs/miniupnpc/igd_desc_parse.h rename to libs/miniupnpc/include/igd_desc_parse.h index bab1fd56f..0de546b69 100644 --- a/libs/miniupnpc/igd_desc_parse.h +++ b/libs/miniupnpc/include/igd_desc_parse.h @@ -1,13 +1,13 @@ -/* $Id: igd_desc_parse.h,v 1.10 2011/04/11 09:19:24 nanard Exp $ */ +/* $Id: igd_desc_parse.h,v 1.12 2014/11/17 17:19:13 nanard Exp $ */ /* Project : miniupnp * http://miniupnp.free.fr/ * Author : Thomas Bernard - * Copyright (c) 2005-2010 Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ -#ifndef __IGD_DESC_PARSE_H__ -#define __IGD_DESC_PARSE_H__ +#ifndef IGD_DESC_PARSE_H_INCLUDED +#define IGD_DESC_PARSE_H_INCLUDED /* Structure to store the result of the parsing of UPnP * descriptions of Internet Gateway Devices */ @@ -42,7 +42,8 @@ struct IGDdatas { void IGDstartelt(void *, const char *, int); void IGDendelt(void *, const char *, int); void IGDdata(void *, const char *, int); +#ifdef DEBUG void printIGD(struct IGDdatas *); +#endif /* DEBUG */ -#endif - +#endif /* IGD_DESC_PARSE_H_INCLUDED */ diff --git a/libs/miniupnpc/miniupnpc.h b/libs/miniupnpc/include/miniupnpc.h similarity index 52% rename from libs/miniupnpc/miniupnpc.h rename to libs/miniupnpc/include/miniupnpc.h index 50df01777..90537ed45 100644 --- a/libs/miniupnpc/miniupnpc.h +++ b/libs/miniupnpc/include/miniupnpc.h @@ -1,15 +1,17 @@ -/* $Id: miniupnpc.h,v 1.23 2011/04/11 08:21:46 nanard Exp $ */ -/* Project: miniupnp - * http://miniupnp.free.fr/ +/* $Id: miniupnpc.h,v 1.61 2022/10/21 21:15:02 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author: Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2022 Thomas Bernard * This software is subjects to the conditions detailed * in the LICENCE file provided within this distribution */ -#ifndef __MINIUPNPC_H__ -#define __MINIUPNPC_H__ +#ifndef MINIUPNPC_H_INCLUDED +#define MINIUPNPC_H_INCLUDED -#include "declspec.h" +#include "miniupnpc_declspec.h" #include "igd_desc_parse.h" +#include "upnpdev.h" /* error codes : */ #define UPNPDISCOVER_SUCCESS (0) @@ -17,6 +19,16 @@ #define UPNPDISCOVER_SOCKET_ERROR (-101) #define UPNPDISCOVER_MEMORY_ERROR (-102) +/* versions : */ +#define MINIUPNPC_VERSION "2.2.5" +#define MINIUPNPC_API_VERSION 17 + +/* Source port: + Using "1" as an alias for 1900 for backwards compatibility + (presuming one would have used that for the "sameport" parameter) */ +#define UPNP_LOCAL_PORT_ANY 0 +#define UPNP_LOCAL_PORT_SAME 1 + #ifdef __cplusplus extern "C" { #endif @@ -29,13 +41,6 @@ simpleUPnPcommand(int, const char *, const char *, const char *, struct UPNParg *, int *); -struct UPNPDev { - struct UPNPDev * pNext; - char * descURL; - char * st; - char buffer[2]; -}; - /* upnpDiscover() * discover UPnP devices on the network. * The discovered devices are returned as a chained list. @@ -47,21 +52,43 @@ struct UPNPDev { * is NULL. * If multicastif is not NULL, it will be used instead of the default * multicast interface for sending SSDP discover packets. - * If sameport is not null, SSDP packets will be sent from the source port - * 1900 (same as destination port) otherwise system assign a source port. */ -LIBSPEC struct UPNPDev * + * If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent + * from the source port 1900 (same as destination port), if set to + * UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will + * be attempted as the source port. + * "searchalltypes" parameter is useful when searching several types, + * if 0, the discovery will stop with the first type returning results. + * TTL should default to 2. */ +MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif, - const char * minissdpdsock, int sameport, - int ipv6, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, int * error); -/* freeUPNPDevlist() - * free list returned by upnpDiscover() */ -LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); + +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverAll(int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevice(const char * device, int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevices(const char * const deviceTypes[], + int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error, + int searchalltypes); /* parserootdesc() : * parse root XML description of a UPnP device and fill the IGDdatas * structure. */ -LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *); +MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *); /* structure used to get fast access to urls * controlURL: controlURL of the WANIPConnection @@ -74,6 +101,7 @@ struct UPNPUrls { char * ipcondescURL; char * controlURL_CIF; char * controlURL_6FC; + char * rootdescURL; }; /* UPNP_GetValidIGD() : @@ -88,7 +116,7 @@ struct UPNPUrls { * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to * free allocated memory. */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, struct IGDdatas * data, @@ -96,21 +124,25 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, /* UPNP_GetIGDFromUrl() * Used when skipping the discovery process. + * When succeding, urls, data, and lanaddr arguments are set. * return value : * 0 - Not ok * 1 - OK */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetIGDFromUrl(const char * rootdescurl, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen); -LIBSPEC void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *); +MINIUPNP_LIBSPEC void +GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, + const char *, unsigned int); -LIBSPEC void FreeUPNPUrls(struct UPNPUrls *); +MINIUPNP_LIBSPEC void +FreeUPNPUrls(struct UPNPUrls *); /* return 0 or 1 */ -LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *); +MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *); #ifdef __cplusplus diff --git a/libs/miniupnpc/include/miniupnpc_declspec.h b/libs/miniupnpc/include/miniupnpc_declspec.h new file mode 100644 index 000000000..40adb922e --- /dev/null +++ b/libs/miniupnpc/include/miniupnpc_declspec.h @@ -0,0 +1,21 @@ +#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED +#define MINIUPNPC_DECLSPEC_H_INCLUDED + +#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB) + /* for windows dll */ + #ifdef MINIUPNP_EXPORTS + #define MINIUPNP_LIBSPEC __declspec(dllexport) + #else + #define MINIUPNP_LIBSPEC __declspec(dllimport) + #endif +#else + #if defined(__GNUC__) && __GNUC__ >= 4 + /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ + #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define MINIUPNP_LIBSPEC + #endif +#endif + +#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */ + diff --git a/libs/miniupnpc/miniupnpctypes.h b/libs/miniupnpc/include/miniupnpctypes.h similarity index 56% rename from libs/miniupnpc/miniupnpctypes.h rename to libs/miniupnpc/include/miniupnpctypes.h index 86081c3cc..1ae978d70 100644 --- a/libs/miniupnpc/miniupnpctypes.h +++ b/libs/miniupnpc/include/miniupnpctypes.h @@ -1,13 +1,15 @@ -/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */ +/* $Id: miniupnpctypes.h,v 1.3 2021/08/21 09:50:00 nanard Exp $ */ /* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org * Author : Thomas Bernard - * Copyright (c) 2011 Thomas Bernard + * Copyright (c) 2021 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided within this distribution */ -#ifndef __MINIUPNPCTYPES_H__ -#define __MINIUPNPCTYPES_H__ +#ifndef MINIUPNPCTYPES_H_INCLUDED +#define MINIUPNPCTYPES_H_INCLUDED -#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) +/* Use unsigned long long when available : + * strtoull is C99 */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define UNSIGNED_INTEGER unsigned long long #define STRTOUI strtoull #else diff --git a/libs/miniupnpc/include/miniwget.h b/libs/miniupnpc/include/miniwget.h new file mode 100644 index 000000000..92943c144 --- /dev/null +++ b/libs/miniupnpc/include/miniwget.h @@ -0,0 +1,27 @@ +/* $Id: miniwget.h,v 1.13 2018/04/06 10:53:15 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2005-2016 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef MINIWGET_H_INCLUDED +#define MINIWGET_H_INCLUDED + +#include "miniupnpc_declspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *); + +MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *); + +int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/miniupnpc/portlistingparse.h b/libs/miniupnpc/include/portlistingparse.h similarity index 76% rename from libs/miniupnpc/portlistingparse.h rename to libs/miniupnpc/include/portlistingparse.h index 18524786c..661ad1faa 100644 --- a/libs/miniupnpc/portlistingparse.h +++ b/libs/miniupnpc/include/portlistingparse.h @@ -1,22 +1,16 @@ -/* $Id: portlistingparse.h,v 1.4 2011/02/15 23:03:56 nanard Exp $ */ +/* $Id: portlistingparse.h,v 1.11 2015/07/21 13:16:55 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2011 Thomas Bernard + * (c) 2011-2015 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ -#ifndef __PORTLISTINGPARSE_H__ -#define __PORTLISTINGPARSE_H__ +#ifndef PORTLISTINGPARSE_H_INCLUDED +#define PORTLISTINGPARSE_H_INCLUDED -#include "declspec.h" +#include "miniupnpc_declspec.h" /* for the definition of UNSIGNED_INTEGER */ #include "miniupnpctypes.h" -#if defined(NO_SYS_QUEUE_H) || defined(WIN32) || defined(__HAIKU__) -#include "bsdqueue.h" -#else -#include -#endif - #ifdef __cplusplus extern "C" { #endif @@ -37,11 +31,11 @@ typedef enum { PortMappingEltNone, PortMappingEntry, NewRemoteHost, NewExternalPort, NewProtocol, NewInternalPort, NewInternalClient, - NewEnabled, NewDescription, + NewEnabled, NewDescription, NewLeaseTime } portMappingElt; struct PortMapping { - LIST_ENTRY(PortMapping) entries; + struct PortMapping * l_next; /* list next element */ UNSIGNED_INTEGER leaseTime; unsigned short externalPort; unsigned short internalPort; @@ -53,15 +47,15 @@ struct PortMapping { }; struct PortMappingParserData { - LIST_HEAD(portmappinglisthead, PortMapping) head; + struct PortMapping * l_head; /* list head */ portMappingElt curelt; }; -LIBSPEC void +MINIUPNP_LIBSPEC void ParsePortListing(const char * buffer, int bufsize, struct PortMappingParserData * pdata); -LIBSPEC void +MINIUPNP_LIBSPEC void FreePortListing(struct PortMappingParserData * pdata); #ifdef __cplusplus diff --git a/libs/miniupnpc/upnpcommands.h b/libs/miniupnpc/include/upnpcommands.h similarity index 61% rename from libs/miniupnpc/upnpcommands.h rename to libs/miniupnpc/include/upnpcommands.h index 66d95e0ca..994104e55 100644 --- a/libs/miniupnpc/upnpcommands.h +++ b/libs/miniupnpc/include/upnpcommands.h @@ -1,15 +1,13 @@ -/* $Id: upnpcommands.h,v 1.23 2011/04/11 09:14:00 nanard Exp $ */ +/* $Id: upnpcommands.h,v 1.33 2019/02/10 12:29:25 nanard Exp $ */ /* Miniupnp project : http://miniupnp.free.fr/ * Author : Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2018 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided within this distribution */ -#ifndef __UPNPCOMMANDS_H__ -#define __UPNPCOMMANDS_H__ +#ifndef UPNPCOMMANDS_H_INCLUDED +#define UPNPCOMMANDS_H_INCLUDED -#include "upnpreplyparse.h" -#include "portlistingparse.h" -#include "declspec.h" +#include "miniupnpc_declspec.h" #include "miniupnpctypes.h" /* MiniUPnPc return codes : */ @@ -17,24 +15,28 @@ #define UPNPCOMMAND_UNKNOWN_ERROR (-1) #define UPNPCOMMAND_INVALID_ARGS (-2) #define UPNPCOMMAND_HTTP_ERROR (-3) +#define UPNPCOMMAND_INVALID_RESPONSE (-4) +#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5) #ifdef __cplusplus extern "C" { #endif -LIBSPEC UNSIGNED_INTEGER +struct PortMappingParserData; + +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalBytesSent(const char * controlURL, const char * servicetype); -LIBSPEC UNSIGNED_INTEGER +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalBytesReceived(const char * controlURL, const char * servicetype); -LIBSPEC UNSIGNED_INTEGER +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalPacketsSent(const char * controlURL, const char * servicetype); -LIBSPEC UNSIGNED_INTEGER +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalPacketsReceived(const char * controlURL, const char * servicetype); @@ -43,7 +45,7 @@ UPNP_GetTotalPacketsReceived(const char * controlURL, * Return values : * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * or a UPnP Error code */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetStatusInfo(const char * controlURL, const char * servicetype, char * status, @@ -55,23 +57,23 @@ UPNP_GetStatusInfo(const char * controlURL, * Return Values : * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * or a UPnP Error code */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetConnectionTypeInfo(const char * controlURL, const char * servicetype, char * connectionType); /* UPNP_GetExternalIPAddress() call the corresponding UPNP method. * if the third arg is not null the value is copied to it. - * at least 16 bytes must be available + * at least 16 bytes must be available * * Return values : * 0 : SUCCESS * NON ZERO : ERROR Either an UPnP error code or an unknown error. - * + * * possible UPnP Errors : * 402 Invalid Args - See UPnP Device Architecture section on Control. * 501 Action Failed - See UPnP Device Architecture section on Control. */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetExternalIPAddress(const char * controlURL, const char * servicetype, char * extIpAdd); @@ -82,7 +84,7 @@ UPNP_GetExternalIPAddress(const char * controlURL, * return values : * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * or a UPnP Error Code. */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetLinkLayerMaxBitRates(const char* controlURL, const char* servicetype, unsigned int * bitrateDown, @@ -95,33 +97,75 @@ UPNP_GetLinkLayerMaxBitRates(const char* controlURL, * Return values : * 0 : SUCCESS * NON ZERO : ERROR. Either an UPnP error code or an unknown error. - * + * * List of possible UPnP errors for AddPortMapping : * errorCode errorDescription (short) - Description (long) * 402 Invalid Args - See UPnP Device Architecture section on Control. * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization and + * the sender was not authorized. * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be * wild-carded * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded * 718 ConflictInMappingEntry - The port mapping entry specified conflicts * with a mapping assigned previously to another client * 724 SamePortValuesRequired - Internal and External port values - * must be the same + * must be the same * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports * permanent lease times on port mappings * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard * and cannot be a specific IP address or DNS name * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and - * cannot be a specific port value */ -LIBSPEC int + * cannot be a specific port value + * 728 NoPortMapsAvailable - There are not enough free ports available to + * complete port mapping. + * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed + * due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded + */ +MINIUPNP_LIBSPEC int UPNP_AddPortMapping(const char * controlURL, const char * servicetype, - const char * extPort, - const char * inPort, - const char * inClient, - const char * desc, - const char * proto, - const char * remoteHost, - const char * leaseDuration); + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration); + +/* UPNP_AddAnyPortMapping() + * if desc is NULL, it will be defaulted to "libminiupnpc" + * remoteHost is usually NULL because IGD don't support it. + * + * Return values : + * 0 : SUCCESS + * NON ZERO : ERROR. Either an UPnP error code or an unknown error. + * + * List of possible UPnP errors for AddPortMapping : + * errorCode errorDescription (short) - Description (long) + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization and + * the sender was not authorized. + * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be + * wild-carded + * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded + * 728 NoPortMapsAvailable - There are not enough free ports available to + * complete port mapping. + * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed + * due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded + */ +MINIUPNP_LIBSPEC int +UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration, + char * reservedPort); /* UPNP_DeletePortMapping() * Use same argument values as what was used for AddPortMapping(). @@ -132,24 +176,46 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, * * List of possible UPnP errors for DeletePortMapping : * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. * 714 NoSuchEntryInArray - The specified value does not exist in the array */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, - const char * extPort, const char * proto, - const char * remoteHost); + const char * extPort, const char * proto, + const char * remoteHost); + +/* UPNP_DeletePortRangeMapping() + * Use same argument values as what was used for AddPortMapping(). + * remoteHost is usually NULL because IGD don't support it. + * Return Values : + * 0 : SUCCESS + * NON ZERO : error. Either an UPnP error code or an undefined error. + * + * List of possible UPnP errors for DeletePortMapping : + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. + * 730 PortMappingNotFound - This error message is returned if no port + * mapping is found in the specified range. + * 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */ +MINIUPNP_LIBSPEC int +UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, + const char * extPortStart, const char * extPortEnd, + const char * proto, + const char * manage); /* UPNP_GetPortMappingNumberOfEntries() * not supported by all routers */ -LIBSPEC int -UPNP_GetPortMappingNumberOfEntries(const char* controlURL, - const char* servicetype, - unsigned int * num); +MINIUPNP_LIBSPEC int +UPNP_GetPortMappingNumberOfEntries(const char * controlURL, + const char * servicetype, + unsigned int * numEntries); /* UPNP_GetSpecificPortMappingEntry() * retrieves an existing port mapping * params : * in extPort * in proto + * in remoteHost * out intClient (16 bytes) * out intPort (6 bytes) * out desc (80 bytes) @@ -158,12 +224,21 @@ UPNP_GetPortMappingNumberOfEntries(const char* controlURL, * * return value : * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR - * or a UPnP Error Code. */ -LIBSPEC int + * or a UPnP Error Code. + * + * List of possible UPnP errors for _GetSpecificPortMappingEntry : + * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 501 Action Failed - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. + * 714 NoSuchEntryInArray - The specified value does not exist in the array. + */ +MINIUPNP_LIBSPEC int UPNP_GetSpecificPortMappingEntry(const char * controlURL, const char * servicetype, const char * extPort, const char * proto, + const char * remoteHost, char * intClient, char * intPort, char * desc, @@ -188,9 +263,11 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, * * Possible UPNP Error codes : * 402 Invalid Args - See UPnP Device Architecture section on Control. + * 606 Action not authorized - The action requested REQUIRES authorization + * and the sender was not authorized. * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetGenericPortMappingEntry(const char * controlURL, const char * servicetype, const char * index, @@ -212,7 +289,7 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, * 733 InconsistantParameters - NewStartPort and NewEndPort values are not * consistent. */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetListOfPortMappings(const char * controlURL, const char * servicetype, const char * startPort, @@ -221,14 +298,14 @@ UPNP_GetListOfPortMappings(const char * controlURL, const char * numberOfPorts, struct PortMappingParserData * data); -/* IGD:2, functions for service WANIPv6FirewallControl:1 */ -LIBSPEC int +/* IGD:2, functions for service WANIPv6FirewallControl:1 */ +MINIUPNP_LIBSPEC int UPNP_GetFirewallStatus(const char * controlURL, const char * servicetype, - int * firewallEnabled, + int * firewallEnabled, int * inboundPinholeAllowed); -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype, const char * remoteHost, const char * remotePort, @@ -237,7 +314,7 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype const char * proto, int * opTimeout); -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_AddPinhole(const char * controlURL, const char * servicetype, const char * remoteHost, const char * remotePort, @@ -247,19 +324,19 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, const char * leaseTime, char * uniqueID); -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, const char * uniqueID, const char * leaseTime); -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID); -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, const char * uniqueID, int * isWorking); -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, const char * uniqueID, int * packets); diff --git a/libs/miniupnpc/include/upnpdev.h b/libs/miniupnpc/include/upnpdev.h new file mode 100644 index 000000000..171d495be --- /dev/null +++ b/libs/miniupnpc/include/upnpdev.h @@ -0,0 +1,44 @@ +/* $Id: upnpdev.h,v 1.4 2021/08/21 09:45:01 nanard Exp $ */ +/* Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas BERNARD + * copyright (c) 2005-2021 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#ifndef UPNPDEV_H_INCLUDED +#define UPNPDEV_H_INCLUDED + +#include "miniupnpc_declspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct UPNPDev { + struct UPNPDev * pNext; + char * descURL; + char * st; + char * usn; + unsigned int scope_id; +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + /* C99 flexible array member */ + char buffer[]; +#elif defined(__GNUC__) + char buffer[0]; +#else + /* Fallback to a hack */ + char buffer[1]; +#endif +}; + +/* freeUPNPDevlist() + * free list returned by upnpDiscover() */ +MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); + + +#ifdef __cplusplus +} +#endif + + +#endif /* UPNPDEV_H_INCLUDED */ diff --git a/libs/miniupnpc/upnperrors.h b/libs/miniupnpc/include/upnperrors.h similarity index 52% rename from libs/miniupnpc/upnperrors.h rename to libs/miniupnpc/include/upnperrors.h index 2c544c97c..3115aee5b 100644 --- a/libs/miniupnpc/upnperrors.h +++ b/libs/miniupnpc/include/upnperrors.h @@ -1,23 +1,23 @@ -/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */ -/* (c) 2007 Thomas Bernard +/* $Id: upnperrors.h,v 1.6 2015/07/21 13:16:55 nanard Exp $ */ +/* (c) 2007-2015 Thomas Bernard * All rights reserved. * MiniUPnP Project. * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * This software is subjet to the conditions detailed in the * provided LICENCE file. */ -#ifndef __UPNPERRORS_H__ -#define __UPNPERRORS_H__ +#ifndef UPNPERRORS_H_INCLUDED +#define UPNPERRORS_H_INCLUDED -#include "declspec.h" +#include "miniupnpc_declspec.h" #ifdef __cplusplus extern "C" { #endif /* strupnperror() - * Return a string description of the UPnP error code + * Return a string description of the UPnP error code * or NULL for undefinded errors */ -LIBSPEC const char * strupnperror(int err); +MINIUPNP_LIBSPEC const char * strupnperror(int err); #ifdef __cplusplus } diff --git a/libs/miniupnpc/upnpreplyparse.h b/libs/miniupnpc/include/upnpreplyparse.h similarity index 71% rename from libs/miniupnpc/upnpreplyparse.h rename to libs/miniupnpc/include/upnpreplyparse.h index 267ea8783..6badd15b2 100644 --- a/libs/miniupnpc/upnpreplyparse.h +++ b/libs/miniupnpc/include/upnpreplyparse.h @@ -1,34 +1,31 @@ -/* $Id: upnpreplyparse.h,v 1.11 2011/02/07 16:17:06 nanard Exp $ */ +/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2011 Thomas Bernard + * (c) 2006-2013 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ -#ifndef __UPNPREPLYPARSE_H__ -#define __UPNPREPLYPARSE_H__ - -#if defined(NO_SYS_QUEUE_H) || defined(WIN32) || defined(__HAIKU__) -#include "bsdqueue.h" -#else -#include -#endif +#ifndef UPNPREPLYPARSE_H_INCLUDED +#define UPNPREPLYPARSE_H_INCLUDED #ifdef __cplusplus extern "C" { #endif struct NameValue { - LIST_ENTRY(NameValue) entries; - char name[64]; - char value[64]; + struct NameValue * l_next; + char name[64]; + char value[128]; }; struct NameValueParserData { - LIST_HEAD(listhead, NameValue) head; - char curelt[64]; + struct NameValue * l_head; + char curelt[64]; char * portListing; int portListingLength; + int topelt; + const char * cdata; + int cdatalen; }; /* ParseNameValue() */ @@ -45,10 +42,12 @@ char * GetValueFromNameValueList(struct NameValueParserData * pdata, const char * Name); +#if 0 /* GetValueFromNameValueListIgnoreNS() */ char * GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, const char * Name); +#endif /* DisplayNameValueList() */ #ifdef DEBUG diff --git a/libs/miniupnpc/java/JavaBridgeTest.java b/libs/miniupnpc/java/JavaBridgeTest.java index 62bba345c..c658c5990 100644 --- a/libs/miniupnpc/java/JavaBridgeTest.java +++ b/libs/miniupnpc/java/JavaBridgeTest.java @@ -1,4 +1,6 @@ import java.nio.ByteBuffer; +import java.nio.IntBuffer; + import fr.free.miniupnp.*; /** @@ -27,7 +29,7 @@ public class JavaBridgeTest { return; } - devlist = miniupnpc.upnpDiscover(UPNP_DELAY, (String) null, (String) null, 0, null); + devlist = miniupnpc.upnpDiscover(UPNP_DELAY, (String) null, (String) null, 0, 0, (byte)2, IntBuffer.allocate(1)); if (devlist != null) { System.out.println("List of UPNP devices found on the network :"); for (UPNPDev device = devlist; device != null; device = device.pNext) { @@ -70,12 +72,12 @@ public class JavaBridgeTest { System.out.println("AddPortMapping() failed with code " + ret); ret = miniupnpc.UPNP_GetSpecificPortMappingEntry( urls.controlURL.getString(0), new String(data.first.servicetype), - args[0], args[1], intClient, intPort, + args[0], args[1], null, intClient, intPort, desc, enabled, leaseDuration); if (ret != MiniupnpcLibrary.UPNPCOMMAND_SUCCESS) System.out.println("GetSpecificPortMappingEntry() failed with code " + ret); System.out.println("InternalIP:Port = " + - new String(intClient.array()) + ":" + new String(intPort.array()) + + new String(intClient.array()) + ":" + new String(intPort.array()) + " (" + new String(desc.array()) + ")"); ret = miniupnpc.UPNP_DeletePortMapping( urls.controlURL.getString(0), diff --git a/libs/miniupnpc/java/testjava.bat b/libs/miniupnpc/java/testjava.bat new file mode 100644 index 000000000..b836da149 --- /dev/null +++ b/libs/miniupnpc/java/testjava.bat @@ -0,0 +1,8 @@ +@echo off +set JAVA=java +set JAVAC=javac +REM notice the semicolon for Windows. Write once, run ... oh nevermind +set CP=miniupnpc_win32.jar;. + +%JAVAC% -cp "%CP%" JavaBridgeTest.java || exit 1 +%JAVA% -cp "%CP%" JavaBridgeTest 12345 UDP || exit 1 diff --git a/libs/miniupnpc/java/testjava.sh b/libs/miniupnpc/java/testjava.sh index c997baf9e..9880523a1 100755 --- a/libs/miniupnpc/java/testjava.sh +++ b/libs/miniupnpc/java/testjava.sh @@ -1,8 +1,8 @@ -#! /bin/sh +#!/bin/bash JAVA=java JAVAC=javac +CP=$(for i in *.jar; do echo -n $i:; done). -$JAVAC -cp miniupnpc_Linux.jar JavaBridgeTest.java -$JAVA -cp miniupnpc_Linux.jar:. JavaBridgeTest 12345 UDP - +$JAVAC -cp $CP JavaBridgeTest.java || exit 1 +$JAVA -cp $CP JavaBridgeTest 12345 UDP || exit 1 diff --git a/libs/miniupnpc/man3/miniupnpc.3 b/libs/miniupnpc/man3/miniupnpc.3 index 0e35aaac5..e5c3ec88c 100644 --- a/libs/miniupnpc/man3/miniupnpc.3 +++ b/libs/miniupnpc/man3/miniupnpc.3 @@ -1,5 +1,4 @@ -\" $Id: miniupnpc.3,v 1.3 2011/07/25 18:02:11 nanard Exp $ -.TH miniupnpc 3 +.TH MINIUPNPC 3 .SH NAME miniupnpc \- UPnP client library .SH SYNOPSIS @@ -26,25 +25,28 @@ through the miniupnpc API. The name of the C functions are matching the UPnP methods names. ie: GetGenericPortMappingEntry is UPNP_GetGenericPortMappingEntry. .SH "API FUNCTIONS" -.IP "struct UPNPDev * upnpDiscover(int delay, const char * multicastif, const char * minissdpdsock, int sameport, int ipv6, int * error);" +.IP "struct UPNPDev * upnpDiscover(int delay, const char * multicastif, const char * minissdpdsock, int localport, int ipv6, int * error);" execute the discovery process. delay (in millisecond) is the maximum time for waiting any device response. If available, device list will be obtained from MiniSSDPd. Default path for minissdpd socket will be used if minissdpdsock argument is NULL. If multicastif is not NULL, it will be used instead of the default multicast interface for sending SSDP discover packets. -If sameport is not null, SSDP packets will be sent from the source port 1900 (same as destination port) otherwise system assign a source port. +If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent +from the source port 1900 (same as destination port), if set to +UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will +be attempted as the source port. If ipv6 is not 0, IPv6 is used instead of IPv4 for the discovery process. .IP "void freeUPNPDevlist(struct UPNPDev * devlist);" free the list returned by upnpDiscover(). .IP "int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen);" browse the list of device returned by upnpDiscover(), find a live UPnP internet gateway device and fill structures passed as arguments -with data used for UPNP methods invokation. +with data used for UPNP methods invocation. .IP "int UPNP_GetIGDFromUrl(const char * rootdescurl, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen);" -permit to bypass the upnpDiscover() call if the xml root description +permit one to bypass the upnpDiscover() call if the xml root description URL of the UPnP IGD is known. Fill structures passed as arguments -with data used for UPNP methods invokation. +with data used for UPNP methods invocation. .IP "void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *);" .IP "void FreeUPNPUrls(struct UPNPUrls *);" diff --git a/libs/miniupnpc/mingw32/libminiupnpc.a b/libs/miniupnpc/mingw32/libminiupnpc.a index fb2065753..063e379bd 100644 Binary files a/libs/miniupnpc/mingw32/libminiupnpc.a and b/libs/miniupnpc/mingw32/libminiupnpc.a differ diff --git a/libs/miniupnpc/mingw64/libminiupnpc.a b/libs/miniupnpc/mingw64/libminiupnpc.a index 4275196d3..afa9533fb 100644 Binary files a/libs/miniupnpc/mingw64/libminiupnpc.a and b/libs/miniupnpc/mingw64/libminiupnpc.a differ diff --git a/libs/miniupnpc/minissdpc.c b/libs/miniupnpc/minissdpc.c deleted file mode 100644 index e5e852855..000000000 --- a/libs/miniupnpc/minissdpc.c +++ /dev/null @@ -1,132 +0,0 @@ -/* $Id: minissdpc.c,v 1.14 2010/11/25 09:57:25 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas BERNARD - * copyright (c) 2005-2009 Thomas Bernard - * This software is subjet to the conditions detailed in the - * provided LICENCE file. */ -/*#include */ -#include -#include -#include -#include -#include -#if defined(WIN32) || defined(__amigaos__) || defined(__amigaos4__) -#ifdef WIN32 -#include -#include -#include -#include -#include -#endif -#if defined(__amigaos__) || defined(__amigaos4__) -#include -#endif -#if defined(__amigaos__) -#define uint16_t unsigned short -#endif -/* Hack */ -#define UNIX_PATH_LEN 108 -struct sockaddr_un { - uint16_t sun_family; - char sun_path[UNIX_PATH_LEN]; -}; -#else -#include -#include -#endif - -#include "minissdpc.h" -#include "miniupnpc.h" - -#include "codelength.h" - -struct UPNPDev * -getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath) -{ - struct UPNPDev * tmp; - struct UPNPDev * devlist = NULL; - unsigned char buffer[2048]; - ssize_t n; - unsigned char * p; - unsigned char * url; - unsigned int i; - unsigned int urlsize, stsize, usnsize, l; - int s; - struct sockaddr_un addr; - - s = socket(AF_UNIX, SOCK_STREAM, 0); - if(s < 0) - { - /*syslog(LOG_ERR, "socket(unix): %m");*/ - perror("socket(unix)"); - return NULL; - } - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path)); - /* TODO : check if we need to handle the EINTR */ - if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) - { - /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/ - close(s); - return NULL; - } - stsize = strlen(devtype); - buffer[0] = 1; /* request type 1 : request devices/services by type */ - p = buffer + 1; - l = stsize; CODELENGTH(l, p); - if(p + stsize > buffer + sizeof(buffer)) - { - /* devtype is too long ! */ - close(s); - return NULL; - } - memcpy(p, devtype, stsize); - p += stsize; - if(write(s, buffer, p - buffer) < 0) - { - /*syslog(LOG_ERR, "write(): %m");*/ - perror("minissdpc.c: write()"); - close(s); - return NULL; - } - n = read(s, buffer, sizeof(buffer)); - if(n<=0) - { - perror("minissdpc.c: read()"); - close(s); - return NULL; - } - p = buffer + 1; - for(i = 0; i < buffer[0]; i++) - { - if(p+2>=buffer+sizeof(buffer)) - break; - DECODELENGTH(urlsize, p); - if(p+urlsize+2>=buffer+sizeof(buffer)) - break; - url = p; - p += urlsize; - DECODELENGTH(stsize, p); - if(p+stsize+2>=buffer+sizeof(buffer)) - break; - tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize); - tmp->pNext = devlist; - tmp->descURL = tmp->buffer; - tmp->st = tmp->buffer + 1 + urlsize; - memcpy(tmp->buffer, url, urlsize); - tmp->buffer[urlsize] = '\0'; - memcpy(tmp->buffer + urlsize + 1, p, stsize); - p += stsize; - tmp->buffer[urlsize+1+stsize] = '\0'; - devlist = tmp; - /* added for compatibility with recent versions of MiniSSDPd - * >= 2007/12/19 */ - DECODELENGTH(usnsize, p); - p += usnsize; - if(p>buffer + sizeof(buffer)) - break; - } - close(s); - return devlist; -} - diff --git a/libs/miniupnpc/minissdpc.h b/libs/miniupnpc/minissdpc.h deleted file mode 100644 index 25e91ce31..000000000 --- a/libs/miniupnpc/minissdpc.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: minissdpc.h,v 1.1 2007/08/31 15:15:33 nanard Exp $ */ -/* Project: miniupnp - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * Author: Thomas Bernard - * Copyright (c) 2005-2007 Thomas Bernard - * This software is subjects to the conditions detailed - * in the LICENCE file provided within this distribution */ -#ifndef __MINISSDPC_H__ -#define __MINISSDPC_H__ - -struct UPNPDev * -getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath); - -#endif - diff --git a/libs/miniupnpc/miniupnpc-config.cmake b/libs/miniupnpc/miniupnpc-config.cmake new file mode 100644 index 000000000..116876a5d --- /dev/null +++ b/libs/miniupnpc/miniupnpc-config.cmake @@ -0,0 +1,6 @@ +if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-shared.cmake" OR MINIUPNPC_USE_STATIC_LIBS) + include("${CMAKE_CURRENT_LIST_DIR}/miniupnpc-private.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-static.cmake") +else() + include("${CMAKE_CURRENT_LIST_DIR}/libminiupnpc-shared.cmake") +endif() diff --git a/libs/miniupnpc/miniupnpc.c b/libs/miniupnpc/miniupnpc.c deleted file mode 100644 index 0a3aae33d..000000000 --- a/libs/miniupnpc/miniupnpc.c +++ /dev/null @@ -1,943 +0,0 @@ -/* $Id: miniupnpc.c,v 1.95 2011/05/15 21:42:26 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas BERNARD - * copyright (c) 2005-2011 Thomas Bernard - * This software is subjet to the conditions detailed in the - * provided LICENSE file. */ -#define __EXTENSIONS__ 1 -#if !defined(MACOSX) && !defined(__sun) -#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__) -#ifndef __cplusplus -#define _XOPEN_SOURCE 600 -#endif -#endif -#ifndef __BSD_VISIBLE -#define __BSD_VISIBLE 1 -#endif -#endif - -#include -#include -#include -#ifdef WIN32 -/* Win32 Specific includes and defines */ -#include -#include -#include -#include -#define snprintf _snprintf -#ifndef strncasecmp -#if defined(_MSC_VER) && (_MSC_VER >= 1400) -#define strncasecmp _memicmp -#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ -#define strncasecmp memicmp -#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ -#endif /* #ifndef strncasecmp */ -#define MAXHOSTNAMELEN 64 -#else /* #ifdef WIN32 */ -/* Standard POSIX includes */ -#include -#if defined(__amigaos__) && !defined(__amigaos4__) -/* Amiga OS 3 specific stuff */ -#define socklen_t int -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#if !defined(__amigaos__) && !defined(__amigaos4__) -#include -#endif -#include -#include -#define closesocket close -#endif /* #else WIN32 */ -#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT -#include -#endif -#if defined(__amigaos__) || defined(__amigaos4__) -/* Amiga OS specific stuff */ -#define TIMEVAL struct timeval -#endif - -#include "miniupnpc.h" -#include "minissdpc.h" -#include "miniwget.h" -#include "minisoap.h" -#include "minixml.h" -#include "upnpcommands.h" -#include "connecthostport.h" -#include "receivedata.h" - -#ifdef WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif - -#define SOAPPREFIX "s" -#define SERVICEPREFIX "u" -#define SERVICEPREFIX2 'u' - -/* root description parsing */ -LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data) -{ - struct xmlparser parser; - /* xmlparser object */ - parser.xmlstart = buffer; - parser.xmlsize = bufsize; - parser.data = data; - parser.starteltfunc = IGDstartelt; - parser.endeltfunc = IGDendelt; - parser.datafunc = IGDdata; - parser.attfunc = 0; - parsexml(&parser); -#ifdef DEBUG - printIGD(data); -#endif -} - -/* simpleUPnPcommand2 : - * not so simple ! - * return values : - * pointer - OK - * NULL - error */ -char * simpleUPnPcommand2(int s, const char * url, const char * service, - const char * action, struct UPNParg * args, - int * bufsize, const char * httpversion) -{ - char hostname[MAXHOSTNAMELEN+1]; - unsigned short port = 0; - char * path; - char soapact[128]; - char soapbody[2048]; - char * buf; - int n; - - *bufsize = 0; - snprintf(soapact, sizeof(soapact), "%s#%s", service, action); - if(args==NULL) - { - /*soapbodylen = */snprintf(soapbody, sizeof(soapbody), - "\r\n" - "<" SOAPPREFIX ":Envelope " - "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" " - SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" - "<" SOAPPREFIX ":Body>" - "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">" - "" - "" - "\r\n", action, service, action); - } - else - { - char * p; - const char * pe, * pv; - int soapbodylen; - soapbodylen = snprintf(soapbody, sizeof(soapbody), - "\r\n" - "<" SOAPPREFIX ":Envelope " - "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" " - SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" - "<" SOAPPREFIX ":Body>" - "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">", - action, service); - p = soapbody + soapbodylen; - while(args->elt) - { - /* check that we are never overflowing the string... */ - if(soapbody + sizeof(soapbody) <= p + 100) - { - /* we keep a margin of at least 100 bytes */ - return NULL; - } - *(p++) = '<'; - pe = args->elt; - while(*pe) - *(p++) = *(pe++); - *(p++) = '>'; - if((pv = args->val)) - { - while(*pv) - *(p++) = *(pv++); - } - *(p++) = '<'; - *(p++) = '/'; - pe = args->elt; - while(*pe) - *(p++) = *(pe++); - *(p++) = '>'; - args++; - } - *(p++) = '<'; - *(p++) = '/'; - *(p++) = SERVICEPREFIX2; - *(p++) = ':'; - pe = action; - while(*pe) - *(p++) = *(pe++); - strncpy(p, ">\r\n", - soapbody + sizeof(soapbody) - p); - } - if(!parseURL(url, hostname, &port, &path)) return NULL; - if(s<0) - { - s = connecthostport(hostname, port); - if(s < 0) - { - return NULL; - } - } - - n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion); - if(n<=0) { -#ifdef DEBUG - printf("Error sending SOAP request\n"); -#endif - closesocket(s); - return NULL; - } - - buf = getHTTPResponse(s, bufsize); -#ifdef DEBUG - if(*bufsize > 0 && buf) - { - printf("SOAP Response :\n%.*s\n", *bufsize, buf); - } -#endif - closesocket(s); - return buf; -} - -/* simpleUPnPcommand : - * not so simple ! - * return values : - * pointer - OK - * NULL - error */ -char * simpleUPnPcommand(int s, const char * url, const char * service, - const char * action, struct UPNParg * args, - int * bufsize) -{ - char * buf; - - buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1"); -/* - buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0"); - if (!buf || *bufsize == 0) - { -#if DEBUG - printf("Error or no result from SOAP request; retrying with HTTP/1.1\n"); -#endif - buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1"); - } -*/ - return buf; -} - -/* parseMSEARCHReply() - * the last 4 arguments are filled during the parsing : - * - location/locationsize : "location:" field of the SSDP reply packet - * - st/stsize : "st:" field of the SSDP reply packet. - * The strings are NOT null terminated */ -static void -parseMSEARCHReply(const char * reply, int size, - const char * * location, int * locationsize, - const char * * st, int * stsize) -{ - int a, b, i; - i = 0; - a = i; /* start of the line */ - b = 0; /* end of the "header" (position of the colon) */ - while(isin_addr, sizeof (struct in_addr)); - break; - - case AF_INET6: - memcpy (dst, &sin6->sin6_addr, sizeof (struct in6_addr)); - break; - } - return 1; - } - - return 0; - } -#endif - -/* upnpDiscover() : - * return a chained list of all devices found or NULL if - * no devices was found. - * It is up to the caller to free the chained list - * delay is in millisecond (poll) */ -LIBSPEC struct UPNPDev * -upnpDiscover(int delay, const char * multicastif, - const char * minissdpdsock, int sameport, - int ipv6, - int * error) -{ - struct UPNPDev * tmp; - struct UPNPDev * devlist = 0; - int opt = 1; - static const char MSearchMsgFmt[] = - "M-SEARCH * HTTP/1.1\r\n" - "HOST: %s:" XSTR(PORT) "\r\n" - "ST: %s\r\n" - "MAN: \"ssdp:discover\"\r\n" - "MX: %u\r\n" - "\r\n"; - static const char * const deviceList[] = { -#if 0 - "urn:schemas-upnp-org:device:InternetGatewayDevice:2", - "urn:schemas-upnp-org:service:WANIPConnection:2", -#endif - "urn:schemas-upnp-org:device:InternetGatewayDevice:1", - "urn:schemas-upnp-org:service:WANIPConnection:1", - "urn:schemas-upnp-org:service:WANPPPConnection:1", - "upnp:rootdevice", - 0 - }; - int deviceIndex = 0; - char bufr[1536]; /* reception and emission buffer */ - int sudp; - int n; - struct sockaddr_storage sockudp_r; - unsigned int mx; -#ifdef NO_GETADDRINFO - struct sockaddr_storage sockudp_w; -#else - int rv; - struct addrinfo hints, *servinfo, *p; -#endif -#ifdef WIN32 - MIB_IPFORWARDROW ip_forward; -#endif - int linklocal = 1; - - if(error) - *error = UPNPDISCOVER_UNKNOWN_ERROR; -#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) - /* first try to get infos from minissdpd ! */ - if(!minissdpdsock) - minissdpdsock = "/var/run/minissdpd.sock"; - while(!devlist && deviceList[deviceIndex]) { - devlist = getDevicesFromMiniSSDPD(deviceList[deviceIndex], - minissdpdsock); - /* We return what we have found if it was not only a rootdevice */ - if(devlist && !strstr(deviceList[deviceIndex], "rootdevice")) { - if(error) - *error = UPNPDISCOVER_SUCCESS; - return devlist; - } - deviceIndex++; - } - deviceIndex = 0; -#endif - /* fallback to direct discovery */ -#ifdef WIN32 - sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP); -#else - sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0); -#endif - if(sudp < 0) - { - if(error) - *error = UPNPDISCOVER_SOCKET_ERROR; - PRINT_SOCKET_ERROR("socket"); - return NULL; - } - /* reception */ - memset(&sockudp_r, 0, sizeof(struct sockaddr_storage)); - if(ipv6) { - struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r; - p->sin6_family = AF_INET6; - if(sameport) - p->sin6_port = htons(PORT); - p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */ - } else { - struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r; - p->sin_family = AF_INET; - if(sameport) - p->sin_port = htons(PORT); - p->sin_addr.s_addr = INADDR_ANY; - } -#ifdef WIN32 -/* This code could help us to use the right Network interface for - * SSDP multicast traffic */ -/* Get IP associated with the index given in the ip_forward struct - * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */ - if(!ipv6 - && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) { - DWORD dwRetVal = 0; - PMIB_IPADDRTABLE pIPAddrTable; - DWORD dwSize = 0; -#ifdef DEBUG - IN_ADDR IPAddr; -#endif - int i; -#ifdef DEBUG - printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop); -#endif - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE)); - if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { - free(pIPAddrTable); - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize); - } - if(pIPAddrTable) { - dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); -#ifdef DEBUG - printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries); -#endif - for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) { -#ifdef DEBUG - printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr; - printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask; - printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr; - printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr); - printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize); - printf("\tType and State[%d]:", i); - printf("\n"); -#endif - if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) { - /* Set the address of this interface to be used */ - struct in_addr mc_if; - memset(&mc_if, 0, sizeof(mc_if)); - mc_if.s_addr = pIPAddrTable->table[i].dwAddr; - if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); - } - ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr; -#ifndef DEBUG - break; -#endif - } - } - free(pIPAddrTable); - pIPAddrTable = NULL; - } - } -#endif - -#ifdef WIN32 - if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0) -#else - if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0) -#endif - { - if(error) - *error = UPNPDISCOVER_SOCKET_ERROR; - PRINT_SOCKET_ERROR("setsockopt"); - return NULL; - } - - if(multicastif) - { - if(ipv6) { -#if !defined(WIN32) - /* according to MSDN, if_nametoindex() is supported since - * MS Windows Vista and MS Windows Server 2008. - * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */ - unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */ - if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(&ifindex)) < 0) - { - PRINT_SOCKET_ERROR("setsockopt"); - } -#else -#ifdef DEBUG - printf("Setting of multicast interface not supported in IPv6 under Windows.\n"); -#endif -#endif - } else { - struct in_addr mc_if; - mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */ - ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr; - if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) - { - PRINT_SOCKET_ERROR("setsockopt"); - } - } - } - - /* Avant d'envoyer le paquet on bind pour recevoir la reponse */ - if (bind(sudp, (const struct sockaddr *)&sockudp_r, - ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0) - { - if(error) - *error = UPNPDISCOVER_SOCKET_ERROR; - PRINT_SOCKET_ERROR("bind"); - closesocket(sudp); - return NULL; - } - - if(error) - *error = UPNPDISCOVER_SUCCESS; - /* Calculating maximum response time in seconds */ - mx = ((unsigned int)delay) / 1000u; - /* receiving SSDP response packet */ - for(n = 0; deviceList[deviceIndex]; deviceIndex++) - { - if(n == 0) - { - /* sending the SSDP M-SEARCH packet */ - n = snprintf(bufr, sizeof(bufr), - MSearchMsgFmt, - ipv6 ? - (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]") - : UPNP_MCAST_ADDR, - deviceList[deviceIndex], mx); -#ifdef DEBUG - printf("Sending %s", bufr); -#endif -#ifdef NO_GETADDRINFO - /* the following code is not using getaddrinfo */ - /* emission */ - memset(&sockudp_w, 0, sizeof(struct sockaddr_storage)); - if(ipv6) { - struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w; - p->sin6_family = AF_INET6; - p->sin6_port = htons(PORT); - inet_pton(AF_INET6, - linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR, - &(p->sin6_addr)); - } else { - struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w; - p->sin_family = AF_INET; - p->sin_port = htons(PORT); - p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR); - } - n = sendto(sudp, bufr, n, 0, - (const void *)&sockudp_w, - ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - if (n < 0) { - if(error) - *error = UPNPDISCOVER_SOCKET_ERROR; - PRINT_SOCKET_ERROR("sendto"); - break; - } -#else /* #ifdef NO_GETADDRINFO */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET - hints.ai_socktype = SOCK_DGRAM; - /*hints.ai_flags = */ - if ((rv = getaddrinfo(ipv6 - ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR) - : UPNP_MCAST_ADDR, - XSTR(PORT), &hints, &servinfo)) != 0) { - if(error) - *error = UPNPDISCOVER_SOCKET_ERROR; -#ifdef WIN32 - fprintf(stderr, "getaddrinfo() failed: %d\n", rv); -#else - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); -#endif - break; - } - for(p = servinfo; p; p = p->ai_next) { - n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen); - if (n < 0) { - PRINT_SOCKET_ERROR("sendto"); - continue; - } - } - freeaddrinfo(servinfo); - if(n < 0) { - if(error) - *error = UPNPDISCOVER_SOCKET_ERROR; - break; - } -#endif /* #ifdef NO_GETADDRINFO */ - } - /* Waiting for SSDP REPLY packet to M-SEARCH */ - n = receivedata(sudp, bufr, sizeof(bufr), delay); - if (n < 0) { - /* error */ - if(error) - *error = UPNPDISCOVER_SOCKET_ERROR; - break; - } else if (n == 0) { - /* no data or Time Out */ - if (devlist) { - /* no more device type to look for... */ - if(error) - *error = UPNPDISCOVER_SUCCESS; - break; - } - if(ipv6) { - if(linklocal) { - linklocal = 0; - --deviceIndex; - } else { - linklocal = 1; - } - } - } else { - const char * descURL=NULL; - int urlsize=0; - const char * st=NULL; - int stsize=0; - /*printf("%d byte(s) :\n%s\n", n, bufr);*/ /* affichage du message */ - parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize); - if(st&&descURL) - { -#ifdef DEBUG - printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n", - stsize, st, urlsize, descURL); -#endif - for(tmp=devlist; tmp; tmp = tmp->pNext) { - if(memcmp(tmp->descURL, descURL, urlsize) == 0 && - tmp->descURL[urlsize] == '\0' && - memcmp(tmp->st, st, stsize) == 0 && - tmp->st[stsize] == '\0') - break; - } - /* at the exit of the loop above, tmp is null if - * no duplicate device was found */ - if(tmp) - continue; - tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize); - if(!tmp) { - /* memory allocation error */ - if(error) - *error = UPNPDISCOVER_MEMORY_ERROR; - break; - } - tmp->pNext = devlist; - tmp->descURL = tmp->buffer; - tmp->st = tmp->buffer + 1 + urlsize; - memcpy(tmp->buffer, descURL, urlsize); - tmp->buffer[urlsize] = '\0'; - memcpy(tmp->buffer + urlsize + 1, st, stsize); - tmp->buffer[urlsize+1+stsize] = '\0'; - devlist = tmp; - } - } - } - closesocket(sudp); - return devlist; -} - -/* freeUPNPDevlist() should be used to - * free the chained list returned by upnpDiscover() */ -LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist) -{ - struct UPNPDev * next; - while(devlist) - { - next = devlist->pNext; - free(devlist); - devlist = next; - } -} - -static void -url_cpy_or_cat(char * dst, const char * src, int n) -{ - if( (src[0] == 'h') - &&(src[1] == 't') - &&(src[2] == 't') - &&(src[3] == 'p') - &&(src[4] == ':') - &&(src[5] == '/') - &&(src[6] == '/')) - { - strncpy(dst, src, n); - } - else - { - int l = strlen(dst); - if(src[0] != '/') - dst[l++] = '/'; - if(l<=n) - strncpy(dst + l, src, n - l); - } -} - -/* Prepare the Urls for usage... - */ -LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data, - const char * descURL) -{ - char * p; - int n1, n2, n3, n4; - n1 = strlen(data->urlbase); - if(n1==0) - n1 = strlen(descURL); - n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */ - n2 = n1; n3 = n1; n4 = n1; - n1 += strlen(data->first.scpdurl); - n2 += strlen(data->first.controlurl); - n3 += strlen(data->CIF.controlurl); - n4 += strlen(data->IPv6FC.controlurl); - - urls->ipcondescURL = (char *)malloc(n1); - urls->controlURL = (char *)malloc(n2); - urls->controlURL_CIF = (char *)malloc(n3); - urls->controlURL_6FC = (char *)malloc(n4); - /* maintenant on chope la desc du WANIPConnection */ - if(data->urlbase[0] != '\0') - strncpy(urls->ipcondescURL, data->urlbase, n1); - else - strncpy(urls->ipcondescURL, descURL, n1); - p = strchr(urls->ipcondescURL+7, '/'); - if(p) p[0] = '\0'; - strncpy(urls->controlURL, urls->ipcondescURL, n2); - strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3); - strncpy(urls->controlURL_6FC, urls->ipcondescURL, n4); - - url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1); - - url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2); - - url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3); - - url_cpy_or_cat(urls->controlURL_6FC, data->IPv6FC.controlurl, n4); - -#ifdef DEBUG - printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL, - (unsigned)strlen(urls->ipcondescURL), n1); - printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL, - (unsigned)strlen(urls->controlURL), n2); - printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF, - (unsigned)strlen(urls->controlURL_CIF), n3); - printf("urls->controlURL_6FC='%s' %u n4=%d\n", urls->controlURL_6FC, - (unsigned)strlen(urls->controlURL_6FC), n4); -#endif -} - -LIBSPEC void -FreeUPNPUrls(struct UPNPUrls * urls) -{ - if(!urls) - return; - free(urls->controlURL); - urls->controlURL = 0; - free(urls->ipcondescURL); - urls->ipcondescURL = 0; - free(urls->controlURL_CIF); - urls->controlURL_CIF = 0; - free(urls->controlURL_6FC); - urls->controlURL_6FC = 0; -} - -int -UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data) -{ - char status[64]; - unsigned int uptime; - status[0] = '\0'; - UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype, - status, &uptime, NULL); - if(0 == strcmp("Connected", status)) - { - return 1; - } - else - return 0; -} - - -/* UPNP_GetValidIGD() : - * return values : - * 0 = NO IGD found - * 1 = A valid connected IGD has been found - * 2 = A valid IGD has been found but it reported as - * not connected - * 3 = an UPnP device has been found but was not recognized as an IGD - * - * In any non zero return case, the urls and data structures - * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to - * free allocated memory. - */ -LIBSPEC int -UPNP_GetValidIGD(struct UPNPDev * devlist, - struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen) -{ - char * descXML; - int descXMLsize = 0; - struct UPNPDev * dev; - int ndev = 0; - int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ - if(!devlist) - { -#ifdef DEBUG - printf("Empty devlist\n"); -#endif - return 0; - } - for(state = 1; state <= 3; state++) - { - for(dev = devlist; dev; dev = dev->pNext) - { - /* we should choose an internet gateway device. - * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */ - descXML = miniwget_getaddr(dev->descURL, &descXMLsize, - lanaddr, lanaddrlen); - if(descXML) - { - ndev++; - memset(data, 0, sizeof(struct IGDdatas)); - memset(urls, 0, sizeof(struct UPNPUrls)); - parserootdesc(descXML, descXMLsize, data); - free(descXML); - descXML = NULL; - if(0==strcmp(data->CIF.servicetype, - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1") - || state >= 3 ) - { - GetUPNPUrls(urls, data, dev->descURL); - -#ifdef DEBUG - printf("UPNPIGD_IsConnected(%s) = %d\n", - urls->controlURL, - UPNPIGD_IsConnected(urls, data)); -#endif - if((state >= 2) || UPNPIGD_IsConnected(urls, data)) - return state; - FreeUPNPUrls(urls); - if(data->second.servicetype[0] != '\0') { -#ifdef DEBUG - printf("We tried %s, now we try %s !\n", - data->first.servicetype, data->second.servicetype); -#endif - /* swaping WANPPPConnection and WANIPConnection ! */ - memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service)); - memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service)); - memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service)); - GetUPNPUrls(urls, data, dev->descURL); -#ifdef DEBUG - printf("UPNPIGD_IsConnected(%s) = %d\n", - urls->controlURL, - UPNPIGD_IsConnected(urls, data)); -#endif - if((state >= 2) || UPNPIGD_IsConnected(urls, data)) - return state; - FreeUPNPUrls(urls); - } - } - memset(data, 0, sizeof(struct IGDdatas)); - } -#ifdef DEBUG - else - { - printf("error getting XML description %s\n", dev->descURL); - } -#endif - } - } - return 0; -} - -/* UPNP_GetIGDFromUrl() - * Used when skipping the discovery process. - * return value : - * 0 - Not ok - * 1 - OK */ -int -UPNP_GetIGDFromUrl(const char * rootdescurl, - struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen) -{ - char * descXML; - int descXMLsize = 0; - descXML = miniwget_getaddr(rootdescurl, &descXMLsize, - lanaddr, lanaddrlen); - if(descXML) { - memset(data, 0, sizeof(struct IGDdatas)); - memset(urls, 0, sizeof(struct UPNPUrls)); - parserootdesc(descXML, descXMLsize, data); - free(descXML); - descXML = NULL; - GetUPNPUrls(urls, data, rootdescurl); - return 1; - } else { - return 0; - } -} - diff --git a/libs/miniupnpc/miniupnpc.def b/libs/miniupnpc/miniupnpc.def index 10b9f5800..baa841f20 100644 --- a/libs/miniupnpc/miniupnpc.def +++ b/libs/miniupnpc/miniupnpc.def @@ -1,9 +1,13 @@ LIBRARY ; miniupnpc library + miniupnpc EXPORTS ; miniupnpc upnpDiscover + upnpDiscoverDevice + upnpDiscoverDevices + upnpDiscoverAll freeUPNPDevlist parserootdesc UPNP_GetValidIGD @@ -23,7 +27,9 @@ EXPORTS UPNP_GetExternalIPAddress UPNP_GetLinkLayerMaxBitRates UPNP_AddPortMapping + UPNP_AddAnyPortMapping UPNP_DeletePortMapping + UPNP_DeletePortMappingRange UPNP_GetPortMappingNumberOfEntries UPNP_GetSpecificPortMappingEntry UPNP_GetGenericPortMappingEntry diff --git a/libs/miniupnpc/miniupnpc.pc.in b/libs/miniupnpc/miniupnpc.pc.in new file mode 100644 index 000000000..3d33cbdb0 --- /dev/null +++ b/libs/miniupnpc/miniupnpc.pc.in @@ -0,0 +1,18 @@ +# this template is filled-in by CMake `configure_file(... @ONLY)` +# the `@....@` are filled in by CMake configure_file(), +# from variables set in your CMakeLists.txt or by CMake itself +# +# Good tutoral for understanding .pc files: +# https://people.freedesktop.org/~dbn/pkg-config-guide.html + +prefix="@CMAKE_INSTALL_PREFIX@" +exec_prefix="${prefix}" +libdir="${prefix}/lib" +includedir="${prefix}/include" + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +URL: @PROJECT_HOMEPAGE_URL@ +Version: @PROJECT_VERSION@ +Libs: -L"${libdir}" -lminiupnpc +Cflags: -I"${includedir}" diff --git a/libs/miniupnpc/miniupnpc.rc b/libs/miniupnpc/miniupnpc.rc new file mode 100644 index 000000000..92954afda --- /dev/null +++ b/libs/miniupnpc/miniupnpc.rc @@ -0,0 +1,36 @@ +#include +#include "rc_version.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION LIBMINIUPNPC_MAJOR_VERSION,LIBMINIUPNPC_MINOR_VERSION,LIBMINIUPNPC_MICRO_VERSION,0 + PRODUCTVERSION LIBMINIUPNPC_MAJOR_VERSION,LIBMINIUPNPC_MINOR_VERSION,LIBMINIUPNPC_MICRO_VERSION,0 + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0 +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE VFT2_UNKNOWN // not used +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" /* Lang = US English, Charset = ASCII */ + BEGIN + VALUE "FileDescription", "MiniUPnPc library\0" + VALUE "FileVersion", LIBMINIUPNPC_DOTTED_VERSION "\0" + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", "Copyright (C) Thomas Bernard\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", INTERNAL_NAME + VALUE "ProductName", "MiniUPnPc\0" + VALUE "ProductVersion", LIBMINIUPNPC_DOTTED_VERSION "\0" + VALUE "Comments", "For more information visit https://miniupnp.tuxfamil.org/\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0 /* US English, ASCII */ + END +END diff --git a/libs/miniupnpc/miniupnpcstrings.h.cmake b/libs/miniupnpc/miniupnpcstrings.h.cmake index a3726350c..78c8fe9c5 100644 --- a/libs/miniupnpc/miniupnpcstrings.h.cmake +++ b/libs/miniupnpc/miniupnpcstrings.h.cmake @@ -1,7 +1,15 @@ -#ifndef __MINIUPNPCSTRINGS_H__ -#define __MINIUPNPCSTRINGS_H__ +#ifndef MINIUPNPCSTRINGS_H_INCLUDED +#define MINIUPNPCSTRINGS_H_INCLUDED #define OS_STRING "${CMAKE_SYSTEM_NAME}" #define MINIUPNPC_VERSION_STRING "${MINIUPNPC_VERSION}" +#if 0 +/* according to "UPnP Device Architecture 1.0" */ +#define UPNP_VERSION_STRING "UPnP/1.0" +#else +/* according to "UPnP Device Architecture 1.1" */ +#define UPNP_VERSION_STRING "UPnP/1.1" +#endif + #endif diff --git a/libs/miniupnpc/miniupnpcstrings.h.in b/libs/miniupnpc/miniupnpcstrings.h.in index 201c9a862..68bf4293d 100644 --- a/libs/miniupnpc/miniupnpcstrings.h.in +++ b/libs/miniupnpc/miniupnpcstrings.h.in @@ -1,15 +1,23 @@ -/* $Id: miniupnpcstrings.h.in,v 1.4 2011/01/04 11:41:53 nanard Exp $ */ +/* $Id: miniupnpcstrings.h.in,v 1.6 2014/11/04 22:31:55 nanard Exp $ */ /* Project: miniupnp * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author: Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2014 Thomas Bernard * This software is subjects to the conditions detailed * in the LICENCE file provided within this distribution */ -#ifndef __MINIUPNPCSTRINGS_H__ -#define __MINIUPNPCSTRINGS_H__ +#ifndef MINIUPNPCSTRINGS_H_INCLUDED +#define MINIUPNPCSTRINGS_H_INCLUDED #define OS_STRING "OS/version" #define MINIUPNPC_VERSION_STRING "version" +#if 0 +/* according to "UPnP Device Architecture 1.0" */ +#define UPNP_VERSION_STRING "UPnP/1.0" +#else +/* according to "UPnP Device Architecture 1.1" */ +#define UPNP_VERSION_STRING "UPnP/1.1" +#endif + #endif diff --git a/libs/miniupnpc/miniwget.h b/libs/miniupnpc/miniwget.h deleted file mode 100644 index 8314b4000..000000000 --- a/libs/miniupnpc/miniwget.h +++ /dev/null @@ -1,30 +0,0 @@ -/* $Id: miniwget.h,v 1.6 2010/12/09 16:11:33 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * */ -#ifndef __MINIWGET_H__ -#define __MINIWGET_H__ - -#include "declspec.h" - -#ifdef __cplusplus -extern "C" { -#endif - -LIBSPEC void * getHTTPResponse(int s, int * size); - -LIBSPEC void * miniwget(const char *, int *); - -LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int); - -int parseURL(const char *, char *, unsigned short *, char * *); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/libs/miniupnpc/msvc/genminiupnpcstrings.vbs b/libs/miniupnpc/msvc/genminiupnpcstrings.vbs new file mode 100644 index 000000000..415b2ee6b --- /dev/null +++ b/libs/miniupnpc/msvc/genminiupnpcstrings.vbs @@ -0,0 +1,112 @@ +' VBScript to generate miniupnpcstrings.h +' Copyright 2018 Thomas Bernard +'Set WshShell = CreateObject("WScript.Shell") +Set FSO = CreateObject("Scripting.FileSystemObject") +versionfile = "..\version" +infile = "..\miniupnpcstrings.h.in" +outfile = "..\miniupnpcstrings.h" +outfilerc = "..\rc_version.h" + +On Error Resume Next + +'Wscript.Echo revision + +Err.Clear +Set f = FSO.OpenTextFile(versionfile, 1, False) ' 1 = Read +If Err.Number = 0 Then + version = f.ReadLine + f.Close +Else + ' Exit error + WScript.Quit 1 +End If + +os_version = "0.0.0" +strComputer = "." +Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") +Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem") +For Each objOperatingSystem in colOperatingSystems + 'Wscript.Echo objOperatingSystem.Caption & " -- " + os_version = objOperatingSystem.Version +Next + +'Wscript.Echo os_version + +Dim array +needWrite = True + +' First Check if the file already contains the right versions +Err.Clear +Set f_in = FSO.OpenTextFile(outfile, 1, False) +If Err.Number = 0 Then + old_version = "" + old_os_version = "" + Do Until f_in.AtEndOfStream + line = f_in.ReadLine + If Len(line) > 0 Then + array = Split(line, " ") + If UBound(array) >= 2 And array(0) = "#define" Then + If array(1) = "OS_STRING" Then + old_os_version = Replace(array(2), Chr(34), "") + ElseIf array(1) = "MINIUPNPC_VERSION_STRING" Then + old_version = Replace(array(2), Chr(34), "") + End if + End if + End If + Loop + f_in.Close + If old_version = version And old_os_version = "MSWindows/" & os_version Then + needWrite = False + Else + needWrite = True + End If +End If + +If Not needWrite Then + ' check files dates + Set fIn1 = FSO.GetFile(versionfile) + Set fIn2 = FSO.GetFile(infile) + Set fOut = FSO.GetFile(outfile) + If DateDiff("s", fIn1.DateLastModified, fOut.DateLastModified) < 0 Then + needWrite = True + End If + If DateDiff("s", fIn2.DateLastModified, fOut.DateLastModified) < 0 Then + needWrite = True + End If +End If + +If Not needWrite Then + ' nothing to do + WScript.Quit 0 +End if + +' generate the file +Err.Clear +Set f_in = FSO.OpenTextFile(infile, 1, False) +If Err.Number = 0 Then + Set f_out = FSO.OpenTextFile(outfile, 2, True) ' 2 = Write + Do Until f_in.AtEndOfStream + line = f_in.ReadLine + If Len(line) > 0 Then + array = Split(line, " ") + If UBound(array) >= 2 And array(0) = "#define" Then + If array(1) = "OS_STRING" Then + line = "#define OS_STRING " & Chr(34) & "MSWindows/" & os_version & Chr(34) + ElseIf array(1) = "MINIUPNPC_VERSION_STRING" Then + line = "#define MINIUPNPC_VERSION_STRING " & Chr(34) & version & Chr(34) + End if + End if + End If + f_out.WriteLine line + Loop + f_in.Close + f_out.Close +End If + +Set f_out = FSO.OpenTextFile(outfilerc, 2, True) ' 2 = Write +f_out.WriteLine "#define LIBMINIUPNPC_DOTTED_VERSION " & Chr(34) & version & Chr(34) +ver = Split(version, ".") +f_out.WriteLine "#define LIBMINIUPNPC_MAJOR_VERSION " & ver(0) +f_out.WriteLine "#define LIBMINIUPNPC_MINOR_VERSION " & ver(1) +f_out.WriteLine "#define LIBMINIUPNPC_MICRO_VERSION " & ver(2) +f_out.Close diff --git a/libs/miniupnpc/msvc/miniupnpc.vcproj b/libs/miniupnpc/msvc/miniupnpc.vcproj index cce2de576..90646c53f 100644 --- a/libs/miniupnpc/msvc/miniupnpc.vcproj +++ b/libs/miniupnpc/msvc/miniupnpc.vcproj @@ -41,7 +41,7 @@ + + @@ -184,6 +188,10 @@ RelativePath="..\upnpcommands.c" > + + @@ -198,10 +206,6 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > - - @@ -218,6 +222,10 @@ RelativePath="..\minisoap.h" > + + @@ -250,6 +258,10 @@ RelativePath="..\upnpcommands.h" > + + diff --git a/libs/miniupnpc/msvc/miniupnpc.vcxproj b/libs/miniupnpc/msvc/miniupnpc.vcxproj new file mode 100644 index 000000000..24c24ea08 --- /dev/null +++ b/libs/miniupnpc/msvc/miniupnpc.vcxproj @@ -0,0 +1,215 @@ +ï»ż + + + + Debug Dll + Win32 + + + Debug + Win32 + + + Release Dll + Win32 + + + Release + Win32 + + + + {D28CE435-CB33-4BAE-8A52-C6EF915956F5} + miniupnpc + Win32Proj + + + + StaticLibrary + v140 + Unicode + true + + + DynamicLibrary + v140 + Unicode + true + + + StaticLibrary + v140 + Unicode + + + DynamicLibrary + v140 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.25123.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + Disabled + _CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies) + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + _CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies) + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/miniupnpc/msvc/miniupnpc.vcxproj.filters b/libs/miniupnpc/msvc/miniupnpc.vcxproj.filters new file mode 100644 index 000000000..245bc4c24 --- /dev/null +++ b/libs/miniupnpc/msvc/miniupnpc.vcxproj.filters @@ -0,0 +1,133 @@ +ï»ż + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {508da401-2f8e-4fdb-9a43-95baa95a91e7} + + + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + Fichiers sources + + + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + + + scripts + + + + + Fichiers de ressources + + + \ No newline at end of file diff --git a/libs/miniupnpc/msvc/miniupnpc_vs2010.sln b/libs/miniupnpc/msvc/miniupnpc_vs2010.sln new file mode 100644 index 000000000..2fdfe91ab --- /dev/null +++ b/libs/miniupnpc/msvc/miniupnpc_vs2010.sln @@ -0,0 +1,36 @@ +ï»ż +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc_vs2010", "miniupnpc_vs2010.vcxproj", "{D28CE435-CB33-4BAE-8A52-C6EF915956F5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "upnpc-static_vs2010", "upnpc-static_vs2010.vcxproj", "{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Dll|Win32 = Debug Dll|Win32 + Debug|Win32 = Debug|Win32 + Release Dll|Win32 = Release Dll|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.Build.0 = Debug Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.ActiveCfg = Debug|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.Build.0 = Debug|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.ActiveCfg = Release Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.Build.0 = Release Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.ActiveCfg = Release|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.Build.0 = Release|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.Build.0 = Debug Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.ActiveCfg = Debug|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.Build.0 = Debug|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.ActiveCfg = Release Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.Build.0 = Release Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.ActiveCfg = Release|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libs/miniupnpc/msvc/miniupnpc_vs2010.vcxproj b/libs/miniupnpc/msvc/miniupnpc_vs2010.vcxproj new file mode 100644 index 000000000..e5433fb36 --- /dev/null +++ b/libs/miniupnpc/msvc/miniupnpc_vs2010.vcxproj @@ -0,0 +1,209 @@ +ï»ż + + + + Debug Dll + Win32 + + + Debug + Win32 + + + Release Dll + Win32 + + + Release + Win32 + + + + {D28CE435-CB33-4BAE-8A52-C6EF915956F5} + miniupnpc + Win32Proj + + + + StaticLibrary + Unicode + true + + + DynamicLibrary + Unicode + true + + + StaticLibrary + Unicode + + + DynamicLibrary + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + miniupnpc + miniupnpc + miniupnpc + miniupnpc + + + + Disabled + _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + Disabled + _CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies) + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + _CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies) + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/miniupnpc/msvc/miniupnpc_vs2010.vcxproj.filters b/libs/miniupnpc/msvc/miniupnpc_vs2010.vcxproj.filters new file mode 100644 index 000000000..deb8688d4 --- /dev/null +++ b/libs/miniupnpc/msvc/miniupnpc_vs2010.vcxproj.filters @@ -0,0 +1,133 @@ +ï»ż + + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + sources + + + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + headers + + + Fichiers d%27en-tĂȘte + + + Fichiers d%27en-tĂȘte + + + + + {f2cbd46b-f63f-412e-80e5-b7c9048a1add} + + + {2b3996de-1bc4-418b-8a83-a5f34fdf0df5} + + + {45dbc39d-c3ca-4a75-adf0-76070e20415a} + + + + + scripts + + + + + Fichiers de ressources + + + diff --git a/libs/miniupnpc/msvc/miniupnpc_vs2015.sln b/libs/miniupnpc/msvc/miniupnpc_vs2015.sln new file mode 100644 index 000000000..76318f69c --- /dev/null +++ b/libs/miniupnpc/msvc/miniupnpc_vs2015.sln @@ -0,0 +1,38 @@ +ï»ż +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc", "miniupnpc.vcxproj", "{D28CE435-CB33-4BAE-8A52-C6EF915956F5}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "upnpc-static", "upnpc-static.vcxproj", "{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug Dll|Win32 = Debug Dll|Win32 + Debug|Win32 = Debug|Win32 + Release Dll|Win32 = Release Dll|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug Dll|Win32.Build.0 = Debug Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.ActiveCfg = Debug|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.Build.0 = Debug|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.ActiveCfg = Release Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release Dll|Win32.Build.0 = Release Dll|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.ActiveCfg = Release|Win32 + {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.Build.0 = Release|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.ActiveCfg = Debug Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug Dll|Win32.Build.0 = Debug Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.ActiveCfg = Debug|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.Build.0 = Debug|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.ActiveCfg = Release Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release Dll|Win32.Build.0 = Release Dll|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.ActiveCfg = Release|Win32 + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libs/miniupnpc/msvc/upnpc-static.vcproj b/libs/miniupnpc/msvc/upnpc-static.vcproj index 37fbbca27..f2246f57b 100644 --- a/libs/miniupnpc/msvc/upnpc-static.vcproj +++ b/libs/miniupnpc/msvc/upnpc-static.vcproj @@ -41,7 +41,7 @@ + + + + Debug Dll + Win32 + + + Debug + Win32 + + + Release Dll + Win32 + + + Release + Win32 + + + + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1} + upnpcstatic + Win32Proj + + + + Application + v140 + Unicode + true + + + Application + v140 + Unicode + true + + + Application + v140 + Unicode + + + Application + v140 + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.25123.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + upnpc-shared + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + upnpc-shared + + + + Disabled + _DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + ..;..\include + + + ws2_32.lib;$(Configuration)\miniupnpc.lib;IPHlpApi.Lib;%(AdditionalDependencies) + true + Console + MachineX86 + + + + + Disabled + _DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + ..;..\include + + + ws2_32.lib;$(Configuration)\miniupnpc.lib;$(AdditionalDependencies) + true + Console + MachineX86 + + + + + MaxSpeed + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + ..;..\include + + + ws2_32.lib;$(Configuration)\miniupnpc.lib;IPHlpApi.Lib;%(AdditionalDependencies) + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..;..\include + + + ws2_32.lib;$(Configuration)\miniupnpc.lib;IPHlpApi.Lib;%(AdditionalDependencies) + true + Console + true + true + MachineX86 + + + + + + + + {d28ce435-cb33-4bae-8a52-c6ef915956f5} + false + + + + + + \ No newline at end of file diff --git a/libs/miniupnpc/msvc/upnpc-static.vcxproj.filters b/libs/miniupnpc/msvc/upnpc-static.vcxproj.filters new file mode 100644 index 000000000..a67f6d317 --- /dev/null +++ b/libs/miniupnpc/msvc/upnpc-static.vcxproj.filters @@ -0,0 +1,22 @@ +ï»ż + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Fichiers sources + + + \ No newline at end of file diff --git a/libs/miniupnpc/msvc/upnpc-static_vs2010.vcxproj b/libs/miniupnpc/msvc/upnpc-static_vs2010.vcxproj new file mode 100644 index 000000000..d97fb5887 --- /dev/null +++ b/libs/miniupnpc/msvc/upnpc-static_vs2010.vcxproj @@ -0,0 +1,176 @@ +ï»ż + + + + Debug Dll + Win32 + + + Debug + Win32 + + + Release Dll + Win32 + + + Release + Win32 + + + + {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1} + upnpcstatic + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + true + + + Application + Unicode + + + Application + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + true + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + false + false + upnpc-static + upnpc-static + upnpc-static + upnpc-static + + + + Disabled + _DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + ..;..\include + + + ws2_32.lib;IPHlpApi.Lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies) + true + Console + MachineX86 + + + + + Disabled + _DEBUG;_CONSOLE;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + ..;..\include + + + ws2_32.lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies) + true + Console + MachineX86 + + + + + MaxSpeed + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..;..\include + + + ws2_32.lib;IPHlpApi.Lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies) + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..;..\include + + + ws2_32.lib;$(Configuration)\miniupnpc.lib;%(AdditionalDependencies) + true + Console + true + true + MachineX86 + + + + + + + + {d28ce435-cb33-4bae-8a52-c6ef915956f5} + false + + + + + + \ No newline at end of file diff --git a/libs/miniupnpc/pymoduletest.py b/libs/miniupnpc/pymoduletest.py index d35a3b092..0bda1bf2e 100644 --- a/libs/miniupnpc/pymoduletest.py +++ b/libs/miniupnpc/pymoduletest.py @@ -1,35 +1,70 @@ -#! /usr/bin/python +#! /usr/bin/env python +# vim: tabstop=2 shiftwidth=2 expandtab # MiniUPnP project # Author : Thomas Bernard +# Python 3 # This Sample code is public domain. -# website : http://miniupnp.tuxfamily.org/ +# website : https://miniupnp.tuxfamily.org/ # import the python miniupnpc module import miniupnpc import sys -# create the object -u = miniupnpc.UPnP() -print 'inital(default) values :' -print ' discoverdelay', u.discoverdelay -print ' lanaddr', u.lanaddr -print ' multicastif', u.multicastif -print ' minissdpdsocket', u.minissdpdsocket -u.discoverdelay = 200; +try: + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('-m', '--multicastif') + parser.add_argument('-p', '--minissdpdsocket') + parser.add_argument('-d', '--discoverdelay', type=int, default=200) + parser.add_argument('-z', '--localport', type=int, default=0) + # create the object + u = miniupnpc.UPnP(**vars(parser.parse_args())) +except: + print('argparse not available') + i = 1 + multicastif = None + minissdpdsocket = None + discoverdelay = 200 + localport = 0 + while i < len(sys.argv): + print(sys.argv[i]) + if sys.argv[i] == '-m' or sys.argv[i] == '--multicastif': + multicastif = sys.argv[i+1] + elif sys.argv[i] == '-p' or sys.argv[i] == '--minissdpdsocket': + minissdpdsocket = sys.argv[i+1] + elif sys.argv[i] == '-d' or sys.argv[i] == '--discoverdelay': + discoverdelay = int(sys.argv[i+1]) + elif sys.argv[i] == '-z' or sys.argv[i] == '--localport': + localport = int(sys.argv[i+1]) + else: + raise Exception('invalid argument %s' % sys.argv[i]) + i += 2 + # create the object + u = miniupnpc.UPnP(multicastif, minissdpdsocket, discoverdelay, localport) + +print('inital(default) values :') +print(' discoverdelay', u.discoverdelay) +print(' lanaddr', u.lanaddr) +print(' multicastif', u.multicastif) +print(' minissdpdsocket', u.minissdpdsocket) #u.minissdpdsocket = '../minissdpd/minissdpd.sock' -# discovery process, it usualy takes several seconds (2 seconds or more) -print 'Discovering... delay=%ums' % u.discoverdelay -print u.discover(), 'device(s) detected' +# discovery process, it usually takes several seconds (2 seconds or more) +print('Discovering... delay=%ums' % u.discoverdelay) +print('%d device(s) detected' % u.discover()) # select an igd try: u.selectigd() -except Exception, e: - print 'Exception :', e +except Exception as e: + print('Exception :', e) sys.exit(1) +# it is also possible to pass the root description URL to u.selectigd() : +# u.selectigd('http://192.168.1.254:5678/desc/root') # display information about the IGD and the internet connection -print 'local ip address :', u.lanaddr -print 'external ip address :', u.externalipaddress() -print u.statusinfo(), u.connectiontype() +print('local ip address :', u.lanaddr) +print('external ip address :', u.externalipaddress()) +print( u.statusinfo(), u.connectiontype()) +print('total bytes : sent', u.totalbytesent(), 'received', u.totalbytereceived()) +print('total packets : sent', u.totalpacketsent(), 'received', u.totalpacketreceived()) #print u.addportmapping(64000, 'TCP', # '192.168.1.166', 63000, 'port mapping test', '') @@ -43,10 +78,14 @@ while True: p = u.getgenericportmapping(i) if p==None: break - print i, p + print(i, p) (port, proto, (ihost,iport), desc, c, d, e) = p #print port, desc i = i + 1 -print u.getspecificportmapping(port, proto) +print(u.getspecificportmapping(port, proto)) +try: + print(u.getportmappingnumberofentries()) +except Exception as e: + print('GetPortMappingNumberOfEntries() is not supported :', e) diff --git a/libs/miniupnpc/receivedata.c b/libs/miniupnpc/receivedata.c deleted file mode 100644 index a1eadfc46..000000000 --- a/libs/miniupnpc/receivedata.c +++ /dev/null @@ -1,81 +0,0 @@ -/* $Id: receivedata.c,v 1.1 2011/04/11 08:21:47 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas Bernard - * Copyright (c) 2011 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. */ - -#include -#ifdef WIN32 -#include -#include -#else -#include -#if defined(__amigaos__) && !defined(__amigaos4__) -#define socklen_t int -#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */ -#include -#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */ -#include -#if !defined(__amigaos__) && !defined(__amigaos4__) -#include -#endif -#include -#define MINIUPNPC_IGNORE_EINTR -#endif - -#ifdef WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif - -#include "receivedata.h" - -int -receivedata(int socket, char * data, int length, int timeout) -{ - int n; -#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) - /* using poll */ - struct pollfd fds[1]; /* for the poll */ -#ifdef MINIUPNPC_IGNORE_EINTR - do { -#endif - fds[0].fd = socket; - fds[0].events = POLLIN; - n = poll(fds, 1, timeout); -#ifdef MINIUPNPC_IGNORE_EINTR - } while(n < 0 && errno == EINTR); -#endif - if(n < 0) { - PRINT_SOCKET_ERROR("poll"); - return -1; - } else if(n == 0) { - /* timeout */ - return 0; - } -#else /* !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ - /* using select under WIN32 and amigaos */ - fd_set socketSet; - TIMEVAL timeval; - FD_ZERO(&socketSet); - FD_SET(socket, &socketSet); - timeval.tv_sec = timeout / 1000; - timeval.tv_usec = (timeout % 1000) * 1000; - n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval); - if(n < 0) { - PRINT_SOCKET_ERROR("select"); - return -1; - } else if(n == 0) { - return 0; - } -#endif - n = recv(socket, data, length, 0); - if(n<0) { - PRINT_SOCKET_ERROR("recv"); - } - return n; -} - - diff --git a/libs/miniupnpc/receivedata.h b/libs/miniupnpc/receivedata.h deleted file mode 100644 index 7a551b9ac..000000000 --- a/libs/miniupnpc/receivedata.h +++ /dev/null @@ -1,17 +0,0 @@ -/* $Id: receivedata.h,v 1.1 2011/04/11 08:21:47 nanard Exp $ */ -/* Project: miniupnp - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * Author: Thomas Bernard - * Copyright (c) 2011 Thomas Bernard - * This software is subjects to the conditions detailed - * in the LICENCE file provided within this distribution */ -#ifndef __RECEIVEDATA_H__ -#define __RECEIVEDATA_H__ - -/* Reads data from the specified socket. - * Returns the number of bytes read if successful, zero if no bytes were - * read or if we timed out. Returns negative if there was an error. */ -int receivedata(int socket, char * data, int length, int timeout); - -#endif - diff --git a/libs/miniupnpc/setup.py b/libs/miniupnpc/setup.py index ca31f9615..d54f924b5 100644 --- a/libs/miniupnpc/setup.py +++ b/libs/miniupnpc/setup.py @@ -1,15 +1,35 @@ -#! /usr/bin/python -# $Id: setup.py,v 1.6 2011/01/04 09:46:08 nanard Exp $ -# the MiniUPnP Project (c) 2007-2011 Thomas Bernard -# http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ +#! /usr/bin/env python +# vim: tabstop=8 shiftwidth=8 expandtab +# $Id: setup.py,v 1.15 2021/09/28 21:10:11 nanard Exp $ +# the MiniUPnP Project (c) 2007-2021 Thomas Bernard +# https://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ # # python script to build the miniupnpc module under unix # -# replace libminiupnpc.a by libminiupnpc.so for shared library usage -from distutils.core import setup, Extension -setup(name="miniupnpc", version="1.5", - ext_modules=[ - Extension(name="miniupnpc", sources=["miniupnpcmodule.c"], - extra_objects=["libminiupnpc.a"]) - ]) +# Uses MAKE environment variable (defaulting to 'make') + +from setuptools import setup, Extension +from setuptools.command import build_ext +import subprocess +import os + +EXT = ['build/libminiupnpc.a'] + +class make_then_build_ext(build_ext.build_ext): + def run(self): + subprocess.check_call([os.environ.get('MAKE', 'make')] + EXT) + build_ext.build_ext.run(self) + +setup(name="miniupnpc", + version=open('VERSION').read().strip(), + author='Thomas BERNARD', + author_email='miniupnp@free.fr', + license=open('LICENSE').read(), + url='http://miniupnp.free.fr/', + description='miniUPnP client', + cmdclass={'build_ext': make_then_build_ext}, + ext_modules=[ + Extension(name="miniupnpc", sources=["src/miniupnpcmodule.c"], + include_dirs=['include'], extra_objects=EXT) + ]) diff --git a/libs/miniupnpc/setupmingw32.py b/libs/miniupnpc/setupmingw32.py index d0539e450..26e85f037 100644 --- a/libs/miniupnpc/setupmingw32.py +++ b/libs/miniupnpc/setupmingw32.py @@ -1,15 +1,35 @@ -#! /usr/bin/python -# $Id: setupmingw32.py,v 1.5 2011/05/15 21:18:43 nanard Exp $ -# the MiniUPnP Project (c) 2007-2011 Thomas Bernard -# http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ +#! /usr/bin/env python +# vim: tabstop=8 shiftwidth=8 expandtab +# $Id: setupmingw32.py,v 1.14 2021/09/28 21:10:11 nanard Exp $ +# the MiniUPnP Project (c) 2007-2021 Thomas Bernard +# https://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ # # python script to build the miniupnpc module under windows (using mingw32) # -from distutils.core import setup, Extension -setup(name="miniupnpc", version="1.5", - ext_modules=[ - Extension(name="miniupnpc", sources=["miniupnpcmodule.c"], - libraries=["ws2_32", "iphlpapi"], - extra_objects=["libminiupnpc.a"]) - ]) +import sys + +if (sys.version_info.major * 10 + sys.version_info.minor) >= 35: + compat_lib = ["legacy_stdio_definitions"] +else: + compat_lib = [] + +try: + from setuptools import setup, Extension +except ImportError: + from distutils.core import setup, Extension +from distutils import sysconfig +sysconfig.get_config_vars()["OPT"] = '' +sysconfig.get_config_vars()["CFLAGS"] = '' +setup(name="miniupnpc", + version=open('VERSION').read().strip(), + author='Thomas BERNARD', + author_email='miniupnp@free.fr', + license=open('LICENSE').read(), + url='http://miniupnp.free.fr/', + description='miniUPnP client', + ext_modules=[ + Extension(name="miniupnpc", sources=["src/miniupnpcmodule.c"], + libraries=["ws2_32", "iphlpapi"] + compat_lib, + include_dirs=['include'], extra_objects=["miniupnpc.lib"]) + ]) diff --git a/libs/miniupnpc/src/addr_is_reserved.c b/libs/miniupnpc/src/addr_is_reserved.c new file mode 100644 index 000000000..bd721c17c --- /dev/null +++ b/libs/miniupnpc/src/addr_is_reserved.c @@ -0,0 +1,79 @@ +/* $Id: addr_is_reserved.c,v 1.5 2021/05/10 20:53:02 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas BERNARD + * copyright (c) 2005-2021 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#ifdef _WIN32 +/* Win32 Specific includes and defines */ +#include +#include +#if !defined(_MSC_VER) +#include +#else /* !defined(_MSC_VER) */ +typedef unsigned long uint32_t; +#endif /* !defined(_MSC_VER) */ +#else /* _WIN32 */ +#include +#include +#include +#include +#endif /* _WIN32 */ + +/* List of IP address blocks which are private / reserved and therefore not suitable for public external IP addresses */ +#define IP(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) +#define MSK(m) (32-(m)) +static const struct { uint32_t address; uint32_t rmask; } reserved[] = { + { IP( 0, 0, 0, 0), MSK( 8) }, /* RFC1122 "This host on this network" */ + { IP( 10, 0, 0, 0), MSK( 8) }, /* RFC1918 Private-Use */ + { IP(100, 64, 0, 0), MSK(10) }, /* RFC6598 Shared Address Space */ + { IP(127, 0, 0, 0), MSK( 8) }, /* RFC1122 Loopback */ + { IP(169, 254, 0, 0), MSK(16) }, /* RFC3927 Link-Local */ + { IP(172, 16, 0, 0), MSK(12) }, /* RFC1918 Private-Use */ + { IP(192, 0, 0, 0), MSK(24) }, /* RFC6890 IETF Protocol Assignments */ + { IP(192, 0, 2, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-1) */ + { IP(192, 31, 196, 0), MSK(24) }, /* RFC7535 AS112-v4 */ + { IP(192, 52, 193, 0), MSK(24) }, /* RFC7450 AMT */ + { IP(192, 88, 99, 0), MSK(24) }, /* RFC7526 6to4 Relay Anycast */ + { IP(192, 168, 0, 0), MSK(16) }, /* RFC1918 Private-Use */ + { IP(192, 175, 48, 0), MSK(24) }, /* RFC7534 Direct Delegation AS112 Service */ + { IP(198, 18, 0, 0), MSK(15) }, /* RFC2544 Benchmarking */ + { IP(198, 51, 100, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-2) */ + { IP(203, 0, 113, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-3) */ + { IP(224, 0, 0, 0), MSK( 4) }, /* RFC1112 Multicast */ + { IP(240, 0, 0, 0), MSK( 4) }, /* RFC1112 Reserved for Future Use + RFC919 Limited Broadcast */ +}; +#undef IP +#undef MSK + +/** + * @return 1 or 0 + */ +int addr_is_reserved(const char * addr_str) +{ + uint32_t addr_n, address; + size_t i; + +#if defined(_WIN32) && _WIN32_WINNT < 0x0600 // _WIN32_WINNT_VISTA + addr_n = inet_addr(addr_str); + if (addr_n == INADDR_NONE) + return 1; +#else + /* was : addr_n = inet_addr(addr_str); */ + if (inet_pton(AF_INET, addr_str, &addr_n) <= 0) { + /* error */ + return 1; + } +#endif + + address = ntohl(addr_n); + + for (i = 0; i < sizeof(reserved)/sizeof(reserved[0]); ++i) { + if ((address >> reserved[i].rmask) == (reserved[i].address >> reserved[i].rmask)) + return 1; + } + + return 0; +} diff --git a/libs/miniupnpc/src/addr_is_reserved.h b/libs/miniupnpc/src/addr_is_reserved.h new file mode 100644 index 000000000..3286bc4c8 --- /dev/null +++ b/libs/miniupnpc/src/addr_is_reserved.h @@ -0,0 +1,14 @@ +/* $Id: addr_is_reserved.h,v 1.1 2020/09/28 21:11:19 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2020 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef ADDR_IS_RESERVED_H_INCLUDED +#define ADDR_IS_RESERVED_H_INCLUDED + +int addr_is_reserved(const char * addr_str); + +#endif /* ADDR_IS_RESERVED_H_INCLUDED */ diff --git a/libs/miniupnpc/src/codelength.h b/libs/miniupnpc/src/codelength.h new file mode 100644 index 000000000..f5f8e30f9 --- /dev/null +++ b/libs/miniupnpc/src/codelength.h @@ -0,0 +1,54 @@ +/* $Id: codelength.h,v 1.5 2015/07/09 12:40:18 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas BERNARD + * copyright (c) 2005-2015 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#ifndef CODELENGTH_H_INCLUDED +#define CODELENGTH_H_INCLUDED + +/* Encode length by using 7bit per Byte : + * Most significant bit of each byte specifies that the + * following byte is part of the code */ + +/* n : unsigned + * p : unsigned char * + */ +#define DECODELENGTH(n, p) n = 0; \ + do { n = (n << 7) | (*p & 0x7f); } \ + while((*(p++)&0x80) && (n<(1<<25))); + +/* n : unsigned + * READ : function/macro to read one byte (unsigned char) + */ +#define DECODELENGTH_READ(n, READ) \ + n = 0; \ + do { \ + unsigned char c; \ + READ(c); \ + n = (n << 7) | (c & 0x07f); \ + if(!(c&0x80)) break; \ + } while(n<(1<<25)); + +/* n : unsigned + * p : unsigned char * + * p_limit : unsigned char * + */ +#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \ + n = 0; \ + do { \ + if((p) >= (p_limit)) break; \ + n = (n << 7) | (*(p) & 0x7f); \ + } while((*((p)++)&0x80) && (n<(1<<25))); + + +/* n : unsigned + * p : unsigned char * + */ +#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ + if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ + if(n>=16384) *(p++) = (n >> 14) | 0x80; \ + if(n>=128) *(p++) = (n >> 7) | 0x80; \ + *(p++) = n & 0x7f; + +#endif /* CODELENGTH_H_INCLUDED */ diff --git a/libs/miniupnpc/connecthostport.c b/libs/miniupnpc/src/connecthostport.c similarity index 62% rename from libs/miniupnpc/connecthostport.c rename to libs/miniupnpc/src/connecthostport.c index 76e8e374b..79f832b8d 100644 --- a/libs/miniupnpc/connecthostport.c +++ b/libs/miniupnpc/src/connecthostport.c @@ -1,7 +1,8 @@ -/* $Id: connecthostport.c,v 1.5 2011/04/09 08:49:50 nanard Exp $ */ -/* Project : miniupnp +/* $Id: connecthostport.c,v 1.24 2020/11/09 19:26:53 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2010-2011 Thomas Bernard + * Copyright (c) 2010-2020 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -13,35 +14,32 @@ #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #include #include #define MAXHOSTNAMELEN 64 -#define snprintf _snprintf +#include "win32_snprintf.h" #define herror #define socklen_t int -#else /* #ifdef WIN32 */ +#else /* #ifdef _WIN32 */ #include +#include +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT +#include +#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ #include +#include #include #define closesocket close #include +#include /* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions * during the connect() call */ #define MINIUPNPC_IGNORE_EINTR -#ifndef USE_GETHOSTBYNAME -#include #include -#endif /* #ifndef USE_GETHOSTBYNAME */ -#endif /* #else WIN32 */ - -/* definition of PRINT_SOCKET_ERROR */ -#ifdef WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif +#include +#endif /* #else _WIN32 */ #if defined(__amigaos__) || defined(__amigaos4__) #define herror(A) printf("%s\n", A) @@ -49,12 +47,18 @@ #include "connecthostport.h" +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + /* connecthostport() * return a socket connected (TCP) to the host and port * or -1 in case of error */ -int connecthostport(const char * host, unsigned short port) +SOCKET connecthostport(const char * host, unsigned short port, + unsigned int scope_id) { - int s, n; + SOCKET s; + int n; #ifdef USE_GETHOSTBYNAME struct sockaddr_in dest; struct hostent *hp; @@ -67,21 +71,21 @@ int connecthostport(const char * host, unsigned short port) #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT struct timeval timeout; #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ - + #ifdef USE_GETHOSTBYNAME hp = gethostbyname(host); if(hp == NULL) { herror(host); - return -1; + return INVALID_SOCKET; } memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr)); memset(dest.sin_zero, 0, sizeof(dest.sin_zero)); s = socket(PF_INET, SOCK_STREAM, 0); - if(s < 0) + if(ISINVALID(s)) { PRINT_SOCKET_ERROR("socket"); - return -1; + return INVALID_SOCKET; } #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT /* setting a 3 seconds timeout for the connect() call */ @@ -89,35 +93,52 @@ int connecthostport(const char * host, unsigned short port) timeout.tv_usec = 0; if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); + PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO"); } timeout.tv_sec = 3; timeout.tv_usec = 0; if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); + PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO"); } #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ dest.sin_family = AF_INET; dest.sin_port = htons(port); n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in)); #ifdef MINIUPNPC_IGNORE_EINTR - while(n < 0 && errno == EINTR) + /* EINTR The system call was interrupted by a signal that was caught + * EINPROGRESS The socket is nonblocking and the connection cannot + * be completed immediately. */ + while(n < 0 && (errno == EINTR || errno == EINPROGRESS)) { socklen_t len; fd_set wset; int err; FD_ZERO(&wset); FD_SET(s, &wset); - if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR) +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + timeout.tv_sec = 3; + timeout.tv_usec = 0; + n = select(s + 1, NULL, &wset, NULL, &timeout); +#else + n = select(s + 1, NULL, &wset, NULL, NULL); +#endif + if(n == -1 && errno == EINTR) continue; +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + if(n == 0) { + errno = ETIMEDOUT; + n = -1; + break; + } +#endif /*len = 0;*/ /*n = getpeername(s, NULL, &len);*/ len = sizeof(err); if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { PRINT_SOCKET_ERROR("getsockopt"); closesocket(s); - return -1; + return INVALID_SOCKET; } if(err != 0) { errno = err; @@ -129,7 +150,7 @@ int connecthostport(const char * host, unsigned short port) { PRINT_SOCKET_ERROR("connect"); closesocket(s); - return -1; + return INVALID_SOCKET; } #else /* #ifdef USE_GETHOSTBYNAME */ /* use getaddrinfo() instead of gethostbyname() */ @@ -145,10 +166,12 @@ int connecthostport(const char * host, unsigned short port) if(host[0] == '[') { /* literal ip v6 address */ - int i; - for(i = 0; host[i+1] && (host[i+1] != ']') && i < MAXHOSTNAMELEN; i++) + int i, j; + for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++) { - tmp_host[i] = host[i+1]; + tmp_host[i] = host[j]; + if(0 == strncmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */ + j+=2; /* skip "25" */ } tmp_host[i] = '\0'; } @@ -160,19 +183,29 @@ int connecthostport(const char * host, unsigned short port) n = getaddrinfo(tmp_host, port_str, &hints, &ai); if(n != 0) { -#ifdef WIN32 +#ifdef _WIN32 fprintf(stderr, "getaddrinfo() error : %d\n", n); #else fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n)); #endif - return -1; + return INVALID_SOCKET; } - s = -1; + s = INVALID_SOCKET; for(p = ai; p; p = p->ai_next) { + if(!ISINVALID(s)) + closesocket(s); +#ifdef DEBUG + printf("ai_family=%d ai_socktype=%d ai_protocol=%d (PF_INET=%d, PF_INET6=%d)\n", + p->ai_family, p->ai_socktype, p->ai_protocol, PF_INET, PF_INET6); +#endif s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if(s < 0) + if(ISINVALID(s)) continue; + if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) { + struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr; + addr6->sin6_scope_id = scope_id; + } #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT /* setting a 3 seconds timeout for the connect() call */ timeout.tv_sec = 3; @@ -188,17 +221,34 @@ int connecthostport(const char * host, unsigned short port) PRINT_SOCKET_ERROR("setsockopt"); } #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ - n = connect(s, p->ai_addr, p->ai_addrlen); + n = connect(s, p->ai_addr, MSC_CAST_INT p->ai_addrlen); #ifdef MINIUPNPC_IGNORE_EINTR - while(n < 0 && errno == EINTR) + /* EINTR The system call was interrupted by a signal that was caught + * EINPROGRESS The socket is nonblocking and the connection cannot + * be completed immediately. */ + while(n < 0 && (errno == EINTR || errno == EINPROGRESS)) { socklen_t len; fd_set wset; int err; FD_ZERO(&wset); FD_SET(s, &wset); - if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR) +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + timeout.tv_sec = 3; + timeout.tv_usec = 0; + n = select(s + 1, NULL, &wset, NULL, &timeout); +#else + n = select(s + 1, NULL, &wset, NULL, NULL); +#endif + if(n == -1 && errno == EINTR) continue; +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + if(n == 0) { + errno = ETIMEDOUT; + n = -1; + break; + } +#endif /*len = 0;*/ /*n = getpeername(s, NULL, &len);*/ len = sizeof(err); @@ -206,7 +256,7 @@ int connecthostport(const char * host, unsigned short port) PRINT_SOCKET_ERROR("getsockopt"); closesocket(s); freeaddrinfo(ai); - return -1; + return INVALID_SOCKET; } if(err != 0) { errno = err; @@ -214,28 +264,21 @@ int connecthostport(const char * host, unsigned short port) } } #endif /* #ifdef MINIUPNPC_IGNORE_EINTR */ - if(n < 0) - { - closesocket(s); - continue; - } - else - { + if(n >= 0) /* connect() was successful */ break; - } } freeaddrinfo(ai); - if(s < 0) + if(ISINVALID(s)) { PRINT_SOCKET_ERROR("socket"); - return -1; + return INVALID_SOCKET; } if(n < 0) { PRINT_SOCKET_ERROR("connect"); - return -1; + closesocket(s); + return INVALID_SOCKET; } #endif /* #ifdef USE_GETHOSTBYNAME */ return s; } - diff --git a/libs/miniupnpc/src/connecthostport.h b/libs/miniupnpc/src/connecthostport.h new file mode 100644 index 000000000..b1ce627ce --- /dev/null +++ b/libs/miniupnpc/src/connecthostport.h @@ -0,0 +1,20 @@ +/* $Id: connecthostport.h,v 1.4 2018/04/06 10:53:13 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ + * Author: Thomas Bernard + * Copyright (c) 2010-2018 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef CONNECTHOSTPORT_H_INCLUDED +#define CONNECTHOSTPORT_H_INCLUDED + +#include "miniupnpc_socketdef.h" + +/* connecthostport() + * return a socket connected (TCP) to the host and port + * or INVALID_SOCKET in case of error */ +SOCKET connecthostport(const char * host, unsigned short port, + unsigned int scope_id); + +#endif + diff --git a/libs/miniupnpc/igd_desc_parse.c b/libs/miniupnpc/src/igd_desc_parse.c similarity index 83% rename from libs/miniupnpc/igd_desc_parse.c rename to libs/miniupnpc/src/igd_desc_parse.c index 6c3e65677..d2999ad01 100644 --- a/libs/miniupnpc/igd_desc_parse.c +++ b/libs/miniupnpc/src/igd_desc_parse.c @@ -1,8 +1,8 @@ -/* $Id: igd_desc_parse.c,v 1.14 2011/04/11 09:19:24 nanard Exp $ */ +/* $Id: igd_desc_parse.c,v 1.17 2015/09/15 13:30:04 nanard Exp $ */ /* Project : miniupnp * http://miniupnp.free.fr/ * Author : Thomas Bernard - * Copyright (c) 2005-2010 Thomas Bernard + * Copyright (c) 2005-2015 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -15,7 +15,9 @@ void IGDstartelt(void * d, const char * name, int l) { struct IGDdatas * datas = (struct IGDdatas *)d; - memcpy( datas->cureltname, name, l); + if(l >= MINIUPNPC_URL_MAXSIZE) + l = MINIUPNPC_URL_MAXSIZE-1; + memcpy(datas->cureltname, name, l); datas->cureltname[l] = '\0'; datas->level++; if( (l==7) && !memcmp(name, "service", l) ) { @@ -26,6 +28,8 @@ void IGDstartelt(void * d, const char * name, int l) } } +#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1)) + /* End element handler : * update nesting level counter and update parser state if * service element is parsed */ @@ -36,23 +40,16 @@ void IGDendelt(void * d, const char * name, int l) /*printf("endelt %2d %.*s\n", datas->level, l, name);*/ if( (l==7) && !memcmp(name, "service", l) ) { - /* - if( datas->state < 1 - && !strcmp(datas->servicetype, - // "urn:schemas-upnp-org:service:WANIPConnection:1") ) - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) - datas->state ++; - */ - if(0==strcmp(datas->tmp.servicetype, - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) { + if(COMPARE(datas->tmp.servicetype, + "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) { memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service)); - } else if(0==strcmp(datas->tmp.servicetype, - "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1")) { + } else if(COMPARE(datas->tmp.servicetype, + "urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) { memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service)); - } else if(0==strcmp(datas->tmp.servicetype, - "urn:schemas-upnp-org:service:WANIPConnection:1") - || 0==strcmp(datas->tmp.servicetype, - "urn:schemas-upnp-org:service:WANPPPConnection:1") ) { + } else if(COMPARE(datas->tmp.servicetype, + "urn:schemas-upnp-org:service:WANIPConnection:") + || COMPARE(datas->tmp.servicetype, + "urn:schemas-upnp-org:service:WANPPPConnection:") ) { if(datas->first.servicetype[0] == '\0') { memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service)); } else { @@ -93,6 +90,7 @@ void IGDdata(void * d, const char * data, int l) } } +#ifdef DEBUG void printIGD(struct IGDdatas * d) { printf("urlbase = '%s'\n", d->urlbase); @@ -121,5 +119,5 @@ void printIGD(struct IGDdatas * d) printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl); printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl); } - +#endif /* DEBUG */ diff --git a/libs/miniupnpc/src/listdevices.c b/libs/miniupnpc/src/listdevices.c new file mode 100644 index 000000000..dabc056e9 --- /dev/null +++ b/libs/miniupnpc/src/listdevices.c @@ -0,0 +1,197 @@ +/* $Id: listdevices.c,v 1.8 2018/05/03 08:16:44 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2013-2015 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. */ + +#include +#include +#include +#ifdef _WIN32 +#include +#endif /* _WIN32 */ +#include "miniupnpc.h" + +struct upnp_dev_list { + struct upnp_dev_list * next; + char * descURL; + struct UPNPDev * * array; + size_t count; + size_t allocated_count; +}; + +#define ADD_DEVICE_COUNT_STEP 16 + +void add_device(struct upnp_dev_list * * list_head, struct UPNPDev * dev) +{ + struct upnp_dev_list * elt; + size_t i; + + if(dev == NULL) + return; + for(elt = *list_head; elt != NULL; elt = elt->next) { + if(strcmp(elt->descURL, dev->descURL) == 0) { + for(i = 0; i < elt->count; i++) { + if (strcmp(elt->array[i]->st, dev->st) == 0 && strcmp(elt->array[i]->usn, dev->usn) == 0) { + return; /* already found */ + } + } + if(elt->count >= elt->allocated_count) { + struct UPNPDev * * tmp; + elt->allocated_count += ADD_DEVICE_COUNT_STEP; + tmp = realloc(elt->array, elt->allocated_count * sizeof(struct UPNPDev *)); + if(tmp == NULL) { + fprintf(stderr, "Failed to realloc(%p, %lu)\n", elt->array, (unsigned long)(elt->allocated_count * sizeof(struct UPNPDev *))); + return; + } + elt->array = tmp; + } + elt->array[elt->count++] = dev; + return; + } + } + elt = malloc(sizeof(struct upnp_dev_list)); + if(elt == NULL) { + fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)sizeof(struct upnp_dev_list)); + return; + } + elt->next = *list_head; + elt->descURL = strdup(dev->descURL); + if(elt->descURL == NULL) { + fprintf(stderr, "Failed to strdup(%s)\n", dev->descURL); + free(elt); + return; + } + elt->allocated_count = ADD_DEVICE_COUNT_STEP; + elt->array = malloc(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *)); + if(elt->array == NULL) { + fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *))); + free(elt->descURL); + free(elt); + return; + } + elt->array[0] = dev; + elt->count = 1; + *list_head = elt; +} + +void free_device(struct upnp_dev_list * elt) +{ + free(elt->descURL); + free(elt->array); + free(elt); +} + +int main(int argc, char * * argv) +{ + const char * searched_device = NULL; + const char * * searched_devices = NULL; + const char * multicastif = 0; + const char * minissdpdpath = 0; + int ipv6 = 0; + unsigned char ttl = 2; + int error = 0; + struct UPNPDev * devlist = 0; + struct UPNPDev * dev; + struct upnp_dev_list * sorted_list = NULL; + struct upnp_dev_list * dev_array; + int i; + +#ifdef _WIN32 + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if(nResult != NO_ERROR) + { + fprintf(stderr, "WSAStartup() failed.\n"); + return -1; + } +#endif + + for(i = 1; i < argc; i++) { + if(strcmp(argv[i], "-6") == 0) + ipv6 = 1; + else if(strcmp(argv[i], "-d") == 0) { + if(++i >= argc) { + fprintf(stderr, "%s option needs one argument\n", "-d"); + return 1; + } + searched_device = argv[i]; + } else if(strcmp(argv[i], "-t") == 0) { + if(++i >= argc) { + fprintf(stderr, "%s option needs one argument\n", "-t"); + return 1; + } + ttl = (unsigned char)atoi(argv[i]); + } else if(strcmp(argv[i], "-l") == 0) { + if(++i >= argc) { + fprintf(stderr, "-l option needs at least one argument\n"); + return 1; + } + searched_devices = (const char * *)(argv + i); + break; + } else if(strcmp(argv[i], "-m") == 0) { + if(++i >= argc) { + fprintf(stderr, "-m option needs one argument\n"); + return 1; + } + multicastif = argv[i]; + } else { + printf("usage : %s [options] [-l ...]\n", argv[0]); + printf("options :\n"); + printf(" -6 : use IPv6\n"); + printf(" -m address/ifname : network interface to use for multicast\n"); + printf(" -d : search only for this type of device\n"); + printf(" -l ... : search only for theses types of device\n"); + printf(" -t ttl : set multicast TTL. Default value is 2.\n"); + printf(" -h : this help\n"); + return 1; + } + } + + if(searched_device) { + printf("searching UPnP device type %s\n", searched_device); + devlist = upnpDiscoverDevice(searched_device, + 2000, multicastif, minissdpdpath, + 0/*localport*/, ipv6, ttl, &error); + } else if(searched_devices) { + printf("searching UPnP device types :\n"); + for(i = 0; searched_devices[i]; i++) + printf("\t%s\n", searched_devices[i]); + devlist = upnpDiscoverDevices(searched_devices, + 2000, multicastif, minissdpdpath, + 0/*localport*/, ipv6, ttl, &error, 1); + } else { + printf("searching all UPnP devices\n"); + devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath, + 0/*localport*/, ipv6, ttl, &error); + } + if(devlist) { + for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) { + printf("%3d: %-48s\n", i, dev->st); + printf(" %s\n", dev->descURL); + printf(" %s\n", dev->usn); + add_device(&sorted_list, dev); + } + putchar('\n'); + for (dev_array = sorted_list; dev_array != NULL ; dev_array = dev_array->next) { + printf("%s :\n", dev_array->descURL); + for(i = 0; (unsigned)i < dev_array->count; i++) { + printf("%2d: %s\n", i+1, dev_array->array[i]->st); + printf(" %s\n", dev_array->array[i]->usn); + } + putchar('\n'); + } + freeUPNPDevlist(devlist); + while(sorted_list != NULL) { + dev_array = sorted_list; + sorted_list = sorted_list->next; + free_device(dev_array); + } + } else { + printf("no device found.\n"); + } + + return 0; +} + diff --git a/libs/miniupnpc/minihttptestserver.c b/libs/miniupnpc/src/minihttptestserver.c similarity index 56% rename from libs/miniupnpc/minihttptestserver.c rename to libs/miniupnpc/src/minihttptestserver.c index a1dddf0ea..57235841e 100644 --- a/libs/miniupnpc/minihttptestserver.c +++ b/libs/miniupnpc/src/minihttptestserver.c @@ -1,7 +1,7 @@ -/* $Id: minihttptestserver.c,v 1.6 2011/05/09 08:53:15 nanard Exp $ */ +/* $Id: minihttptestserver.c,v 1.25 2020/05/29 21:14:22 nanard Exp $ */ /* Project : miniUPnP * Author : Thomas Bernard - * Copyright (c) 2011 Thomas Bernard + * Copyright (c) 2011-2018 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ @@ -16,36 +16,43 @@ #include #include #include +#include + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 +#endif #define CRAP_LENGTH (2048) -volatile int quit = 0; -volatile int child_to_wait_for = 0; +static int server(unsigned short port, const char * expected_file_name, int ipv6); + +volatile sig_atomic_t quit = 0; +volatile sig_atomic_t child_to_wait_for = 0; /** * signal handler for SIGCHLD (child status has changed) */ void handle_signal_chld(int sig) { - printf("handle_signal_chld(%d)\n", sig); + (void)sig; + /* printf("handle_signal_chld(%d)\n", sig); */ ++child_to_wait_for; } /** * signal handler for SIGINT (CRTL C) */ -#if 0 void handle_signal_int(int sig) { - printf("handle_signal_int(%d)\n", sig); + (void)sig; + /* printf("handle_signal_int(%d)\n", sig); */ quit = 1; } -#endif /** * build a text/plain content of the specified length */ -void build_content(char * p, int n) +void build_content(char * p, size_t n) { char line_buffer[80]; int k; @@ -74,10 +81,10 @@ void build_content(char * p, int n) /** * build crappy content */ -void build_crap(char * p, int n) +void build_crap(char * p, size_t n) { static const char crap[] = "_CRAP_\r\n"; - int i; + size_t i; while(n > 0) { i = sizeof(crap) - 1; @@ -93,15 +100,19 @@ void build_crap(char * p, int n) * build chunked response. * return a malloc'ed buffer */ -char * build_chunked_response(int content_length, int * response_len) { +char * build_chunked_response(size_t content_length, size_t * response_len) +{ char * response_buffer; char * content_buffer; - int buffer_length; - int i, n; + size_t buffer_length; + size_t i; + unsigned int n; /* allocate to have some margin */ buffer_length = 256 + content_length + (content_length >> 4); response_buffer = malloc(buffer_length); + if(response_buffer == NULL) + return NULL; *response_len = snprintf(response_buffer, buffer_length, "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" @@ -110,6 +121,10 @@ char * build_chunked_response(int content_length, int * response_len) { /* build the content */ content_buffer = malloc(content_length); + if(content_buffer == NULL) { + free(response_buffer); + return NULL; + } build_content(content_buffer, content_length); /* chunk it */ @@ -129,16 +144,126 @@ char * build_chunked_response(int content_length, int * response_len) { response_buffer[(*response_len)++] = '\r'; response_buffer[(*response_len)++] = '\n'; } - memcpy(response_buffer + *response_len, "0\r\n", 3); - *response_len += 3; + /* the last chunk : "0\r\n" a empty body and then + * the final "\r\n" */ + memcpy(response_buffer + *response_len, "0\r\n\r\n", 5); + *response_len += 5; free(content_buffer); - printf("resp_length=%d buffer_length=%d content_length=%d\n", + printf("resp_length=%lu buffer_length=%lu content_length=%lu\n", *response_len, buffer_length, content_length); return response_buffer; } -enum modes { MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL }; +/* favicon.ico generator */ +#ifdef OLD_HEADER +#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4) +#else +#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4) +#endif +void build_favicon_content(unsigned char * p, size_t n) +{ + int i; + if(n < FAVICON_LENGTH) + return; + /* header : 6 bytes */ + *p++ = 0; + *p++ = 0; + *p++ = 1; /* type : ICO */ + *p++ = 0; + *p++ = 1; /* number of images in file */ + *p++ = 0; + /* image directory (1 entry) : 16 bytes */ + *p++ = 16; /* width */ + *p++ = 16; /* height */ + *p++ = 2; /* number of colors in the palette. 0 = no palette */ + *p++ = 0; /* reserved */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ +#ifdef OLD_HEADER + *p++ = 12 + 8 + 32 * 4; /* bmp size */ +#else + *p++ = 40 + 8 + 32 * 4; /* bmp size */ +#endif + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 6 + 16; /* bmp offset */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + /* BMP */ +#ifdef OLD_HEADER + /* BITMAPCOREHEADER */ + *p++ = 12; /* size of this header */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16; /* width */ + *p++ = 0; /* " */ + *p++ = 16 * 2; /* height x 2 ! */ + *p++ = 0; /* " */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ +#else + /* BITMAPINFOHEADER */ + *p++ = 40; /* size of this header */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16; /* width */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16 * 2; /* height x 2 ! */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ + /* compression method, image size, ppm x, ppm y */ + /* colors in the palette ? */ + /* important colors */ + for(i = 4 * 6; i > 0; --i) + *p++ = 0; +#endif + /* palette */ + *p++ = 0; /* b */ + *p++ = 0; /* g */ + *p++ = 0; /* r */ + *p++ = 0; /* reserved */ + *p++ = 255; /* b */ + *p++ = 255; /* g */ + *p++ = 255; /* r */ + *p++ = 0; /* reserved */ + /* pixel data */ + for(i = 16; i > 0; --i) { + if(i & 1) { + *p++ = 0125; + *p++ = 0125; + } else { + *p++ = 0252; + *p++ = 0252; + } + *p++ = 0; + *p++ = 0; + } + /* Opacity MASK */ + for(i = 16 * 4; i > 0; --i) { + *p++ = 0; + } +} + +enum modes { + MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON, MODE_MALFORMED +}; + const struct { const enum modes mode; const char * text; @@ -146,28 +271,33 @@ const struct { {MODE_CHUNKED, "chunked"}, {MODE_ADDCRAP, "addcrap"}, {MODE_NORMAL, "normal"}, + {MODE_FAVICON, "favicon.ico"}, + {MODE_MALFORMED, "malformed"}, {MODE_INVALID, NULL} }; /** * write the response with random behaviour ! */ -void send_response(int c, const char * buffer, int len) +void send_response(int c, const char * buffer, size_t len) { - int n; + ssize_t n; while(len > 0) { n = (rand() % 99) + 1; - if(n > len) + if((size_t)n > len) n = len; n = write(c, buffer, n); if(n < 0) { - perror("write"); - return; + if(errno != EINTR) { + perror("write"); + return; + } + /* if errno == EINTR, try again */ } else { len -= n; buffer += n; + usleep(10000); /* 10ms */ } - usleep(10000); /* 10ms */ } } @@ -177,17 +307,18 @@ void send_response(int c, const char * buffer, int len) void handle_http_connection(int c) { char request_buffer[2048]; - int request_len = 0; + size_t request_len = 0; int headers_found = 0; - int n, i; + ssize_t n, m; + size_t i; char request_method[16]; char request_uri[256]; char http_version[16]; char * p; char * response_buffer; - int response_len; + size_t response_len; enum modes mode; - int content_length = 16*1024; + size_t content_length = 16*1024; /* read the request */ while(request_len < sizeof(request_buffer) && !headers_found) { @@ -195,6 +326,8 @@ void handle_http_connection(int c) request_buffer + request_len, sizeof(request_buffer) - request_len); if(n < 0) { + if(errno == EINTR) + continue; perror("read"); return; } else if(n==0) { @@ -213,9 +346,10 @@ void handle_http_connection(int c) } if(!headers_found) { /* error */ + printf("no HTTP header found in the request\n"); return; } - printf("headers :\n%.*s", request_len, request_buffer); + printf("headers :\n%.*s", (int)request_len, request_buffer); /* the request have been received, now parse the request line */ p = request_buffer; for(i = 0; i < sizeof(request_method) - 1; i++) { @@ -227,7 +361,7 @@ void handle_http_connection(int c) request_method[i] = '\0'; while(*p == ' ') p++; - for(i = 0; i < sizeof(request_uri) - 1; i++) { + for(i = 0; i < (int)sizeof(request_uri) - 1; i++) { if(*p == ' ' || *p == '\r') break; request_uri[i] = *p; @@ -236,7 +370,7 @@ void handle_http_connection(int c) request_uri[i] = '\0'; while(*p == ' ') p++; - for(i = 0; i < sizeof(http_version) - 1; i++) { + for(i = 0; i < (int)sizeof(http_version) - 1; i++) { if(*p == ' ' || *p == '\r') break; http_version[i] = *p; @@ -249,10 +383,24 @@ void handle_http_connection(int c) if(0 != strcmp(request_method, "GET")) { const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n" "Allow: GET\r\n\r\n"; + const char * pc; /* 405 Method Not Allowed */ /* The response MUST include an Allow header containing a list * of valid methods for the requested resource. */ - write(c, response405, sizeof(response405) - 1); + n = sizeof(response405) - 1; + pc = response405; + while(n > 0) { + m = write(c, pc, n); + if(m<0) { + if(errno != EINTR) { + perror("write"); + return; + } + } else { + n -= m; + pc += m; + } + } return; } @@ -266,33 +414,81 @@ void handle_http_connection(int c) } switch(mode) { + case MODE_MALFORMED: + response_len = 2048; + response_buffer = malloc(response_len); + if(!response_buffer) + break; + n = snprintf(response_buffer, response_len, + "HTTP/1.1 \r\n" + "\r\n" + /*"0000\r\n"*/); + for (i = n; i < response_len; i++) { + response_buffer[i] = ' '; + } + response_len = n; + break; case MODE_CHUNKED: response_buffer = build_chunked_response(content_length, &response_len); break; case MODE_ADDCRAP: response_len = content_length+256; response_buffer = malloc(response_len); + if(!response_buffer) + break; n = snprintf(response_buffer, response_len, "HTTP/1.1 200 OK\r\n" "Server: minihttptestserver\r\n" "Content-Type: text/plain\r\n" - "Content-Length: %d\r\n" + "Content-Length: %lu\r\n" "\r\n", content_length); response_len = content_length+n+CRAP_LENGTH; - response_buffer = realloc(response_buffer, response_len); + p = realloc(response_buffer, response_len); + if(p == NULL) { + /* error 500 */ + free(response_buffer); + response_buffer = NULL; + break; + } + response_buffer = p; build_content(response_buffer + n, content_length); build_crap(response_buffer + n + content_length, CRAP_LENGTH); break; + case MODE_FAVICON: + content_length = FAVICON_LENGTH; + response_len = content_length + 256; + response_buffer = malloc(response_len); + if(!response_buffer) + break; + n = snprintf(response_buffer, response_len, + "HTTP/1.1 200 OK\r\n" + "Server: minihttptestserver\r\n" + "Content-Type: image/vnd.microsoft.icon\r\n" + "Content-Length: %lu\r\n" + "\r\n", content_length); + /* image/x-icon */ + build_favicon_content((unsigned char *)(response_buffer + n), content_length); + response_len = content_length + n; + break; default: response_len = content_length+256; response_buffer = malloc(response_len); + if(!response_buffer) + break; n = snprintf(response_buffer, response_len, "HTTP/1.1 200 OK\r\n" "Server: minihttptestserver\r\n" "Content-Type: text/plain\r\n" "\r\n"); response_len = content_length+n; - response_buffer = realloc(response_buffer, response_len); + p = realloc(response_buffer, response_len); + if(p == NULL) { + /* Error 500 */ + free(response_buffer); + response_buffer = NULL; + break; + } + response_buffer = p; build_content(response_buffer + n, response_len - n); } @@ -308,15 +504,8 @@ void handle_http_connection(int c) */ int main(int argc, char * * argv) { int ipv6 = 0; - int s, c, i; + int r, i; unsigned short port = 0; - struct sockaddr_storage server_addr; - socklen_t server_addrlen; - struct sockaddr_storage client_addr; - socklen_t client_addrlen; - pid_t pid; - int child = 0; - int status; const char * expected_file_name = NULL; for(i = 1; i < argc; i++) { @@ -339,15 +528,46 @@ int main(int argc, char * * argv) { fprintf(stderr, "unknown command line switch '%s'\n", argv[i]); } } else { - fprintf(stderr, "unkown command line argument '%s'\n", argv[i]); + fprintf(stderr, "unknown command line argument '%s'\n", argv[i]); } } srand(time(NULL)); - signal(SIGCHLD, handle_signal_chld); -#if 0 - signal(SIGINT, handle_signal_int); -#endif + + r = server(port, expected_file_name, ipv6); + if(r != 0) { + printf("*** ERROR ***\n"); + } + return r; +} + +static int server(unsigned short port, const char * expected_file_name, int ipv6) +{ + int s, c; + int i; + struct sockaddr_storage server_addr; + socklen_t server_addrlen; + struct sockaddr_storage client_addr; + socklen_t client_addrlen; + pid_t pid; + int child = 0; + int status; + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + + /*signal(SIGCHLD, handle_signal_chld);*/ + sa.sa_handler = handle_signal_chld; + if(sigaction(SIGCHLD, &sa, NULL) < 0) { + perror("sigaction"); + return 1; + } + /*signal(SIGINT, handle_signal_int);*/ + sa.sa_handler = handle_signal_int; + if(sigaction(SIGINT, &sa, NULL) < 0) { + perror("sigaction"); + return 1; + } s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); if(s < 0) { @@ -360,12 +580,12 @@ int main(int argc, char * * argv) { struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr; addr->sin6_family = AF_INET6; addr->sin6_port = htons(port); - addr->sin6_addr = in6addr_any; + addr->sin6_addr = in6addr_loopback; } else { struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr; addr->sin_family = AF_INET; addr->sin_port = htons(port); - addr->sin_addr.s_addr = htonl(INADDR_ANY); + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); } if(bind(s, (struct sockaddr *)&server_addr, ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) { @@ -399,10 +619,19 @@ int main(int argc, char * * argv) { if(f) { char * buffer; buffer = malloc(16*1024); - build_content(buffer, 16*1024); - fwrite(buffer, 1, 16*1024, f); - free(buffer); + if(buffer == NULL) { + fprintf(stderr, "memory allocation error\n"); + } else { + build_content(buffer, 16*1024); + i = fwrite(buffer, 1, 16*1024, f); + if(i != 16*1024) { + fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024); + } + free(buffer); + } fclose(f); + } else { + fprintf(stderr, "error opening file %s for writing\n", expected_file_name); } } @@ -413,16 +642,16 @@ int main(int argc, char * * argv) { if(pid < 0) { perror("wait"); } else { - printf("child(%d) terminated with status %d\n", pid, status); + printf("child(%d) terminated with status %d\n", (int)pid, status); } --child_to_wait_for; } - /* TODO : add a select() call in order to handle the case - * when a signal is caught */ client_addrlen = sizeof(struct sockaddr_storage); c = accept(s, (struct sockaddr *)&client_addr, &client_addrlen); if(c < 0) { + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; perror("accept"); return 1; } @@ -450,7 +679,7 @@ int main(int argc, char * * argv) { if(pid < 0) { perror("wait"); } else { - printf("child(%d) terminated with status %d\n", pid, status); + printf("child(%d) terminated with status %d\n", (int)pid, status); } --child_to_wait_for; } diff --git a/libs/miniupnpc/minisoap.c b/libs/miniupnpc/src/minisoap.c similarity index 81% rename from libs/miniupnpc/minisoap.c rename to libs/miniupnpc/src/minisoap.c index 8889bf040..78606672d 100644 --- a/libs/miniupnpc/minisoap.c +++ b/libs/miniupnpc/src/minisoap.c @@ -1,7 +1,8 @@ -/* $Id: minisoap.c,v 1.21 2011/03/22 19:15:35 nanard Exp $ */ -/* Project : miniupnp +/* $Id: minisoap.c,v 1.30 2020/11/09 19:27:42 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2009 Thomas Bernard + * Copyright (c) 2005-2020 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * @@ -9,10 +10,10 @@ */ #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #include -#define snprintf _snprintf +#include "win32_snprintf.h" #else #include #include @@ -24,16 +25,10 @@ /* only for malloc */ #include -#ifdef WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif - /* httpWrite sends the headers and the body to the socket * and returns the number of bytes sent */ static int -httpWrite(int fd, const char * body, int bodysize, +httpWrite(SOCKET fd, const char * body, int bodysize, const char * headers, int headerssize) { int n = 0; @@ -43,10 +38,10 @@ httpWrite(int fd, const char * body, int bodysize, /* Note : my old linksys router only took into account * soap request that are sent into only one packet */ char * p; - /* TODO: AVOID MALLOC */ + /* TODO: AVOID MALLOC, we could use writev() for that */ p = malloc(headerssize+bodysize); if(!p) - return 0; + return -1; memcpy(p, headers, headerssize); memcpy(p+headerssize, body, bodysize); /*n = write(fd, p, headerssize+bodysize);*/ @@ -55,9 +50,9 @@ httpWrite(int fd, const char * body, int bodysize, PRINT_SOCKET_ERROR("send"); } /* disable send on the socket */ - /* draytek routers dont seems to like that... */ + /* draytek routers don't seem to like that... */ #if 0 -#ifdef WIN32 +#ifdef _WIN32 if(shutdown(fd, SD_SEND)<0) { #else if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/ @@ -70,7 +65,7 @@ httpWrite(int fd, const char * body, int bodysize, } /* self explanatory */ -int soapPostSubmit(int fd, +int soapPostSubmit(SOCKET fd, const char * url, const char * host, unsigned short port, @@ -78,11 +73,10 @@ int soapPostSubmit(int fd, const char * body, const char * httpversion) { - int bodysize; char headerbuf[512]; int headerssize; char portstr[8]; - bodysize = (int)strlen(body); + int bodysize = (int)strlen(body); /* We are not using keep-alive HTTP connections. * HTTP/1.1 needs the header Connection: close to do that. * This is the default with HTTP/1.0 @@ -96,7 +90,7 @@ int soapPostSubmit(int fd, headerssize = snprintf(headerbuf, sizeof(headerbuf), "POST %s HTTP/%s\r\n" "Host: %s%s\r\n" - "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" + "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" "Content-Length: %d\r\n" "Content-Type: text/xml\r\n" "SOAPAction: \"%s\"\r\n" @@ -105,6 +99,8 @@ int soapPostSubmit(int fd, "Pragma: no-cache\r\n" "\r\n", url, httpversion, host, portstr, bodysize, action); + if ((unsigned int)headerssize >= sizeof(headerbuf)) + return -1; #ifdef DEBUG /*printf("SOAP request : headersize=%d bodysize=%d\n", headerssize, bodysize); diff --git a/libs/miniupnpc/minisoap.h b/libs/miniupnpc/src/minisoap.h similarity index 51% rename from libs/miniupnpc/minisoap.h rename to libs/miniupnpc/src/minisoap.h index 696725f62..f28684205 100644 --- a/libs/miniupnpc/minisoap.h +++ b/libs/miniupnpc/src/minisoap.h @@ -1,14 +1,16 @@ -/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */ +/* $Id: minisoap.h,v 1.6 2018/04/06 10:53:13 nanard Exp $ */ /* Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard + * Copyright (c) 2005-2018 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ -#ifndef __MINISOAP_H__ -#define __MINISOAP_H__ +#ifndef MINISOAP_H_INCLUDED +#define MINISOAP_H_INCLUDED + +#include "miniupnpc_socketdef.h" /*int httpWrite(int, const char *, int, const char *);*/ -int soapPostSubmit(int, const char *, const char *, unsigned short, +int soapPostSubmit(SOCKET, const char *, const char *, unsigned short, const char *, const char *, const char *); #endif diff --git a/libs/miniupnpc/src/minissdpc.c b/libs/miniupnpc/src/minissdpc.c new file mode 100644 index 000000000..edebb1600 --- /dev/null +++ b/libs/miniupnpc/src/minissdpc.c @@ -0,0 +1,1019 @@ +/* $Id: minissdpc.c,v 1.49 2021/05/13 11:00:36 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas BERNARD + * copyright (c) 2005-2021 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#include +#include +#include +#include +#include +#if defined (__NetBSD__) +#include +#endif +#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) +#ifdef _WIN32 +#include +#include +#include +#include +#include "win32_snprintf.h" +#if !defined(_MSC_VER) +#include +#else /* !defined(_MSC_VER) */ +typedef unsigned short uint16_t; +#endif /* !defined(_MSC_VER) */ +#ifndef strncasecmp +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define strncasecmp _memicmp +#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ +#define strncasecmp memicmp +#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ +#endif /* #ifndef strncasecmp */ +#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP +#define in6addr_any in6addr_any_init +static const IN6_ADDR in6addr_any_init = {0}; +#endif +#endif +#endif /* _WIN32 */ +#if defined(__amigaos__) || defined(__amigaos4__) +#include +#endif /* defined(__amigaos__) || defined(__amigaos4__) */ +#if defined(__amigaos__) +#define uint16_t unsigned short +#endif /* defined(__amigaos__) */ +/* Hack */ +#define UNIX_PATH_LEN 108 +struct sockaddr_un { + uint16_t sun_family; + char sun_path[UNIX_PATH_LEN]; +}; +#else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define closesocket close +#endif + +#include "miniupnpc_socketdef.h" + +#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) && !defined(__HAIKU__) +#define HAS_IP_MREQN +#endif + +#ifndef _WIN32 +#include +#if defined(__sun) || defined(__HAIKU__) +#include +#endif +#endif + +#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN) +/* Several versions of glibc don't define this structure, + * define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */ +struct ip_mreqn +{ + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_address; /* local IP address of interface */ + int imr_ifindex; /* Interface index */ +}; +#endif + +#if defined(__amigaos__) || defined(__amigaos4__) +/* Amiga OS specific stuff */ +#define TIMEVAL struct timeval +#endif + +#include "minissdpc.h" +#include "miniupnpc.h" +#include "receivedata.h" + +#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) + +#include "codelength.h" + +struct UPNPDev * +getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error) +{ + struct UPNPDev * devlist = NULL; + int s; + int res; + + s = connectToMiniSSDPD(socketpath); + if (s < 0) { + if (error) + *error = s; + return NULL; + } + res = requestDevicesFromMiniSSDPD(s, devtype); + if (res < 0) { + if (error) + *error = res; + } else { + devlist = receiveDevicesFromMiniSSDPD(s, error); + } + disconnectFromMiniSSDPD(s); + return devlist; +} + +/* macros used to read from unix socket */ +#define READ_BYTE_BUFFER(c) \ + if((int)bufferindex >= n) { \ + n = read(s, buffer, sizeof(buffer)); \ + if(n<=0) break; \ + bufferindex = 0; \ + } \ + c = buffer[bufferindex++]; + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif /* MIN */ + +#define READ_COPY_BUFFER(dst, len) \ + for(l = len, p = (unsigned char *)dst; l > 0; ) { \ + unsigned int lcopy; \ + if((int)bufferindex >= n) { \ + n = read(s, buffer, sizeof(buffer)); \ + if(n<=0) break; \ + bufferindex = 0; \ + } \ + lcopy = MIN(l, (n - bufferindex)); \ + memcpy(p, buffer + bufferindex, lcopy); \ + l -= lcopy; \ + p += lcopy; \ + bufferindex += lcopy; \ + } + +#define READ_DISCARD_BUFFER(len) \ + for(l = len; l > 0; ) { \ + unsigned int lcopy; \ + if(bufferindex >= n) { \ + n = read(s, buffer, sizeof(buffer)); \ + if(n<=0) break; \ + bufferindex = 0; \ + } \ + lcopy = MIN(l, (n - bufferindex)); \ + l -= lcopy; \ + bufferindex += lcopy; \ + } + +int +connectToMiniSSDPD(const char * socketpath) +{ + int s; + struct sockaddr_un addr; +#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun) + struct timeval timeout; +#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if(s < 0) + { + /*syslog(LOG_ERR, "socket(unix): %m");*/ + perror("socket(unix)"); + return MINISSDPC_SOCKET_ERROR; + } +#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun) + /* setting a 3 seconds timeout */ + /* not supported for AF_UNIX sockets under Solaris */ + timeout.tv_sec = 3; + timeout.tv_usec = 0; + if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0) + { + perror("setsockopt SO_RCVTIMEO unix"); + } + timeout.tv_sec = 3; + timeout.tv_usec = 0; + if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0) + { + perror("setsockopt SO_SNDTIMEO unix"); + } +#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ + if(!socketpath) + socketpath = "/var/run/minissdpd.sock"; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path)); + /* TODO : check if we need to handle the EINTR */ + if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) + { + /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/ + close(s); + return MINISSDPC_SOCKET_ERROR; + } + return s; +} + +int +disconnectFromMiniSSDPD(int s) +{ + if (close(s) < 0) + return MINISSDPC_SOCKET_ERROR; + return MINISSDPC_SUCCESS; +} + +int +requestDevicesFromMiniSSDPD(int s, const char * devtype) +{ + unsigned char buffer[256]; + unsigned char * p; + unsigned int stsize, l; + + stsize = strlen(devtype); + if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8)) + { + buffer[0] = 3; /* request type 3 : everything */ + } + else + { + buffer[0] = 1; /* request type 1 : request devices/services by type */ + } + p = buffer + 1; + l = stsize; CODELENGTH(l, p); + if(p + stsize > buffer + sizeof(buffer)) + { + /* devtype is too long ! */ +#ifdef DEBUG + fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n", + stsize, (unsigned)sizeof(buffer)); +#endif /* DEBUG */ + return MINISSDPC_INVALID_INPUT; + } + memcpy(p, devtype, stsize); + p += stsize; + if(write(s, buffer, p - buffer) < 0) + { + /*syslog(LOG_ERR, "write(): %m");*/ + perror("minissdpc.c: write()"); + return MINISSDPC_SOCKET_ERROR; + } + return MINISSDPC_SUCCESS; +} + +struct UPNPDev * +receiveDevicesFromMiniSSDPD(int s, int * error) +{ + struct UPNPDev * tmp; + struct UPNPDev * devlist = NULL; + unsigned char buffer[256]; + ssize_t n; + unsigned char * p; + unsigned char * url; + unsigned char * st; + unsigned int bufferindex; + unsigned int i, ndev; + unsigned int urlsize, stsize, usnsize, l; + + n = read(s, buffer, sizeof(buffer)); + if(n<=0) + { + perror("minissdpc.c: read()"); + if (error) + *error = MINISSDPC_SOCKET_ERROR; + return NULL; + } + ndev = buffer[0]; + bufferindex = 1; + for(i = 0; i < ndev; i++) + { + DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER); + if(n<=0) { + if (error) + *error = MINISSDPC_INVALID_SERVER_REPLY; + return devlist; + } +#ifdef DEBUG + printf(" urlsize=%u", urlsize); +#endif /* DEBUG */ + url = malloc(urlsize); + if(url == NULL) { + if (error) + *error = MINISSDPC_MEMORY_ERROR; + return devlist; + } + READ_COPY_BUFFER(url, urlsize); + if(n<=0) { + if (error) + *error = MINISSDPC_INVALID_SERVER_REPLY; + goto free_url_and_return; + } + DECODELENGTH_READ(stsize, READ_BYTE_BUFFER); + if(n<=0) { + if (error) + *error = MINISSDPC_INVALID_SERVER_REPLY; + goto free_url_and_return; + } +#ifdef DEBUG + printf(" stsize=%u", stsize); +#endif /* DEBUG */ + st = malloc(stsize); + if (st == NULL) { + if (error) + *error = MINISSDPC_MEMORY_ERROR; + goto free_url_and_return; + } + READ_COPY_BUFFER(st, stsize); + if(n<=0) { + if (error) + *error = MINISSDPC_INVALID_SERVER_REPLY; + goto free_url_and_st_and_return; + } + DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER); + if(n<=0) { + if (error) + *error = MINISSDPC_INVALID_SERVER_REPLY; + goto free_url_and_st_and_return; + } +#ifdef DEBUG + printf(" usnsize=%u\n", usnsize); +#endif /* DEBUG */ + tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize); + if(tmp == NULL) { + if (error) + *error = MINISSDPC_MEMORY_ERROR; + goto free_url_and_st_and_return; + } + tmp->pNext = devlist; + tmp->descURL = tmp->buffer; + tmp->st = tmp->buffer + 1 + urlsize; + memcpy(tmp->buffer, url, urlsize); + tmp->buffer[urlsize] = '\0'; + memcpy(tmp->st, st, stsize); + tmp->buffer[urlsize+1+stsize] = '\0'; + free(url); + free(st); + url = NULL; + st = NULL; + tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize; + READ_COPY_BUFFER(tmp->usn, usnsize); + if(n<=0) { + if (error) + *error = MINISSDPC_INVALID_SERVER_REPLY; + goto free_tmp_and_return; + } + tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0'; + tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */ + devlist = tmp; + } + if (error) + *error = MINISSDPC_SUCCESS; + return devlist; + +free_url_and_st_and_return: + free(st); +free_url_and_return: + free(url); + return devlist; + +free_tmp_and_return: + free(tmp); + return devlist; +} + +#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */ + +/* parseMSEARCHReply() + * the last 4 arguments are filled during the parsing : + * - location/locationsize : "location:" field of the SSDP reply packet + * - st/stsize : "st:" field of the SSDP reply packet. + * - usn/usnsize : "usn:" filed of the SSDP reply packet + * The strings are NOT null terminated */ +static void +parseMSEARCHReply(const char * reply, int size, + const char * * location, int * locationsize, + const char * * st, int * stsize, + const char * * usn, int * usnsize) +{ + int a, b, i; + i = 0; + a = i; /* start of the line */ + b = 0; /* end of the "header" (position of the colon) */ + while(i= 0x0600 // _WIN32_WINNT_VISTA + ULONGLONG ts = GetTickCount64(); +#else + DWORD ts = GetTickCount(); +#endif + tv->tv_sec = (long)(ts / 1000); + tv->tv_usec = (ts % 1000) * 1000; + return 0; /* success */ +#elif defined(CLOCK_MONOTONIC_FAST) || defined(CLOCK_MONOTONIC) +#if defined(__APPLE__) +#if defined(__clang__) + if (__builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { +#else /* !defined(__clang__) */ + if (clock_gettime != NULL) { +#endif /* defined(__clang__) */ +#endif /* defined(__APPLE__) */ + struct timespec ts; + int ret_code = clock_gettime(UPNP_CLOCKID, &ts); + if (ret_code == 0) + { + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + } + return ret_code; +#if defined(__APPLE__) + } + else + { + /* fall-back for earlier Apple platforms */ + return gettimeofday(tv, NULL); + } +#endif /* defined(__APPLE__) */ +#else + return gettimeofday(tv, NULL); +#endif +} +/* port upnp discover : SSDP protocol */ +#define SSDP_PORT 1900 +#define XSTR(s) STR(s) +#define STR(s) #s +#define UPNP_MCAST_ADDR "239.255.255.250" +/* for IPv6 */ +#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */ +#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */ + +/* direct discovery if minissdpd responses are not sufficient */ +/* ssdpDiscoverDevices() : + * return a chained list of all devices found or NULL if + * no devices was found. + * It is up to the caller to free the chained list + * delay is in millisecond (poll). + * UDA v1.1 says : + * The TTL for the IP packet SHOULD default to 2 and + * SHOULD be configurable. */ +struct UPNPDev * +ssdpDiscoverDevices(const char * const deviceTypes[], + int delay, const char * multicastif, + int localport, + int ipv6, unsigned char ttl, + int * error, + int searchalltypes) +{ + struct UPNPDev * tmp; + struct UPNPDev * devlist = NULL; + unsigned int scope_id = 0; + int opt = 1; + static const char MSearchMsgFmt[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST: %s:" XSTR(SSDP_PORT) "\r\n" + "ST: %s\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: %u\r\n" + "\r\n"; + int deviceIndex; + char bufr[1536]; /* reception and emission buffer */ + SOCKET sudp; + int n; + struct sockaddr_storage sockudp_r; + unsigned int mx; +#ifdef NO_GETADDRINFO + struct sockaddr_storage sockudp_w; +#else + int rv; + struct addrinfo hints, *servinfo; +#endif +#ifdef _WIN32 + unsigned long _ttl = (unsigned long)ttl; +#endif + int linklocal = 1; + int sentok; + + if(error) + *error = MINISSDPC_UNKNOWN_ERROR; + + if(localport==UPNP_LOCAL_PORT_SAME) + localport = SSDP_PORT; + +#ifdef _WIN32 + sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#else + sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0); +#endif + if(ISINVALID(sudp)) + { + if(error) + *error = MINISSDPC_SOCKET_ERROR; + PRINT_SOCKET_ERROR("socket"); + return NULL; + } + /* reception */ + memset(&sockudp_r, 0, sizeof(struct sockaddr_storage)); + if(ipv6) { + struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r; + p->sin6_family = AF_INET6; + if(localport > 0 && localport < 65536) + p->sin6_port = htons((unsigned short)localport); + p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */ + } else { + struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r; + p->sin_family = AF_INET; + if(localport > 0 && localport < 65536) + p->sin_port = htons((unsigned short)localport); + p->sin_addr.s_addr = INADDR_ANY; + } +#ifdef _WIN32 +/* This code could help us to use the right Network interface for + * SSDP multicast traffic */ +/* Get IP associated with the index given in the ip_forward struct + * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */ + if(!ipv6) { + DWORD ifbestidx; +#if _WIN32_WINNT >= 0x0600 // _WIN32_WINNT_VISTA + // While we don't need IPv6 support, the IPv4 only funciton is not available in UWP apps. + SOCKADDR_IN destAddr; + memset(&destAddr, 0, sizeof(destAddr)); + destAddr.sin_family = AF_INET; + destAddr.sin_addr.s_addr = inet_addr("223.255.255.255"); + destAddr.sin_port = 0; + if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) { +#else + if (GetBestInterface(inet_addr("223.255.255.255"), &ifbestidx) == NO_ERROR) { +#endif + DWORD dwRetVal = NO_ERROR; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + ULONG outBufLen = 15360; + int Iterations; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + + for (Iterations = 0; Iterations < 3; Iterations++) { + pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, outBufLen); + if (pAddresses == NULL) { + break; + } + + dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen); + + if (dwRetVal != ERROR_BUFFER_OVERFLOW) { + break; + } + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; + } + + if (dwRetVal == NO_ERROR) { + pCurrAddresses = pAddresses; + while (pCurrAddresses) { +#ifdef DEBUG + int i; + PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL; + PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL; + + printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex); + printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName); + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pUnicast != NULL) { + for (i = 0; pUnicast != NULL; i++) { + printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pUnicast->Address.lpSockaddr)->sin_addr) ); + pUnicast = pUnicast->Next; + } + printf("\tNumber of Unicast Addresses: %d\n", i); + } + pAnycast = pCurrAddresses->FirstAnycastAddress; + if (pAnycast) { + for (i = 0; pAnycast != NULL; i++) { + printf("\tAnycast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pAnycast->Address.lpSockaddr)->sin_addr) ); + pAnycast = pAnycast->Next; + } + printf("\tNumber of Anycast Addresses: %d\n", i); + } + pMulticast = pCurrAddresses->FirstMulticastAddress; + if (pMulticast) { + for (i = 0; pMulticast != NULL; i++) { + printf("\tMulticast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pMulticast->Address.lpSockaddr)->sin_addr) ); + pMulticast = pMulticast->Next; + } + } + printf("\n"); +#endif + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pCurrAddresses->IfIndex == ifbestidx && pUnicast != NULL) { + SOCKADDR_IN *ipv4 = (SOCKADDR_IN *)(pUnicast->Address.lpSockaddr); + /* Set the address of this interface to be used */ + struct in_addr mc_if; + memset(&mc_if, 0, sizeof(mc_if)); + mc_if.s_addr = ipv4->sin_addr.s_addr; + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) { + PRINT_SOCKET_ERROR("setsockopt"); + } + ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = ipv4->sin_addr.s_addr; +#ifndef DEBUG + break; +#endif + } + pCurrAddresses = pCurrAddresses->Next; + } + } + if (pAddresses != NULL) { + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; + } + } + } +#endif /* _WIN32 */ + +#ifdef _WIN32 + if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0) +#else + if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0) +#endif + { + if(error) + *error = MINISSDPC_SOCKET_ERROR; + PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)"); + goto error; + } + + if(ipv6) { +#ifdef _WIN32 + DWORD mcastHops = ttl; + if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0) +#else /* _WIN32 */ + int mcastHops = ttl; + if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0) +#endif /* _WIN32 */ + { + PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)"); + } + } else { +#ifdef _WIN32 + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0) +#else /* _WIN32 */ + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) +#endif /* _WIN32 */ + { + /* not a fatal error */ + PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)"); + } + } + + if(multicastif && multicastif[0] != '\0') + { + if(ipv6) { +#if !defined(_WIN32) + /* according to MSDN, if_nametoindex() is supported since + * MS Windows Vista and MS Windows Server 2008. + * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */ + unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */ + if(ifindex == 0) + { + if(error) + *error = MINISSDPC_INVALID_INPUT; + fprintf(stderr, "Invalid multicast interface name %s\n", multicastif); + goto error; + } + if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0) + { + PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF"); + } +#else +#ifdef DEBUG + printf("Setting of multicast interface not supported in IPv6 under Windows.\n"); +#endif +#endif + } else { + struct in_addr mc_if; +#if defined(_WIN32) +#if _WIN32_WINNT >= 0x0600 // _WIN32_WINNT_VISTA + InetPtonA(AF_INET, multicastif, &mc_if); +#else + mc_if.s_addr = inet_addr(multicastif); /* old Windows SDK do not support InetPtoA() */ +#endif +#else + /* was : mc_if.s_addr = inet_addr(multicastif); */ /* ex: 192.168.x.x */ + if (inet_pton(AF_INET, multicastif, &mc_if.s_addr) <= 0) { + mc_if.s_addr = INADDR_NONE; + } +#endif + if(mc_if.s_addr != INADDR_NONE) + { + ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr; + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) + { + PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); + } + } else { + /* was not an ip address, try with an interface name */ +#ifndef _WIN32 +#ifdef HAS_IP_MREQN + struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */ +#endif + struct ifreq ifr; + int ifrlen = sizeof(ifr); + strncpy(ifr.ifr_name, multicastif, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = '\0'; + if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0) + { + PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)"); + goto error; + } + mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; +#ifdef HAS_IP_MREQN + memset(&reqn, 0, sizeof(struct ip_mreqn)); + reqn.imr_address.s_addr = mc_if.s_addr; + reqn.imr_ifindex = if_nametoindex(multicastif); + if(reqn.imr_ifindex == 0) + { + if(error) + *error = MINISSDPC_INVALID_INPUT; + fprintf(stderr, "Invalid multicast ip address / interface name %s\n", multicastif); + goto error; + } + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0) + { + PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); + } +#else + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) + { + PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); + } +#endif +#else /* _WIN32 */ +#ifdef DEBUG + printf("Setting of multicast interface not supported with interface name.\n"); +#endif +#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */ + } + } + } + + /* Before sending the packed, we first "bind" in order to be able + * to receive the response */ + if (bind(sudp, (const struct sockaddr *)&sockudp_r, + ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0) + { + if(error) + *error = MINISSDPC_SOCKET_ERROR; + PRINT_SOCKET_ERROR("bind"); + closesocket(sudp); + return NULL; + } + + if(error) + *error = MINISSDPC_SUCCESS; + /* Calculating maximum response time in seconds */ + mx = ((unsigned int)delay) / 1000u; + if(mx == 0) { + mx = 1; + delay = 1000; + } + /* receiving SSDP response packet */ + for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) { + sentok = 0; + /* sending the SSDP M-SEARCH packet */ + n = snprintf(bufr, sizeof(bufr), + MSearchMsgFmt, + ipv6 ? + (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]") + : UPNP_MCAST_ADDR, + deviceTypes[deviceIndex], mx); + if ((unsigned int)n >= sizeof(bufr)) { + if(error) + *error = MINISSDPC_MEMORY_ERROR; + goto error; + } +#ifdef DEBUG + /*printf("Sending %s", bufr);*/ + printf("Sending M-SEARCH request to %s with ST: %s\n", + ipv6 ? + (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]") + : UPNP_MCAST_ADDR, + deviceTypes[deviceIndex]); +#endif +#ifdef NO_GETADDRINFO + /* the following code is not using getaddrinfo */ + /* emission */ + memset(&sockudp_w, 0, sizeof(struct sockaddr_storage)); + if(ipv6) { + struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w; + p->sin6_family = AF_INET6; + p->sin6_port = htons(SSDP_PORT); + inet_pton(AF_INET6, + linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR, + &(p->sin6_addr)); + } else { + struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w; + p->sin_family = AF_INET; + p->sin_port = htons(SSDP_PORT); + p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR); + } + n = sendto(sudp, bufr, n, 0, &sockudp_w, + ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); + if (n < 0) { + if(error) + *error = MINISSDPC_SOCKET_ERROR; + PRINT_SOCKET_ERROR("sendto"); + } else { + sentok = 1; + } +#else /* #ifdef NO_GETADDRINFO */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */ + hints.ai_socktype = SOCK_DGRAM; + /*hints.ai_flags = */ + if ((rv = getaddrinfo(ipv6 + ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR) + : UPNP_MCAST_ADDR, + XSTR(SSDP_PORT), &hints, &servinfo)) != 0) { + if(error) + *error = MINISSDPC_SOCKET_ERROR; +#ifdef _WIN32 + fprintf(stderr, "getaddrinfo() failed: %d\n", rv); +#else + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); +#endif + break; + } else { + struct addrinfo *p; + for(p = servinfo; p; p = p->ai_next) { + n = sendto(sudp, bufr, n, 0, p->ai_addr, MSC_CAST_INT p->ai_addrlen); + if (n < 0) { +#ifdef DEBUG + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + if (getnameinfo(p->ai_addr, (socklen_t)p->ai_addrlen, hbuf, sizeof(hbuf), sbuf, + sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf); + } +#endif + PRINT_SOCKET_ERROR("sendto"); + continue; + } else { + sentok = 1; + } + } + freeaddrinfo(servinfo); + } + if(!sentok) { + if(error) + *error = MINISSDPC_SOCKET_ERROR; + } +#endif /* #ifdef NO_GETADDRINFO */ + /* Waiting for SSDP REPLY packet to M-SEARCH + * if searchalltypes is set, enter the loop only + * when the last deviceType is reached */ + if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) { + struct timeval start = {0, 0}, current = {0, 0}; + upnp_gettimeofday(&start); + do { + n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id); + if (n < 0) { + /* error */ + if(error) + *error = MINISSDPC_SOCKET_ERROR; + goto error; + } else if (n == 0) { + /* no data or Time Out */ +#ifdef DEBUG + printf("NODATA or TIMEOUT\n"); +#endif /* DEBUG */ + if (devlist && !searchalltypes) { + /* found some devices, stop now*/ + if(error) + *error = MINISSDPC_SUCCESS; + goto error; + } + } else { + const char * descURL=NULL; + int urlsize=0; + const char * st=NULL; + int stsize=0; + const char * usn=NULL; + int usnsize=0; + parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize); + if(st&&descURL) { +#ifdef DEBUG + printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n", + stsize, st, usnsize, (usn?usn:""), urlsize, descURL); +#endif /* DEBUG */ + for(tmp=devlist; tmp; tmp = tmp->pNext) { + if(strncmp(tmp->descURL, descURL, urlsize) == 0 && + tmp->descURL[urlsize] == '\0' && + strncmp(tmp->st, st, stsize) == 0 && + tmp->st[stsize] == '\0' && + (usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) && + tmp->usn[usnsize] == '\0') + break; + } + /* at the exit of the loop above, tmp is null if + * no duplicate device was found */ + if(tmp) + continue; + tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3); + if(!tmp) { + /* memory allocation error */ + if(error) + *error = MINISSDPC_MEMORY_ERROR; + goto error; + } + tmp->pNext = devlist; + tmp->descURL = tmp->buffer; + tmp->st = tmp->buffer + 1 + urlsize; + tmp->usn = tmp->st + 1 + stsize; + memcpy(tmp->buffer, descURL, urlsize); + tmp->buffer[urlsize] = '\0'; + memcpy(tmp->st, st, stsize); + tmp->buffer[urlsize+1+stsize] = '\0'; + if(usn != NULL) + memcpy(tmp->usn, usn, usnsize); + tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0'; + tmp->scope_id = scope_id; + devlist = tmp; + } + if (upnp_gettimeofday(¤t) >= 0) { + /* exit the loop if delay is reached */ + long interval = (current.tv_sec - start.tv_sec) * 1000; + interval += (current.tv_usec - start.tv_usec) / 1000; + if (interval > (long)delay) + break; + } + } + } while(n > 0); + } + if(ipv6) { + /* switch linklocal flag */ + if(linklocal) { + linklocal = 0; + --deviceIndex; + } else { + linklocal = 1; + } + } + } +error: + closesocket(sudp); + return devlist; +} diff --git a/libs/miniupnpc/src/minissdpc.h b/libs/miniupnpc/src/minissdpc.h new file mode 100644 index 000000000..04cdbf82c --- /dev/null +++ b/libs/miniupnpc/src/minissdpc.h @@ -0,0 +1,58 @@ +/* $Id: minissdpc.h,v 1.8 2019/02/10 12:29:23 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2015 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef MINISSDPC_H_INCLUDED +#define MINISSDPC_H_INCLUDED + +#include "miniupnpc_declspec.h" +#include "upnpdev.h" + +/* error codes : */ +#define MINISSDPC_SUCCESS (0) +#define MINISSDPC_UNKNOWN_ERROR (-1) +#define MINISSDPC_SOCKET_ERROR (-101) +#define MINISSDPC_MEMORY_ERROR (-102) +#define MINISSDPC_INVALID_INPUT (-103) +#define MINISSDPC_INVALID_SERVER_REPLY (-104) + +#ifdef __cplusplus +extern "C" { +#endif + +#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) + +MINIUPNP_LIBSPEC struct UPNPDev * +getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error); + +MINIUPNP_LIBSPEC int +connectToMiniSSDPD(const char * socketpath); + +MINIUPNP_LIBSPEC int +disconnectFromMiniSSDPD(int s); + +MINIUPNP_LIBSPEC int +requestDevicesFromMiniSSDPD(int s, const char * devtype); + +MINIUPNP_LIBSPEC struct UPNPDev * +receiveDevicesFromMiniSSDPD(int s, int * error); + +#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */ + +MINIUPNP_LIBSPEC struct UPNPDev * +ssdpDiscoverDevices(const char * const deviceTypes[], + int delay, const char * multicastif, + int localport, + int ipv6, unsigned char ttl, + int * error, + int searchalltypes); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/libs/miniupnpc/src/miniupnpc.c b/libs/miniupnpc/src/miniupnpc.c new file mode 100644 index 000000000..696af9323 --- /dev/null +++ b/libs/miniupnpc/src/miniupnpc.c @@ -0,0 +1,698 @@ +/* $Id: miniupnpc.c,v 1.159 2021/03/02 23:36:32 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas BERNARD + * copyright (c) 2005-2021 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#include +#include +#include +#ifdef _WIN32 +/* Win32 Specific includes and defines */ +#include +#include +#include +#include +#include "win32_snprintf.h" +#define strdup _strdup +#ifndef strncasecmp +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define strncasecmp _memicmp +#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ +#define strncasecmp memicmp +#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ +#endif /* #ifndef strncasecmp */ +#define MAXHOSTNAMELEN 64 +#else /* #ifdef _WIN32 */ +/* Standard POSIX includes */ +#include +#if defined(__amigaos__) && !defined(__amigaos4__) +/* Amiga OS 3 specific stuff */ +#define socklen_t int +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#if !defined(__amigaos__) && !defined(__amigaos4__) +#include +#endif +#include +#include +#define closesocket close +#endif /* #else _WIN32 */ +#ifdef __GNU__ +#define MAXHOSTNAMELEN 64 +#endif + + +#include "miniupnpc.h" +#include "minissdpc.h" +#include "miniwget.h" +#include "miniwget_private.h" +#include "minisoap.h" +#include "minixml.h" +#include "upnpcommands.h" +#include "connecthostport.h" +#include "addr_is_reserved.h" + +/* compare the beginning of a string with a constant string */ +#define COMPARE(str, cstr) (0==strncmp(str, cstr, sizeof(cstr) - 1)) + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#define SOAPPREFIX "s" +#define SERVICEPREFIX "u" +#define SERVICEPREFIX2 'u' + +/* root description parsing */ +MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data) +{ + struct xmlparser parser; + /* xmlparser object */ + parser.xmlstart = buffer; + parser.xmlsize = bufsize; + parser.data = data; + parser.starteltfunc = IGDstartelt; + parser.endeltfunc = IGDendelt; + parser.datafunc = IGDdata; + parser.attfunc = 0; + parsexml(&parser); +#ifdef DEBUG + printIGD(data); +#endif +} + +/* simpleUPnPcommand2 : + * not so simple ! + * return values : + * pointer - OK + * NULL - error */ +static char * +simpleUPnPcommand2(SOCKET s, const char * url, const char * service, + const char * action, struct UPNParg * args, + int * bufsize, const char * httpversion) +{ + char hostname[MAXHOSTNAMELEN+1]; + unsigned short port = 0; + char * path; + char soapact[128]; + char soapbody[2048]; + int soapbodylen; + char * buf; + int n; + int status_code; + + *bufsize = 0; + snprintf(soapact, sizeof(soapact), "%s#%s", service, action); + if(args==NULL) + { + soapbodylen = snprintf(soapbody, sizeof(soapbody), + "\r\n" + "<" SOAPPREFIX ":Envelope " + "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" " + SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<" SOAPPREFIX ":Body>" + "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">" + "" + "" + "\r\n", action, service, action); + if ((unsigned int)soapbodylen >= sizeof(soapbody)) + return NULL; + } + else + { + char * p; + const char * pe, * pv; + const char * const pend = soapbody + sizeof(soapbody); + soapbodylen = snprintf(soapbody, sizeof(soapbody), + "\r\n" + "<" SOAPPREFIX ":Envelope " + "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" " + SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<" SOAPPREFIX ":Body>" + "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">", + action, service); + if ((unsigned int)soapbodylen >= sizeof(soapbody)) + return NULL; + p = soapbody + soapbodylen; + while(args->elt) + { + if(p >= pend) /* check for space to write next byte */ + return NULL; + *(p++) = '<'; + + pe = args->elt; + while(p < pend && *pe) + *(p++) = *(pe++); + + if(p >= pend) /* check for space to write next byte */ + return NULL; + *(p++) = '>'; + + if((pv = args->val)) + { + while(p < pend && *pv) + *(p++) = *(pv++); + } + + if((p+2) > pend) /* check for space to write next 2 bytes */ + return NULL; + *(p++) = '<'; + *(p++) = '/'; + + pe = args->elt; + while(p < pend && *pe) + *(p++) = *(pe++); + + if(p >= pend) /* check for space to write next byte */ + return NULL; + *(p++) = '>'; + + args++; + } + if((p+4) > pend) /* check for space to write next 4 bytes */ + return NULL; + *(p++) = '<'; + *(p++) = '/'; + *(p++) = SERVICEPREFIX2; + *(p++) = ':'; + + pe = action; + while(p < pend && *pe) + *(p++) = *(pe++); + + strncpy(p, ">\r\n", + pend - p); + if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */ + return NULL; + } + if(!parseURL(url, hostname, &port, &path, NULL)) return NULL; + if(ISINVALID(s)) { + s = connecthostport(hostname, port, 0); + if(ISINVALID(s)) { + /* failed to connect */ + return NULL; + } + } + + n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion); + if(n<=0) { +#ifdef DEBUG + printf("Error sending SOAP request\n"); +#endif + closesocket(s); + return NULL; + } + + buf = getHTTPResponse(s, bufsize, &status_code); +#ifdef DEBUG + if(*bufsize > 0 && buf) + { + printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf); + } + else + { + printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize); + } +#endif + closesocket(s); + return buf; +} + +/* simpleUPnPcommand : + * not so simple ! + * return values : + * pointer - OK + * NULL - error */ +char * +simpleUPnPcommand(int s, const char * url, const char * service, + const char * action, struct UPNParg * args, + int * bufsize) +{ + char * buf; + +#if 1 + buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1"); +#else + buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.0"); + if (!buf || *bufsize == 0) + { +#if DEBUG + printf("Error or no result from SOAP request; retrying with HTTP/1.1\n"); +#endif + buf = simpleUPnPcommand2((SOCKET)s, url, service, action, args, bufsize, "1.1"); + } +#endif + return buf; +} + +/* upnpDiscoverDevices() : + * return a chained list of all devices found or NULL if + * no devices was found. + * It is up to the caller to free the chained list + * delay is in millisecond (poll). + * UDA v1.1 says : + * The TTL for the IP packet SHOULD default to 2 and + * SHOULD be configurable. */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevices(const char * const deviceTypes[], + int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error, + int searchalltypes) +{ + struct UPNPDev * tmp; + struct UPNPDev * devlist = 0; +#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) + int deviceIndex; +#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ + + if(error) + *error = UPNPDISCOVER_UNKNOWN_ERROR; +#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) + /* first try to get infos from minissdpd ! */ + if(!minissdpdsock) + minissdpdsock = "/var/run/minissdpd.sock"; + if(minissdpdsock[0] != '\0') { + for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) { + struct UPNPDev * minissdpd_devlist; + int only_rootdevice = 1; + minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex], + minissdpdsock, 0); + if(minissdpd_devlist) { +#ifdef DEBUG + printf("returned by MiniSSDPD: %s\t%s\n", + minissdpd_devlist->st, minissdpd_devlist->descURL); +#endif /* DEBUG */ + if(!strstr(minissdpd_devlist->st, "rootdevice")) + only_rootdevice = 0; + for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) { +#ifdef DEBUG + printf("returned by MiniSSDPD: %s\t%s\n", + tmp->pNext->st, tmp->pNext->descURL); +#endif /* DEBUG */ + if(!strstr(tmp->st, "rootdevice")) + only_rootdevice = 0; + } + tmp->pNext = devlist; + devlist = minissdpd_devlist; + if(!searchalltypes && !only_rootdevice) + break; + } + } + } + for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) { + /* We return what we have found if it was not only a rootdevice */ + if(!strstr(tmp->st, "rootdevice")) { + if(error) + *error = UPNPDISCOVER_SUCCESS; + return devlist; + } + } +#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ + (void)minissdpdsock; /* unused */ +#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ + + /* direct discovery if minissdpd responses are not sufficient */ + { + struct UPNPDev * discovered_devlist; + discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport, + ipv6, ttl, error, searchalltypes); + if(devlist == NULL) + devlist = discovered_devlist; + else { + for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext); + tmp->pNext = discovered_devlist; + } + } + return devlist; +} + +/* upnpDiscover() Discover IGD device */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscover(int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error) +{ + static const char * const deviceList[] = { +#if 0 + "urn:schemas-upnp-org:device:InternetGatewayDevice:2", + "urn:schemas-upnp-org:service:WANIPConnection:2", +#endif + "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + "urn:schemas-upnp-org:service:WANIPConnection:1", + "urn:schemas-upnp-org:service:WANPPPConnection:1", + "upnp:rootdevice", + /*"ssdp:all",*/ + 0 + }; + return upnpDiscoverDevices(deviceList, + delay, multicastif, minissdpdsock, localport, + ipv6, ttl, error, 0); +} + +/* upnpDiscoverAll() Discover all UPnP devices */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverAll(int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error) +{ + static const char * const deviceList[] = { + /*"upnp:rootdevice",*/ + "ssdp:all", + 0 + }; + return upnpDiscoverDevices(deviceList, + delay, multicastif, minissdpdsock, localport, + ipv6, ttl, error, 0); +} + +/* upnpDiscoverDevice() Discover a specific device */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevice(const char * device, int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error) +{ + const char * const deviceList[] = { + device, + 0 + }; + return upnpDiscoverDevices(deviceList, + delay, multicastif, minissdpdsock, localport, + ipv6, ttl, error, 0); +} + +static char * +build_absolute_url(const char * baseurl, const char * descURL, + const char * url, unsigned int scope_id) +{ + size_t l, n; + char * s; + const char * base; + char * p; +#if defined(IF_NAMESIZE) && !defined(_WIN32) + char ifname[IF_NAMESIZE]; +#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */ + char scope_str[8]; +#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */ + + if( (url[0] == 'h') + &&(url[1] == 't') + &&(url[2] == 't') + &&(url[3] == 'p') + &&(url[4] == ':') + &&(url[5] == '/') + &&(url[6] == '/')) + return strdup(url); + base = (baseurl[0] == '\0') ? descURL : baseurl; + n = strlen(base); + if(n > 7) { + p = strchr(base + 7, '/'); + if(p) + n = p - base; + } + l = n + strlen(url) + 1; + if(url[0] != '/') + l++; + if(scope_id != 0) { +#if defined(IF_NAMESIZE) && !defined(_WIN32) + if(if_indextoname(scope_id, ifname)) { + l += 3 + strlen(ifname); /* 3 == strlen(%25) */ + } +#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */ + /* under windows, scope is numerical */ + l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id); +#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */ + } + s = malloc(l); + if(s == NULL) return NULL; + memcpy(s, base, n); + if(scope_id != 0) { + s[n] = '\0'; + if(n > 13 && 0 == memcmp(s, "http://[fe80:", 13)) { + /* this is a linklocal IPv6 address */ + p = strchr(s, ']'); + if(p) { + /* insert %25 into URL */ +#if defined(IF_NAMESIZE) && !defined(_WIN32) + memmove(p + 3 + strlen(ifname), p, strlen(p) + 1); + memcpy(p, "%25", 3); + memcpy(p + 3, ifname, strlen(ifname)); + n += 3 + strlen(ifname); +#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */ + memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1); + memcpy(p, "%25", 3); + memcpy(p + 3, scope_str, strlen(scope_str)); + n += 3 + strlen(scope_str); +#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */ + } + } + } + if(url[0] != '/') + s[n++] = '/'; + memcpy(s + n, url, l - n); + return s; +} + +/* Prepare the Urls for usage... + */ +MINIUPNP_LIBSPEC void +GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data, + const char * descURL, unsigned int scope_id) +{ + /* strdup descURL */ + urls->rootdescURL = strdup(descURL); + + /* get description of WANIPConnection */ + urls->ipcondescURL = build_absolute_url(data->urlbase, descURL, + data->first.scpdurl, scope_id); + urls->controlURL = build_absolute_url(data->urlbase, descURL, + data->first.controlurl, scope_id); + urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL, + data->CIF.controlurl, scope_id); + urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL, + data->IPv6FC.controlurl, scope_id); + +#ifdef DEBUG + printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL); + printf("urls->controlURL='%s'\n", urls->controlURL); + printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF); + printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC); +#endif +} + +MINIUPNP_LIBSPEC void +FreeUPNPUrls(struct UPNPUrls * urls) +{ + if(!urls) + return; + free(urls->controlURL); + urls->controlURL = 0; + free(urls->ipcondescURL); + urls->ipcondescURL = 0; + free(urls->controlURL_CIF); + urls->controlURL_CIF = 0; + free(urls->controlURL_6FC); + urls->controlURL_6FC = 0; + free(urls->rootdescURL); + urls->rootdescURL = 0; +} + +int +UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data) +{ + char status[64]; + unsigned int uptime; + status[0] = '\0'; + UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype, + status, &uptime, NULL); + if(0 == strcmp("Connected", status)) + return 1; + else if(0 == strcmp("Up", status)) /* Also accept "Up" */ + return 1; + else + return 0; +} + + +/* UPNP_GetValidIGD() : + * return values : + * -1 = Internal error + * 0 = NO IGD found + * 1 = A valid connected IGD has been found + * 2 = A valid IGD has been found but it reported as + * not connected + * 3 = an UPnP device has been found but was not recognized as an IGD + * + * In any positive non zero return case, the urls and data structures + * passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to + * free allocated memory. + */ +MINIUPNP_LIBSPEC int +UPNP_GetValidIGD(struct UPNPDev * devlist, + struct UPNPUrls * urls, + struct IGDdatas * data, + char * lanaddr, int lanaddrlen) +{ + struct xml_desc { + char lanaddr[40]; + char * xml; + int size; + int is_igd; + } * desc = NULL; + struct UPNPDev * dev; + int ndev = 0; + int i; + int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ + char extIpAddr[16]; + int status_code = -1; + + if(!devlist) + { +#ifdef DEBUG + printf("Empty devlist\n"); +#endif + return 0; + } + /* counting total number of devices in the list */ + for(dev = devlist; dev; dev = dev->pNext) + ndev++; + /* ndev is always > 0 */ + desc = calloc(ndev, sizeof(struct xml_desc)); + if(!desc) + return -1; /* memory allocation error */ + /* Step 1 : downloading descriptions and testing type */ + for(dev = devlist, i = 0; dev; dev = dev->pNext, i++) + { + /* we should choose an internet gateway device. + * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */ + desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size), + desc[i].lanaddr, sizeof(desc[i].lanaddr), + dev->scope_id, &status_code); +#ifdef DEBUG + if(!desc[i].xml) + { + printf("error getting XML description %s\n", dev->descURL); + } +#endif + if(desc[i].xml) + { + memset(data, 0, sizeof(struct IGDdatas)); + memset(urls, 0, sizeof(struct UPNPUrls)); + parserootdesc(desc[i].xml, desc[i].size, data); + if(COMPARE(data->CIF.servicetype, + "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) + { + desc[i].is_igd = 1; + } + } + } + /* iterate the list to find a device depending on state */ + for(state = 1; state <= 3; state++) + { + for(dev = devlist, i = 0; dev; dev = dev->pNext, i++) + { + if(desc[i].xml) + { + memset(data, 0, sizeof(struct IGDdatas)); + memset(urls, 0, sizeof(struct UPNPUrls)); + parserootdesc(desc[i].xml, desc[i].size, data); + if(desc[i].is_igd || state >= 3 ) + { + int is_connected; + + GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); + + /* in state 2 and 3 we don't test if device is connected ! */ + if(state >= 2) + goto free_and_return; + is_connected = UPNPIGD_IsConnected(urls, data); +#ifdef DEBUG + printf("UPNPIGD_IsConnected(%s) = %d\n", + urls->controlURL, is_connected); +#endif + /* checks that status is connected AND there is a external IP address assigned */ + if(is_connected && + (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) { + if(!addr_is_reserved(extIpAddr)) + goto free_and_return; + } + FreeUPNPUrls(urls); + if(data->second.servicetype[0] != '\0') { +#ifdef DEBUG + printf("We tried %s, now we try %s !\n", + data->first.servicetype, data->second.servicetype); +#endif + /* swaping WANPPPConnection and WANIPConnection ! */ + memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service)); + memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service)); + memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service)); + GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); + is_connected = UPNPIGD_IsConnected(urls, data); +#ifdef DEBUG + printf("UPNPIGD_IsConnected(%s) = %d\n", + urls->controlURL, is_connected); +#endif + if(is_connected && + (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) { + if(!addr_is_reserved(extIpAddr)) + goto free_and_return; + } + FreeUPNPUrls(urls); + } + } + memset(data, 0, sizeof(struct IGDdatas)); + } + } + } + state = 0; +free_and_return: + if (lanaddr != NULL && state >= 1 && state <= 3 && i < ndev) + strncpy(lanaddr, desc[i].lanaddr, lanaddrlen); + for(i = 0; i < ndev; i++) + free(desc[i].xml); + free(desc); + return state; +} + +/* UPNP_GetIGDFromUrl() + * Used when skipping the discovery process. + * return value : + * 0 - Not ok + * 1 - OK */ +int +UPNP_GetIGDFromUrl(const char * rootdescurl, + struct UPNPUrls * urls, + struct IGDdatas * data, + char * lanaddr, int lanaddrlen) +{ + char * descXML; + int descXMLsize = 0; + + descXML = miniwget_getaddr(rootdescurl, &descXMLsize, + lanaddr, lanaddrlen, 0, NULL); + if(descXML) { + memset(data, 0, sizeof(struct IGDdatas)); + memset(urls, 0, sizeof(struct UPNPUrls)); + parserootdesc(descXML, descXMLsize, data); + free(descXML); + GetUPNPUrls(urls, data, rootdescurl, 0); + return 1; + } else { + return 0; + } +} diff --git a/libs/miniupnpc/src/miniupnpc_socketdef.h b/libs/miniupnpc/src/miniupnpc_socketdef.h new file mode 100644 index 000000000..2d5cac5c2 --- /dev/null +++ b/libs/miniupnpc/src/miniupnpc_socketdef.h @@ -0,0 +1,44 @@ +/* $Id: miniupnpc_socketdef.h,v 1.4 2021/03/02 23:35:29 nanard Exp $ */ +/* Miniupnp project : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas Bernard + * Copyright (c) 2018 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided within this distribution */ +#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED +#define MINIUPNPC_SOCKETDEF_H_INCLUDED + +#ifdef _WIN32 + +#define ISINVALID(s) (INVALID_SOCKET==(s)) + +#else + +#ifndef SOCKET +#define SOCKET int +#endif +#ifndef SSIZE_T +#define SSIZE_T ssize_t +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#ifndef ISINVALID +#define ISINVALID(s) ((s)<0) +#endif + +#endif + +#ifdef _MSC_VER +#define MSC_CAST_INT (int) +#else +#define MSC_CAST_INT +#endif + +/* definition of PRINT_SOCKET_ERROR */ +#ifdef _WIN32 +#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError()); +#else +#define PRINT_SOCKET_ERROR(x) perror(x) +#endif + +#endif /* MINIUPNPC_SOCKETDEF_H_INCLUDED */ diff --git a/libs/miniupnpc/miniupnpcmodule.c b/libs/miniupnpc/src/miniupnpcmodule.c similarity index 61% rename from libs/miniupnpc/miniupnpcmodule.c rename to libs/miniupnpc/src/miniupnpcmodule.c index 08a61d22f..66a267073 100644 --- a/libs/miniupnpc/miniupnpcmodule.c +++ b/libs/miniupnpc/src/miniupnpcmodule.c @@ -1,17 +1,22 @@ -/* $Id: miniupnpcmodule.c,v 1.18 2011/04/10 11:21:23 nanard Exp $*/ -/* Project : miniupnp +/* $Id: miniupnpcmodule.c,v 1.38 2021/11/09 18:46:49 nanard Exp $*/ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas BERNARD - * website : http://miniupnp.tuxfamily.org/ - * copyright (c) 2007-2009 Thomas Bernard + * website : https://miniupnp.tuxfamily.org/ + * copyright (c) 2007-2021 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include -#define STATICLIB -#include "structmember.h" +#define MINIUPNP_STATICLIB +#include #include "miniupnpc.h" #include "upnpcommands.h" #include "upnperrors.h" +#ifdef _WIN32 +#include +#endif + /* for compatibility with Python < 2.4 */ #ifndef Py_RETURN_NONE #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None @@ -25,6 +30,16 @@ #define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False #endif +/* for compatibility with Python < 3.0 */ +#ifndef PyVarObject_HEAD_INIT +#define PyVarObject_HEAD_INIT(type, size) \ + PyObject_HEAD_INIT(type) size, +#endif + +#ifndef Py_TYPE +#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) +#endif + typedef struct { PyObject_HEAD /* Type-specific fields go here. */ @@ -32,6 +47,7 @@ typedef struct { struct UPNPUrls urls; struct IGDdatas data; unsigned int discoverdelay; /* value passed to upnpDiscover() */ + unsigned int localport; /* value passed to upnpDiscover() */ char lanaddr[40]; /* our ip address on the LAN */ char * multicastif; char * minissdpdsocket; @@ -44,57 +60,115 @@ static PyMemberDef UPnP_members[] = { {"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay), 0/*READWRITE*/, "value in ms used to wait for SSDP responses" }, - /* T_STRING is allways readonly :( */ + {"localport", T_UINT, offsetof(UPnPObject, localport), + 0/*READWRITE*/, + "If localport is set to UPNP_LOCAL_PORT_SAME(1) " + "SSDP packets will be sent from the source port " + "1900 (same as destination port), if set to " + "UPNP_LOCAL_PORT_ANY(0) system assign a source " + "port, any other value will be attempted as the " + "source port" + }, + /* T_STRING is always readonly :( */ {"multicastif", T_STRING, offsetof(UPnPObject, multicastif), 0, "IP of the network interface to be used for multicast operations" }, - {"minissdpdsocket", T_STRING, offsetof(UPnPObject, multicastif), + {"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket), 0, "path of the MiniSSDPd unix socket" }, {NULL} }; + +static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds) +{ + char* multicastif = NULL; + char* minissdpdsocket = NULL; + static char *kwlist[] = { + "multicastif", "minissdpdsocket", "discoverdelay", + "localport", NULL + }; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist, + &multicastif, + &minissdpdsocket, + &self->discoverdelay, + &self->localport)) + return -1; + + if(self->localport>1 && + (self->localport>65534||self->localport<1024)) { + PyErr_SetString(PyExc_Exception, "Invalid localport value"); + return -1; + } + if(multicastif) + self->multicastif = strdup(multicastif); + if(minissdpdsocket) + self->minissdpdsocket = strdup(minissdpdsocket); + + return 0; +} + static void UPnPObject_dealloc(UPnPObject *self) { freeUPNPDevlist(self->devlist); FreeUPNPUrls(&self->urls); - self->ob_type->tp_free((PyObject*)self); + free(self->multicastif); + free(self->minissdpdsocket); + Py_TYPE(self)->tp_free((PyObject*)self); } static PyObject * UPnP_discover(UPnPObject *self) { - struct UPNPDev * dev; - int i; + int error = 0; PyObject *res = NULL; + if(self->devlist) { freeUPNPDevlist(self->devlist); self->devlist = 0; - } + } Py_BEGIN_ALLOW_THREADS self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/, - 0/* multicast if*/, - 0/*minissdpd socket*/, - 0/*sameport flag*/, + self->multicastif, + self->minissdpdsocket, + (int)self->localport, 0/*ip v6*/, - 0/*error */); + 2/* TTL */, + &error); Py_END_ALLOW_THREADS /* Py_RETURN_NONE ??? */ - for(dev = self->devlist, i = 0; dev; dev = dev->pNext) - i++; - res = Py_BuildValue("i", i); - return res; + if (self->devlist != NULL) { + struct UPNPDev * dev; + int i = 0; + + for(dev = self->devlist; dev; dev = dev->pNext) + i++; + res = Py_BuildValue("i", i); + return res; + } else { + PyErr_SetString(PyExc_Exception, strupnperror(error)); + return NULL; + } } static PyObject * -UPnP_selectigd(UPnPObject *self) +UPnP_selectigd(UPnPObject *self, PyObject *args) { + const char * rootDescUrl = NULL; int r; + if(!PyArg_ParseTuple(args, "|z", &rootDescUrl)) + return NULL; Py_BEGIN_ALLOW_THREADS - r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data, - self->lanaddr, sizeof(self->lanaddr)); + if (rootDescUrl == NULL) { + r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data, + self->lanaddr, sizeof(self->lanaddr)); + } else { + r = UPNP_GetIGDFromUrl(rootDescUrl, &self->urls, &self->data, + self->lanaddr, sizeof(self->lanaddr)); + } Py_END_ALLOW_THREADS if(r) { @@ -116,7 +190,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -127,7 +205,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -138,7 +220,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -149,7 +235,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -166,7 +256,11 @@ Py_BEGIN_ALLOW_THREADS status, &uptime, lastconnerror); Py_END_ALLOW_THREADS if(r==UPNPCOMMAND_SUCCESS) { +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror); +#else + return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror); +#endif } else { /* TODO: have our own exception type ! */ PyErr_SetString(PyExc_Exception, strupnperror(r)); @@ -215,7 +309,7 @@ Py_END_ALLOW_THREADS } /* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc, - * remoteHost) + * remoteHost, leaseDuration) * protocol is 'UDP' or 'TCP' */ static PyObject * UPnP_addportmapping(UPnPObject *self, PyObject *args) @@ -228,17 +322,24 @@ UPnP_addportmapping(UPnPObject *self, PyObject *args) const char * host; const char * desc; const char * remoteHost; - const char * leaseDuration = "0"; + unsigned int intLeaseDuration = 0; + char strLeaseDuration[12]; int r; - if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, - &host, &iPort, &desc, &remoteHost)) +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) + if (!PyArg_ParseTuple(args, "HssHzz|I", &ePort, &proto, + &host, &iPort, &desc, &remoteHost, &intLeaseDuration)) +#else + if (!PyArg_ParseTuple(args, "HssHzz|i", &ePort, &proto, + &host, &iPort, &desc, &remoteHost, (int *)&intLeaseDuration)) +#endif return NULL; Py_BEGIN_ALLOW_THREADS sprintf(extPort, "%hu", ePort); sprintf(inPort, "%hu", iPort); + sprintf(strLeaseDuration, "%u", intLeaseDuration); r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype, extPort, inPort, host, desc, proto, - remoteHost, leaseDuration); + remoteHost, strLeaseDuration); Py_END_ALLOW_THREADS if(r==UPNPCOMMAND_SUCCESS) { @@ -255,6 +356,42 @@ Py_END_ALLOW_THREADS } } +/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc, + * remoteHost) + * protocol is 'UDP' or 'TCP' */ +static PyObject * +UPnP_addanyportmapping(UPnPObject *self, PyObject *args) +{ + char extPort[6]; + unsigned short ePort; + char inPort[6]; + unsigned short iPort; + char reservedPort[6]; + const char * proto; + const char * host; + const char * desc; + const char * remoteHost; + const char * leaseDuration = "0"; + int r; + if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost)) + return NULL; +Py_BEGIN_ALLOW_THREADS + sprintf(extPort, "%hu", ePort); + sprintf(inPort, "%hu", iPort); + r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype, + extPort, inPort, host, desc, proto, + remoteHost, leaseDuration, reservedPort); +Py_END_ALLOW_THREADS + if(r==UPNPCOMMAND_SUCCESS) { + return Py_BuildValue("i", atoi(reservedPort)); + } else { + /* TODO: have our own exception type ! */ + PyErr_SetString(PyExc_Exception, strupnperror(r)); + return NULL; + } +} + + /* DeletePortMapping(extPort, proto, removeHost='') * proto = 'UDP', 'TCP' */ static PyObject * @@ -281,6 +418,37 @@ Py_END_ALLOW_THREADS } } +/* DeletePortMappingRange(extPort, proto, removeHost='') + * proto = 'UDP', 'TCP' */ +static PyObject * +UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args) +{ + char extPortStart[6]; + unsigned short ePortStart; + char extPortEnd[6]; + unsigned short ePortEnd; + const char * proto; + unsigned char manage; + char manageStr[6]; + int r; + if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage)) + return NULL; +Py_BEGIN_ALLOW_THREADS + sprintf(extPortStart, "%hu", ePortStart); + sprintf(extPortEnd, "%hu", ePortEnd); + sprintf(manageStr, "%hu", (unsigned short)manage); + r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype, + extPortStart, extPortEnd, proto, manageStr); +Py_END_ALLOW_THREADS + if(r==UPNPCOMMAND_SUCCESS) { + Py_RETURN_TRUE; + } else { + /* TODO: have our own exception type ! */ + PyErr_SetString(PyExc_Exception, strupnperror(r)); + return NULL; + } +} + static PyObject * UPnP_getportmappingnumberofentries(UPnPObject *self) { @@ -292,7 +460,11 @@ Py_BEGIN_ALLOW_THREADS &n); Py_END_ALLOW_THREADS if(r==UPNPCOMMAND_SUCCESS) { +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", n); +#else + return Py_BuildValue("i", (int)n); +#endif } else { /* TODO: have our own exception type ! */ PyErr_SetString(PyExc_Exception, strupnperror(r)); @@ -300,7 +472,7 @@ Py_END_ALLOW_THREADS } } -/* GetSpecificPortMapping(ePort, proto) +/* GetSpecificPortMapping(ePort, proto, remoteHost='') * proto = 'UDP' or 'TCP' */ static PyObject * UPnP_getspecificportmapping(UPnPObject *self, PyObject *args) @@ -308,13 +480,14 @@ UPnP_getspecificportmapping(UPnPObject *self, PyObject *args) char extPort[6]; unsigned short ePort; const char * proto; + const char * remoteHost = ""; char intClient[40]; char intPort[6]; unsigned short iPort; char desc[80]; char enabled[4]; char leaseDuration[16]; - if(!PyArg_ParseTuple(args, "Hs", &ePort, &proto)) + if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost)) return NULL; extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0'; desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0'; @@ -322,7 +495,7 @@ Py_BEGIN_ALLOW_THREADS sprintf(extPort, "%hu", ePort); UPNP_GetSpecificPortMappingEntry(self->urls.controlURL, self->data.first.servicetype, - extPort, proto, + extPort, proto, remoteHost, intClient, intPort, desc, enabled, leaseDuration); Py_END_ALLOW_THREADS @@ -376,9 +549,15 @@ Py_END_ALLOW_THREADS ePort = (unsigned short)atoi(extPort); iPort = (unsigned short)atoi(intPort); dur = (unsigned int)strtoul(duration, 0, 0); +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("(H,s,(s,H),s,s,s,I)", ePort, protocol, intClient, iPort, desc, enabled, rHost, dur); +#else + return Py_BuildValue("(i,s,(s,i),s,s,s,i)", + (int)ePort, protocol, intClient, (int)iPort, + desc, enabled, rHost, (int)dur); +#endif } else { @@ -391,7 +570,7 @@ static PyMethodDef UPnP_methods[] = { {"discover", (PyCFunction)UPnP_discover, METH_NOARGS, "discover UPnP IGD devices on the network" }, - {"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS, + {"selectigd", (PyCFunction)UPnP_selectigd, METH_VARARGS, "select a valid UPnP IGD among discovered devices" }, {"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS, @@ -418,9 +597,15 @@ static PyMethodDef UPnP_methods[] = { {"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS, "add a port mapping" }, + {"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS, + "add a port mapping, IGD to select alternative if necessary" + }, {"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS, "delete a port mapping" }, + {"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS, + "delete a range of port mappings" + }, {"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS, "-- non standard --" }, @@ -434,8 +619,8 @@ static PyMethodDef UPnP_methods[] = { }; static PyTypeObject UPnPType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, + 0) /*ob_size*/ "miniupnpc.UPnP", /*tp_name*/ sizeof(UPnPObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -470,13 +655,14 @@ static PyTypeObject UPnPType = { 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - 0,/*(initproc)UPnP_init,*/ /* tp_init */ + (initproc)UPnP_init, /* tp_init */ 0, /* tp_alloc */ -#ifndef WIN32 +#ifndef _WIN32 PyType_GenericNew,/*UPnP_new,*/ /* tp_new */ #else - 0, + 0, /* tp_new */ #endif + 0, /* tp_free */ }; /* module methods */ @@ -484,24 +670,69 @@ static PyMethodDef miniupnpc_methods[] = { {NULL} /* Sentinel */ }; +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "miniupnpc", /* m_name */ + "miniupnpc module.", /* m_doc */ + -1, /* m_size */ + miniupnpc_methods, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#endif + #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif + PyMODINIT_FUNC -initminiupnpc(void) +#if PY_MAJOR_VERSION >= 3 +PyInit_miniupnpc(void) +#else +initminiupnpc(void) +#endif { PyObject* m; -#ifdef WIN32 +#ifdef _WIN32 + /* initialize Winsock. */ + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if (nResult != 0) + { + /* error code could be WSASYSNOTREADY WSASYSNOTREADY + * WSASYSNOTREADY WSASYSNOTREADY WSASYSNOTREADY */ +#if PY_MAJOR_VERSION >= 3 + return 0; +#else + return; +#endif + } + UPnPType.tp_new = PyType_GenericNew; #endif if (PyType_Ready(&UPnPType) < 0) +#if PY_MAJOR_VERSION >= 3 + return 0; +#else return; +#endif +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&moduledef); +#else m = Py_InitModule3("miniupnpc", miniupnpc_methods, "miniupnpc module."); +#endif Py_INCREF(&UPnPType); PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType); + +#if PY_MAJOR_VERSION >= 3 + return m; +#endif } diff --git a/libs/miniupnpc/miniwget.c b/libs/miniupnpc/src/miniwget.c similarity index 50% rename from libs/miniupnpc/miniwget.c rename to libs/miniupnpc/src/miniwget.c index 7c9cb3ad5..19c683e64 100644 --- a/libs/miniupnpc/miniwget.c +++ b/libs/miniupnpc/src/miniwget.c @@ -1,21 +1,21 @@ -/* $Id: miniwget.c,v 1.52 2011/06/17 22:59:42 nanard Exp $ */ +/* $Id: miniwget.c,v 1.84 2020/11/09 19:41:18 nanard Exp $ */ /* Project : miniupnp + * Website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2020 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ - + #include #include #include #include -#ifdef WIN32 +#ifdef _WIN32 #include #include #include #define MAXHOSTNAMELEN 64 -#define MIN(x,y) (((x)<(y))?(x):(y)) -#define snprintf _snprintf +#include "win32_snprintf.h" #define socklen_t int #ifndef strncasecmp #if defined(_MSC_VER) && (_MSC_VER >= 1400) @@ -24,7 +24,7 @@ #define strncasecmp memicmp #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ #endif /* #ifndef strncasecmp */ -#else /* #ifdef WIN32 */ +#else /* #ifdef _WIN32 */ #include #include #if defined(__amigaos__) && !defined(__amigaos4__) @@ -33,22 +33,31 @@ #include #endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */ #include +#include #include +#include #include #define closesocket close -/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions - * during the connect() call */ -#define MINIUPNPC_IGNORE_EINTR -#endif /* #else WIN32 */ -#if defined(__sun) || defined(sun) +#include +#endif /* #else _WIN32 */ +#ifdef __GNU__ +#define MAXHOSTNAMELEN 64 +#endif /* __GNU__ */ + +#ifndef MIN #define MIN(x,y) (((x)<(y))?(x):(y)) -#endif +#endif /* MIN */ + #include "miniupnpcstrings.h" #include "miniwget.h" #include "connecthostport.h" #include "receivedata.h" +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + /* * Read a HTTP response from a socket. * Process Content-Length and Transfer-encoding headers. @@ -56,7 +65,7 @@ * to the length parameter. */ void * -getHTTPResponse(int s, int * size) +getHTTPResponse(SOCKET s, int * size, int * status_code) { char buf[2048]; int n; @@ -67,20 +76,42 @@ getHTTPResponse(int s, int * size) unsigned int bytestocopy = 0; /* buffers : */ char * header_buf; - int header_buf_len = 2048; - int header_buf_used = 0; + unsigned int header_buf_len = 2048; + unsigned int header_buf_used = 0; char * content_buf; - int content_buf_len = 2048; - int content_buf_used = 0; + unsigned int content_buf_len = 2048; + unsigned int content_buf_used = 0; char chunksize_buf[32]; - int chunksize_buf_index; + unsigned int chunksize_buf_index; +#ifdef DEBUG + char * reason_phrase = NULL; + int reason_phrase_len = 0; +#endif + if(status_code) *status_code = -1; header_buf = malloc(header_buf_len); + if(header_buf == NULL) + { +#ifdef DEBUG + fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse"); +#endif /* DEBUG */ + *size = -1; + return NULL; + } content_buf = malloc(content_buf_len); + if(content_buf == NULL) + { + free(header_buf); +#ifdef DEBUG + fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse"); +#endif /* DEBUG */ + *size = -1; + return NULL; + } chunksize_buf[0] = '\0'; chunksize_buf_index = 0; - while((n = receivedata(s, buf, 2048, 5000)) > 0) + while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0) { if(endofheaders == 0) { @@ -89,7 +120,15 @@ getHTTPResponse(int s, int * size) int colon=0; int valuestart=0; if(header_buf_used + n > header_buf_len) { - header_buf = realloc(header_buf, header_buf_used + n); + char * tmp = realloc(header_buf, header_buf_used + n); + if(tmp == NULL) { + /* memory allocation error */ + free(header_buf); + free(content_buf); + *size = -1; + return NULL; + } + header_buf = tmp; header_buf_len = header_buf_used + n; } memcpy(header_buf + header_buf_used, buf, n); @@ -97,14 +136,14 @@ getHTTPResponse(int s, int * size) /* search for CR LF CR LF (end of headers) * recognize also LF LF */ i = 0; - while(i < (header_buf_used-1) && (endofheaders == 0)) { + while(i < ((int)header_buf_used-1) && (endofheaders == 0)) { if(header_buf[i] == '\r') { i++; if(header_buf[i] == '\n') { i++; - if(i < header_buf_used && header_buf[i] == '\r') { + if(i < (int)header_buf_used && header_buf[i] == '\r') { i++; - if(i < header_buf_used && header_buf[i] == '\n') { + if(i < (int)header_buf_used && header_buf[i] == '\n') { endofheaders = i+1; } } @@ -121,7 +160,7 @@ getHTTPResponse(int s, int * size) continue; /* parse header lines */ for(i = 0; i < endofheaders - 1; i++) { - if(colon <= linestart && header_buf[i]==':') + if(linestart > 0 && colon <= linestart && header_buf[i]==':') { colon = i; while(i < (endofheaders-1) @@ -132,7 +171,34 @@ getHTTPResponse(int s, int * size) /* detecting end of line */ else if(header_buf[i]=='\r' || header_buf[i]=='\n') { - if(colon > linestart && valuestart > colon) + if(linestart == 0 && status_code) + { + /* Status line + * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ + int sp; + for(sp = 0; sp < i - 1; sp++) + if(header_buf[sp] == ' ') + { + if(*status_code < 0) + { + if (header_buf[sp+1] >= '1' && header_buf[sp+1] <= '9') + *status_code = atoi(header_buf + sp + 1); + } + else + { +#ifdef DEBUG + reason_phrase = header_buf + sp + 1; + reason_phrase_len = i - sp - 1; +#endif + break; + } + } +#ifdef DEBUG + printf("HTTP status code = %d, Reason phrase = %.*s\n", + *status_code, reason_phrase_len, reason_phrase); +#endif + } + else if(colon > linestart && valuestart > colon) { #ifdef DEBUG printf("header='%.*s', value='%.*s'\n", @@ -155,113 +221,130 @@ getHTTPResponse(int s, int * size) chunked = 1; } } - while(header_buf[i]=='\r' || header_buf[i] == '\n') + while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n')) i++; linestart = i; colon = linestart; valuestart = 0; - } + } } /* copy the remaining of the received data back to buf */ n = header_buf_used - endofheaders; memcpy(buf, header_buf + endofheaders, n); /* if(headers) */ } - if(endofheaders) + /* if we get there, endofheaders != 0. + * In the other case, there was a continue above */ + /* content */ + if(chunked) { - /* content */ - if(chunked) + int i = 0; + while(i < n) { - int i = 0; - while(i < n) + if(chunksize == 0) { + /* reading chunk size */ + if(chunksize_buf_index == 0) { + /* skipping any leading CR LF */ + if(buf[i] == '\r') i++; + if(i= '0' + && chunksize_buf[j] <= '9') + chunksize = (chunksize << 4) + (chunksize_buf[j] - '0'); + else + chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10); + } + chunksize_buf[0] = '\0'; + chunksize_buf_index = 0; + i++; + } else { + /* not finished to get chunksize */ + continue; + } +#ifdef DEBUG + printf("chunksize = %u (%x)\n", chunksize, chunksize); +#endif if(chunksize == 0) { - /* reading chunk size */ - if(chunksize_buf_index == 0) { - /* skipping any leading CR LF */ - if(i= '0' - && chunksize_buf[j] <= '9') - chunksize = (chunksize << 4) + (chunksize_buf[j] - '0'); - else - chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10); - } - chunksize_buf[0] = '\0'; - chunksize_buf_index = 0; - i++; - } else { - /* not finished to get chunksize */ - continue; - } #ifdef DEBUG - printf("chunksize = %u (%x)\n", chunksize, chunksize); + printf("end of HTTP content - %d %d\n", i, n); + /*printf("'%.*s'\n", n-i, buf+i);*/ #endif - if(chunksize == 0) - { -#ifdef DEBUG - printf("end of HTTP content - %d %d\n", i, n); - /*printf("'%.*s'\n", n-i, buf+i);*/ -#endif - goto end_of_stream; - } + goto end_of_stream; } - bytestocopy = ((int)chunksize < n - i)?chunksize:(n - i); - if((int)(content_buf_used + bytestocopy) > content_buf_len) - { - if(content_length >= content_buf_used + (int)bytestocopy) { - content_buf_len = content_length; - } else { - content_buf_len = content_buf_used + (int)bytestocopy; - } - content_buf = (char *)realloc((void *)content_buf, - content_buf_len); - } - memcpy(content_buf + content_buf_used, buf + i, bytestocopy); - content_buf_used += bytestocopy; - i += bytestocopy; - chunksize -= bytestocopy; } - } - else - { - /* not chunked */ - if(content_length > 0 - && (content_buf_used + n) > content_length) { - /* skipping additional bytes */ - n = content_length - content_buf_used; - } - if(content_buf_used + n > content_buf_len) + /* it is guaranteed that (n >= i) */ + bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i); + if((content_buf_used + bytestocopy) > content_buf_len) { - if(content_length >= content_buf_used + n) { + char * tmp; + if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) { content_buf_len = content_length; } else { - content_buf_len = content_buf_used + n; + content_buf_len = content_buf_used + bytestocopy; } - content_buf = (char *)realloc((void *)content_buf, - content_buf_len); + tmp = realloc(content_buf, content_buf_len); + if(tmp == NULL) { + /* memory allocation error */ + free(content_buf); + free(header_buf); + *size = -1; + return NULL; + } + content_buf = tmp; } - memcpy(content_buf + content_buf_used, buf, n); - content_buf_used += n; + memcpy(content_buf + content_buf_used, buf + i, bytestocopy); + content_buf_used += bytestocopy; + i += bytestocopy; + chunksize -= bytestocopy; } } + else + { + /* not chunked */ + if(content_length > 0 + && (content_buf_used + n) > (unsigned int)content_length) { + /* skipping additional bytes */ + n = content_length - content_buf_used; + } + if(content_buf_used + n > content_buf_len) + { + char * tmp; + if(content_length >= 0 + && (unsigned int)content_length >= (content_buf_used + n)) { + content_buf_len = content_length; + } else { + content_buf_len = content_buf_used + n; + } + tmp = realloc(content_buf, content_buf_len); + if(tmp == NULL) { + /* memory allocation error */ + free(content_buf); + free(header_buf); + *size = -1; + return NULL; + } + content_buf = tmp; + } + memcpy(content_buf + content_buf_used, buf, n); + content_buf_used += n; + } /* use the Content-Length header value if available */ - if(content_length > 0 && content_buf_used >= content_length) + if(content_length > 0 && content_buf_used >= (unsigned int)content_length) { #ifdef DEBUG printf("End of HTTP content\n"); @@ -270,7 +353,7 @@ getHTTPResponse(int s, int * size) } } end_of_stream: - free(header_buf); header_buf = NULL; + free(header_buf); *size = content_buf_used; if(content_buf_used == 0) { @@ -284,21 +367,22 @@ end_of_stream: * do all the work. * Return NULL if something failed. */ static void * -miniwget3(const char * url, const char * host, +miniwget3(const char * host, unsigned short port, const char * path, int * size, char * addr_str, int addr_str_len, - const char * httpversion) + const char * httpversion, unsigned int scope_id, + int * status_code) { char buf[2048]; - int s; - int n = -1; + SOCKET s; + int n; int len; int sent; void * content; *size = 0; - s = connecthostport(host, port); - if(s < 0) + s = connecthostport(host, port, scope_id); + if(ISINVALID(s)) return NULL; /* get address for caller ! */ @@ -336,16 +420,14 @@ miniwget3(const char * url, const char * host, addr_str, addr_str_len); } #endif -#ifndef NO_GETADDRINFO /* getnameinfo return ip v6 address with the scope identifier * such as : 2a01:e35:8b2b:7330::%4281128194 */ n = getnameinfo((const struct sockaddr *)&saddr, saddrlen, addr_str, addr_str_len, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); -#endif if(n != 0) { -#ifdef WIN32 +#ifdef _WIN32 fprintf(stderr, "getnameinfo() failed : %d\n", n); #else fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n)); @@ -362,10 +444,15 @@ miniwget3(const char * url, const char * host, "GET %s HTTP/%s\r\n" "Host: %s:%d\r\n" "Connection: Close\r\n" - "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" + "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" "\r\n", path, httpversion, host, port); + if ((unsigned int)len >= sizeof(buf)) + { + closesocket(s); + return NULL; + } sent = 0; /* sending the HTTP request */ while(sent < len) @@ -382,7 +469,7 @@ miniwget3(const char * url, const char * host, sent += n; } } - content = getHTTPResponse(s, size); + content = getHTTPResponse(s, size, status_code); closesocket(s); return content; } @@ -390,24 +477,32 @@ miniwget3(const char * url, const char * host, /* miniwget2() : * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */ static void * -miniwget2(const char * url, const char * host, - unsigned short port, const char * path, - int * size, char * addr_str, int addr_str_len) +miniwget2(const char * host, + unsigned short port, const char * path, + int * size, char * addr_str, int addr_str_len, + unsigned int scope_id, int * status_code) { char * respbuffer; - respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1"); -/* - respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.0"); +#if 1 + respbuffer = miniwget3(host, port, path, size, + addr_str, addr_str_len, "1.1", + scope_id, status_code); +#else + respbuffer = miniwget3(host, port, path, size, + addr_str, addr_str_len, "1.0", + scope_id, status_code); if (*size == 0) { #ifdef DEBUG printf("Retrying with HTTP/1.1\n"); #endif free(respbuffer); - respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1"); + respbuffer = miniwget3(host, port, path, size, + addr_str, addr_str_len, "1.1", + scope_id, status_code); } -*/ +#endif return respbuffer; } @@ -419,12 +514,15 @@ miniwget2(const char * url, const char * host, * url : source string not modified * hostname : hostname destination string (size of MAXHOSTNAMELEN+1) * port : port (destination) - * path : pointer to the path part of the URL + * path : pointer to the path part of the URL * * Return values : * 0 - Failure * 1 - Success */ -int parseURL(const char * url, char * hostname, unsigned short * port, char * * path) +int +parseURL(const char * url, + char * hostname, unsigned short * port, + char * * path, unsigned int * scope_id) { char * p1, *p2, *p3; if(!url) @@ -440,7 +538,43 @@ int parseURL(const char * url, char * hostname, unsigned short * port, char * * if(*p1 == '[') { /* IP v6 : http://[2a00:1450:8002::6a]/path/abc */ + char * scope; + scope = strchr(p1, '%'); p2 = strchr(p1, ']'); + if(p2 && scope && scope < p2 && scope_id) { + /* parse scope */ +#ifdef IF_NAMESIZE + char tmp[IF_NAMESIZE]; + int l; + scope++; + /* "%25" is just '%' in URL encoding */ + if(scope[0] == '2' && scope[1] == '5') + scope += 2; /* skip "25" */ + l = p2 - scope; + if(l >= IF_NAMESIZE) + l = IF_NAMESIZE - 1; + memcpy(tmp, scope, l); + tmp[l] = '\0'; + *scope_id = if_nametoindex(tmp); + if(*scope_id == 0) { + *scope_id = (unsigned int)strtoul(tmp, NULL, 10); + } +#else + /* under windows, scope is numerical */ + char tmp[8]; + size_t l; + scope++; + /* "%25" is just '%' in URL encoding */ + if(scope[0] == '2' && scope[1] == '5') + scope += 2; /* skip "25" */ + l = p2 - scope; + if(l >= sizeof(tmp)) + l = sizeof(tmp) - 1; + memcpy(tmp, scope, l); + tmp[l] = '\0'; + *scope_id = (unsigned int)strtoul(tmp, NULL, 10); +#endif + } p3 = strchr(p1, '/'); if(p2 && p3) { @@ -490,35 +624,41 @@ int parseURL(const char * url, char * hostname, unsigned short * port, char * * return 1; } -void * miniwget(const char * url, int * size) +void * +miniwget(const char * url, int * size, + unsigned int scope_id, int * status_code) { unsigned short port; char * path; /* protocol://host:port/chemin */ char hostname[MAXHOSTNAMELEN+1]; *size = 0; - if(!parseURL(url, hostname, &port, &path)) + if(!parseURL(url, hostname, &port, &path, &scope_id)) return NULL; #ifdef DEBUG - printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path); + printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", + hostname, port, path, scope_id); #endif - return miniwget2(url, hostname, port, path, size, 0, 0); + return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code); } -void * miniwget_getaddr(const char * url, int * size, char * addr, int addrlen) +void * +miniwget_getaddr(const char * url, int * size, + char * addr, int addrlen, unsigned int scope_id, + int * status_code) { unsigned short port; char * path; - /* protocol://host:port/chemin */ + /* protocol://host:port/path */ char hostname[MAXHOSTNAMELEN+1]; *size = 0; if(addr) addr[0] = '\0'; - if(!parseURL(url, hostname, &port, &path)) + if(!parseURL(url, hostname, &port, &path, &scope_id)) return NULL; #ifdef DEBUG - printf("parsed url : hostname='%s' port=%hu path='%s'\n", hostname, port, path); + printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", + hostname, port, path, scope_id); #endif - return miniwget2(url, hostname, port, path, size, addr, addrlen); + return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code); } - diff --git a/libs/miniupnpc/src/miniwget_private.h b/libs/miniupnpc/src/miniwget_private.h new file mode 100644 index 000000000..e4eaac808 --- /dev/null +++ b/libs/miniupnpc/src/miniwget_private.h @@ -0,0 +1,15 @@ +/* $Id: miniwget_private.h,v 1.1 2018/04/06 10:17:58 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2018 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef MINIWGET_INTERNAL_H_INCLUDED +#define MINIWGET_INTERNAL_H_INCLUDED + +#include "miniupnpc_socketdef.h" + +void * getHTTPResponse(SOCKET s, int * size, int * status_code); + +#endif diff --git a/libs/miniupnpc/minixml.c b/libs/miniupnpc/src/minixml.c similarity index 88% rename from libs/miniupnpc/minixml.c rename to libs/miniupnpc/src/minixml.c index 8b5594c88..935ec443e 100644 --- a/libs/miniupnpc/minixml.c +++ b/libs/miniupnpc/src/minixml.c @@ -1,10 +1,11 @@ -/* $Id: minixml.c,v 1.9 2011/02/07 13:44:57 nanard Exp $ */ -/* minixml.c : the minimum size a xml parser can be ! */ +/* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * minixml.c : the minimum size a xml parser can be ! */ /* Project : miniupnp * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard -Copyright (c) 2005-2011, Thomas BERNARD +Copyright (c) 2005-2017, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without @@ -113,7 +114,20 @@ static void parseelt(struct xmlparser * p) const char * elementname; while(p->xml < (p->xmlend - 1)) { - if((p->xml)[0]=='<' && (p->xml)[1]!='?') + if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "", 3) != 0); + p->xml += 3; + } + else if((p->xml)[0]=='<' && (p->xml)[1]!='?') { i = 0; elementname = ++p->xml; while( !IS_WHITE_SPACE(*p->xml) @@ -148,8 +162,9 @@ static void parseelt(struct xmlparser * p) if (p->xml >= p->xmlend) return; } - if(memcmp(p->xml, " */ + if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "xml += 9; data = p->xml; diff --git a/libs/miniupnpc/minixml.h b/libs/miniupnpc/src/minixml.h similarity index 83% rename from libs/miniupnpc/minixml.h rename to libs/miniupnpc/src/minixml.h index 857c70ee9..0a9fc08b7 100644 --- a/libs/miniupnpc/minixml.h +++ b/libs/miniupnpc/src/minixml.h @@ -1,4 +1,4 @@ -/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */ +/* $Id: minixml.h,v 1.8 2019/02/10 12:29:25 nanard Exp $ */ /* minimal xml parser * * Project : miniupnp @@ -8,9 +8,9 @@ * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ -#ifndef __MINIXML_H__ -#define __MINIXML_H__ -#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n')) +#ifndef MINIXML_H_INCLUDED +#define MINIXML_H_INCLUDED +#define IS_WHITE_SPACE(c) ((c)==' ' || (c)=='\t' || (c)=='\r' || (c)=='\n') /* if a callback function pointer is set to NULL, * the function is not called */ diff --git a/libs/miniupnpc/minixmlvalid.c b/libs/miniupnpc/src/minixmlvalid.c similarity index 94% rename from libs/miniupnpc/minixmlvalid.c rename to libs/miniupnpc/src/minixmlvalid.c index 766211bcf..dad148812 100644 --- a/libs/miniupnpc/minixmlvalid.c +++ b/libs/miniupnpc/src/minixmlvalid.c @@ -1,4 +1,4 @@ -/* $Id: minixmlvalid.c,v 1.4 2011/02/07 13:44:57 nanard Exp $ */ +/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */ /* MiniUPnP Project * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ * minixmlvalid.c : @@ -32,7 +32,7 @@ int evtlistcmp(struct eventlist * a, struct eventlist * b) if(a->n != b->n) { printf("event number not matching : %d != %d\n", a->n, b->n); - //return 1; + /*return 1;*/ } for(i=0; in; i++) { @@ -82,7 +82,7 @@ static const struct event evtref[] = {ELTEND, "elt2b", 5}, {ELTEND, "elt2a", 5}, {ELTEND, "xmlroot", 7} -}; +}; void startelt(void * data, const char * p, int l) { @@ -128,6 +128,11 @@ int testxmlparser(const char * xml, int size) struct xmlparser parser; evtlist.n = 0; evtlist.events = malloc(sizeof(struct event)*100); + if(evtlist.events == NULL) + { + fprintf(stderr, "Memory allocation error.\n"); + return -1; + } memset(&parser, 0, sizeof(parser)); parser.xmlstart = xml; parser.xmlsize = size; @@ -148,6 +153,8 @@ int testxmlparser(const char * xml, int size) int main(int argc, char * * argv) { int r; + (void)argc; (void)argv; + r = testxmlparser(xmldata, sizeof(xmldata)-1); if(r) printf("minixml validation test failed\n"); diff --git a/libs/miniupnpc/portlistingparse.c b/libs/miniupnpc/src/portlistingparse.c similarity index 81% rename from libs/miniupnpc/portlistingparse.c rename to libs/miniupnpc/src/portlistingparse.c index e09e80f39..54e5696ef 100644 --- a/libs/miniupnpc/portlistingparse.c +++ b/libs/miniupnpc/src/portlistingparse.c @@ -1,14 +1,22 @@ -/* $Id: portlistingparse.c,v 1.4 2011/03/18 11:02:17 nanard Exp $ */ +/* $Id: portlistingparse.c,v 1.11 2020/03/22 22:43:44 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2011 Thomas Bernard + * (c) 2011-2020 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include #include +#ifdef DEBUG +#include +#endif /* DEBUG */ #include "portlistingparse.h" #include "minixml.h" +#if defined(__HAIKU__) +/* rename our private function because Haiku already defines a atoui() function */ +#define atoui atoui2 +#endif + /* list of the elements */ static const struct { const portMappingElt code; @@ -52,7 +60,7 @@ startelt(void * d, const char * name, int l) pdata->curelt = PortMappingEltNone; for(i = 0; elements[i].str; i++) { - if(memcmp(name, elements[i].str, l) == 0) + if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0) { pdata->curelt = elements[i].code; break; @@ -62,7 +70,17 @@ startelt(void * d, const char * name, int l) { struct PortMapping * pm; pm = calloc(1, sizeof(struct PortMapping)); - LIST_INSERT_HEAD( &(pdata->head), pm, entries); + if(pm == NULL) + { + /* malloc error */ +#ifdef DEBUG + fprintf(stderr, "%s: error allocating memory", + "startelt"); +#endif /* DEBUG */ + return; + } + pm->l_next = pdata->l_head; /* insert in list */ + pdata->l_head = pm; } } @@ -71,6 +89,8 @@ static void endelt(void * d, const char * name, int l) { struct PortMappingParserData * pdata = (struct PortMappingParserData *)d; + (void)name; + (void)l; pdata->curelt = PortMappingEltNone; } @@ -80,7 +100,7 @@ data(void * d, const char * data, int l) { struct PortMapping * pm; struct PortMappingParserData * pdata = (struct PortMappingParserData *)d; - pm = pdata->head.lh_first; + pm = pdata->l_head; if(!pm) return; if(l > 63) @@ -132,7 +152,6 @@ ParsePortListing(const char * buffer, int bufsize, struct xmlparser parser; memset(pdata, 0, sizeof(struct PortMappingParserData)); - LIST_INIT(&(pdata->head)); /* init xmlparser */ parser.xmlstart = buffer; parser.xmlsize = bufsize; @@ -148,9 +167,10 @@ void FreePortListing(struct PortMappingParserData * pdata) { struct PortMapping * pm; - while((pm = pdata->head.lh_first) != NULL) + while((pm = pdata->l_head) != NULL) { - LIST_REMOVE(pm, entries); + /* remove from list */ + pdata->l_head = pm->l_next; free(pm); } } diff --git a/libs/miniupnpc/src/receivedata.c b/libs/miniupnpc/src/receivedata.c new file mode 100644 index 000000000..7f187f6e5 --- /dev/null +++ b/libs/miniupnpc/src/receivedata.c @@ -0,0 +1,105 @@ +/* $Id: receivedata.c,v 1.10 2021/03/02 23:33:07 nanard Exp $ */ +/* Project : miniupnp + * Website : http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2011-2021 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. */ + +#include +#include +#ifdef _WIN32 +#include +#include +#else /* _WIN32 */ +#include +#if defined(__amigaos__) && !defined(__amigaos4__) +#define socklen_t int +#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */ +#include +#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */ +#include +#include +#if !defined(__amigaos__) && !defined(__amigaos4__) +#include +#endif /* !defined(__amigaos__) && !defined(__amigaos4__) */ +#include +#define MINIUPNPC_IGNORE_EINTR +#endif /* _WIN32 */ + +#include "receivedata.h" + +int +receivedata(SOCKET socket, + char * data, int length, + int timeout, unsigned int * scope_id) +{ +#ifdef MINIUPNPC_GET_SRC_ADDR + struct sockaddr_storage src_addr; + socklen_t src_addr_len = sizeof(src_addr); +#endif /* MINIUPNPC_GET_SRC_ADDR */ + int n; +#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) + /* using poll */ + struct pollfd fds[1]; /* for the poll */ +#ifdef MINIUPNPC_IGNORE_EINTR + do { +#endif /* MINIUPNPC_IGNORE_EINTR */ + fds[0].fd = socket; + fds[0].events = POLLIN; + n = poll(fds, 1, timeout); +#ifdef MINIUPNPC_IGNORE_EINTR + } while(n < 0 && errno == EINTR); +#endif /* MINIUPNPC_IGNORE_EINTR */ + if(n < 0) { + PRINT_SOCKET_ERROR("poll"); + return -1; + } else if(n == 0) { + /* timeout */ + return 0; + } +#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ + /* using select under _WIN32 and amigaos */ + fd_set socketSet; + TIMEVAL timeval; + FD_ZERO(&socketSet); + FD_SET(socket, &socketSet); + timeval.tv_sec = timeout / 1000; + timeval.tv_usec = (timeout % 1000) * 1000; + n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval); + if(n < 0) { + PRINT_SOCKET_ERROR("select"); + return -1; + } else if(n == 0) { + return 0; + } +#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ +#ifdef MINIUPNPC_GET_SRC_ADDR + memset(&src_addr, 0, sizeof(src_addr)); + n = recvfrom(socket, data, length, 0, + (struct sockaddr *)&src_addr, &src_addr_len); +#else /* MINIUPNPC_GET_SRC_ADDR */ + n = recv(socket, data, length, 0); +#endif /* MINIUPNPC_GET_SRC_ADDR */ + if(n<0) { + PRINT_SOCKET_ERROR("recv"); + } +#ifdef MINIUPNPC_GET_SRC_ADDR + if (src_addr.ss_family == AF_INET6) { + const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr; +#ifdef DEBUG + printf("scope_id=%u\n", src_addr6->sin6_scope_id); +#endif /* DEBUG */ + if(scope_id) + *scope_id = src_addr6->sin6_scope_id; + } else { + if(scope_id) + *scope_id = 0; + } +#else /* MINIUPNPC_GET_SRC_ADDR */ + if(scope_id) + *scope_id = 0; +#endif /* MINIUPNPC_GET_SRC_ADDR */ + return n; +} + diff --git a/libs/miniupnpc/src/receivedata.h b/libs/miniupnpc/src/receivedata.h new file mode 100644 index 000000000..4dce3f3b4 --- /dev/null +++ b/libs/miniupnpc/src/receivedata.h @@ -0,0 +1,21 @@ +/* $Id: receivedata.h,v 1.5 2018/04/06 10:53:15 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2011-2018 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef RECEIVEDATA_H_INCLUDED +#define RECEIVEDATA_H_INCLUDED + +#include "miniupnpc_socketdef.h" + +/* Reads data from the specified socket. + * Returns the number of bytes read if successful, zero if no bytes were + * read or if we timed out. Returns negative if there was an error. */ +int receivedata(SOCKET socket, + char * data, int length, + int timeout, unsigned int * scope_id); + +#endif + diff --git a/libs/miniupnpc/src/testaddr_is_reserved.c b/libs/miniupnpc/src/testaddr_is_reserved.c new file mode 100644 index 000000000..313f0daa4 --- /dev/null +++ b/libs/miniupnpc/src/testaddr_is_reserved.c @@ -0,0 +1,46 @@ +/* $Id: testaddr_is_reserved.c,v 1.1 2020/10/15 22:12:51 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas BERNARD + * copyright (c) 2005-2020 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#include +#include "addr_is_reserved.h" + +static const struct { + const char * str; + int expected_result; +} tests[] = { +{ "0.0.0.0", 1 }, +{ "8.8.8.8", 0 }, +{ "192.168.1.1", 1 }, +{ "10.250.42.12", 1 }, +{ "11.250.42.12", 0 }, +{ "172.31.1.1", 1 }, +{ "172.32.1.1", 0 }, +{ "169.254.42.42", 1 }, +{ "192.0.0.11", 1 }, +{ "198.0.0.11", 0 }, +{ "198.18.0.11", 1 }, +{ "100.64.1.1", 1 }, +{ "100.127.1.1", 1 }, +{ "100.128.1.1", 0 }, +{ NULL, 0 } +}; + +int main(int argc, char * * argv) { + int i, result; + (void)argc; (void)argv; + + for (i = 0; tests[i].str != NULL; i++) { + result = addr_is_reserved(tests[i].str); + printf("testing %s %d %d\n", tests[i].str, tests[i].expected_result, result); + if (result != tests[i].expected_result) { + fprintf(stderr, "*** FAILURE ***\n"); + return 1; /* Failure */ + } + } + return 0; /* success */ +} diff --git a/libs/miniupnpc/src/testigddescparse.c b/libs/miniupnpc/src/testigddescparse.c new file mode 100644 index 000000000..cdc765de4 --- /dev/null +++ b/libs/miniupnpc/src/testigddescparse.c @@ -0,0 +1,187 @@ +/* $Id: testigddescparse.c,v 1.11 2019/02/10 12:33:32 nanard Exp $ */ +/* Project : miniupnp + * http://miniupnp.free.fr/ + * Author : Thomas Bernard + * Copyright (c) 2008-2015 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#include +#include +#include +#include "igd_desc_parse.h" +#include "minixml.h" +#include "miniupnpc.h" + +/* count number of differences */ +int compare_service(struct IGDdatas_service * s, FILE * f) +{ + int n = 0; + char line[1024]; + + while(fgets(line, sizeof(line), f)) { + char * value; + char * equal; + char * name; + char * parsedvalue; + int l; + l = strlen(line); + while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n') || (line[l-1] == ' '))) + line[--l] = '\0'; + if(l == 0) + break; /* end on blank line */ + if(line[0] == '#') + continue; /* skip comments */ + equal = strchr(line, '='); + if(equal == NULL) { + fprintf(stderr, "Warning, line does not contain '=' : %s\n", line); + continue; + } + *equal = '\0'; + name = line; + while(*name == ' ' || *name == '\t') + name++; + l = strlen(name); + while((l > 0) && (name[l-1] == ' ' || name[l-1] == '\t')) + name[--l] = '\0'; + value = equal + 1; + while(*value == ' ' || *value == '\t') + value++; + if(strcmp(name, "controlurl") == 0) + parsedvalue = s->controlurl; + else if(strcmp(name, "eventsuburl") == 0) + parsedvalue = s->eventsuburl; + else if(strcmp(name, "scpdurl") == 0) + parsedvalue = s->scpdurl; + else if(strcmp(name, "servicetype") == 0) + parsedvalue = s->servicetype; + else { + fprintf(stderr, "unknown field '%s'\n", name); + continue; + } + if(0 != strcmp(parsedvalue, value)) { + fprintf(stderr, "difference : '%s' != '%s'\n", parsedvalue, value); + n++; + } + } + return n; +} + +int compare_igd(struct IGDdatas * p, FILE * f) +{ + int n = 0; + char line[1024]; + struct IGDdatas_service * s; + + while(fgets(line, sizeof(line), f)) { + char * colon; + int l = (int)strlen(line); + while((l > 0) && (line[l-1] == '\r' || (line[l-1] == '\n'))) + line[--l] = '\0'; + if(l == 0 || line[0] == '#') + continue; /* skip blank lines and comments */ + colon = strchr(line, ':'); + if(colon == NULL) { + fprintf(stderr, "Warning, no ':' : %s\n", line); + continue; + } + s = NULL; + *colon = '\0'; + if(strcmp(line, "CIF") == 0) + s = &p->CIF; + else if(strcmp(line, "first") == 0) + s = &p->first; + else if(strcmp(line, "second") == 0) + s = &p->second; + else if(strcmp(line, "IPv6FC") == 0) + s = &p->IPv6FC; + else { + s = NULL; + fprintf(stderr, "*** unknown service '%s' ***\n", line); + n++; + continue; + } + n += compare_service(s, f); + } + if(n > 0) + fprintf(stderr, "*** %d difference%s ***\n", n, (n > 1) ? "s" : ""); + return n; +} + +int test_igd_desc_parse(char * buffer, int len, FILE * f) +{ + int n; + struct IGDdatas igd; + struct xmlparser parser; + struct UPNPUrls urls; + + memset(&igd, 0, sizeof(struct IGDdatas)); + memset(&parser, 0, sizeof(struct xmlparser)); + parser.xmlstart = buffer; + parser.xmlsize = len; + parser.data = &igd; + parser.starteltfunc = IGDstartelt; + parser.endeltfunc = IGDendelt; + parser.datafunc = IGDdata; + parsexml(&parser); +#ifdef DEBUG + printIGD(&igd); +#endif /* DEBUG */ + GetUPNPUrls(&urls, &igd, "http://fake/desc/url/file.xml", 0); + printf("ipcondescURL='%s'\n", urls.ipcondescURL); + printf("controlURL='%s'\n", urls.controlURL); + printf("controlURL_CIF='%s'\n", urls.controlURL_CIF); + n = f ? compare_igd(&igd, f) : 0; + FreeUPNPUrls(&urls); + return n; +} + +int main(int argc, char * * argv) +{ + FILE * f; + char * buffer; + int len; + int r; + if(argc<2) { + fprintf(stderr, "Usage: %s file.xml [file.values]\n", argv[0]); + return 1; + } + f = fopen(argv[1], "rb"); + if(!f) { + fprintf(stderr, "Cannot open %s for reading.\n", argv[1]); + return 1; + } + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + buffer = malloc(len); + if(!buffer) { + fprintf(stderr, "Memory allocation error.\n"); + fclose(f); + return 1; + } + r = (int)fread(buffer, 1, len, f); + if(r != len) { + fprintf(stderr, "Failed to read file %s. %d out of %d bytes.\n", + argv[1], r, len); + fclose(f); + free(buffer); + return 1; + } + fclose(f); + f = NULL; + if(argc > 2) { + f = fopen(argv[2], "rb"); + if(!f) { + fprintf(stderr, "Cannot open %s for reading.\n", argv[2]); + free(buffer); + return 1; + } + } + r = test_igd_desc_parse(buffer, len, f); + free(buffer); + if(f) + fclose(f); + return r; +} + diff --git a/libs/miniupnpc/testminiwget.c b/libs/miniupnpc/src/testminiwget.c similarity index 70% rename from libs/miniupnpc/testminiwget.c rename to libs/miniupnpc/src/testminiwget.c index b68fbfeed..cfbb224de 100644 --- a/libs/miniupnpc/testminiwget.c +++ b/libs/miniupnpc/src/testminiwget.c @@ -1,7 +1,8 @@ -/* $Id: testminiwget.c,v 1.3 2011/05/06 16:33:53 nanard Exp $ */ -/* Project : miniupnp +/* $Id: testminiwget.c,v 1.7 2018/01/16 01:01:05 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2018 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ @@ -11,7 +12,7 @@ /** * This program uses the miniwget / miniwget_getaddr function - * from miniwget.c in order to retreive a web ressource using + * from miniwget.c in order to retrieve a web ressource using * a GET HTTP method, and store it in a file. */ int main(int argc, char * * argv) @@ -20,15 +21,17 @@ int main(int argc, char * * argv) int size, writtensize; FILE *f; char addr[64]; + int status_code = -1; + if(argc < 3) { fprintf(stderr, "Usage:\t%s url file\n", argv[0]); fprintf(stderr, "Example:\t%s http://www.google.com/ out.html\n", argv[0]); return 1; } - /*data = miniwget(argv[1], &size);*/ - data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr)); - if(!data) { - fprintf(stderr, "Error fetching %s\n", argv[1]); + data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0, &status_code); + if(!data || (status_code != 200)) { + if(data) free(data); + fprintf(stderr, "Error %d fetching %s\n", status_code, argv[1]); return 1; } printf("local address : %s\n", addr); diff --git a/libs/miniupnpc/testminixml.c b/libs/miniupnpc/src/testminixml.c similarity index 82% rename from libs/miniupnpc/testminixml.c rename to libs/miniupnpc/src/testminixml.c index 3d82527b7..57c4a85eb 100644 --- a/libs/miniupnpc/testminixml.c +++ b/libs/miniupnpc/src/testminixml.c @@ -1,7 +1,11 @@ -/* $Id: testminixml.c,v 1.6 2006/11/19 22:32:35 nanard Exp $ +/* $Id: testminixml.c,v 1.10 2014/11/17 17:19:13 nanard Exp $ + * MiniUPnP project + * Website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * Author : Thomas Bernard. + * Copyright (c) 2005-2014 Thomas Bernard + * * testminixml.c * test program for the "minixml" functions. - * Author : Thomas Bernard. */ #include #include @@ -9,18 +13,11 @@ #include "minixml.h" #include "igd_desc_parse.h" -#ifdef WIN32 -#define NO_BZERO -#endif - -#ifdef NO_BZERO -#define bzero(p, n) memset(p, 0, n) -#endif - /* ---------------------------------------------------------------------- */ void printeltname1(void * d, const char * name, int l) { int i; + (void)d; printf("element "); for(i=0;i +#include +#include "portlistingparse.h" + +struct port_mapping { + unsigned int leasetime; + unsigned short externalport; + unsigned short internalport; + const char * remotehost; + const char * client; + const char * proto; + const char * desc; + unsigned char enabled; +}; + +/* return the number of differences */ +int test(const char * portListingXml, int portListingXmlLen, + const struct port_mapping * ref, int count) +{ + int i; + int r = 0; + struct PortMappingParserData data; + struct PortMapping * pm; + + memset(&data, 0, sizeof(data)); + ParsePortListing(portListingXml, portListingXmlLen, &data); + for(i = 0, pm = data.l_head; + (pm != NULL) && (i < count); + i++, pm = pm->l_next) { + printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n", + i, pm->protocol, pm->externalPort, pm->internalClient, + pm->internalPort, + pm->description, pm->remoteHost, + (unsigned)pm->leaseTime); + if(0 != strcmp(pm->protocol, ref[i].proto)) { + printf("protocol : '%s' != '%s'\n", pm->protocol, ref[i].proto); + r++; + } + if(pm->externalPort != ref[i].externalport) { + printf("externalPort : %hu != %hu\n", + pm->externalPort, ref[i].externalport); + r++; + } + if(0 != strcmp(pm->internalClient, ref[i].client)) { + printf("client : '%s' != '%s'\n", + pm->internalClient, ref[i].client); + r++; + } + if(pm->internalPort != ref[i].internalport) { + printf("internalPort : %hu != %hu\n", + pm->internalPort, ref[i].internalport); + r++; + } + if(0 != strcmp(pm->description, ref[i].desc)) { + printf("description : '%s' != '%s'\n", + pm->description, ref[i].desc); + r++; + } + if(0 != strcmp(pm->remoteHost, ref[i].remotehost)) { + printf("remoteHost : '%s' != '%s'\n", + pm->remoteHost, ref[i].remotehost); + r++; + } + if((unsigned)pm->leaseTime != ref[i].leasetime) { + printf("leaseTime : %u != %u\n", + (unsigned)pm->leaseTime, ref[i].leasetime); + r++; + } + if(pm->enabled != ref[i].enabled) { + printf("enabled : %d != %d\n", + (int)pm->enabled, (int)ref[i].enabled); + r++; + } + } + if((i != count) || (pm != NULL)) { + printf("count mismatch : i=%d count=%d pm=%p\n", i, count, pm); + r++; + } + FreePortListing(&data); + return r; +} + +const char test_document[] = +"\n" +"\n" +" \n" +" \n" +" 5002\n" +" UDP\n" +" 4001\n" +" 192.168.1.123\n" +" 1\n" +" xxx\n" +" 0\n" +" \n" +" \n" +" 202.233.2.1\n" +" 2345\n" +" TCP\n" +" 2349\n" +" 192.168.1.137\n" +" 1\n" +" dooom\n" +" 346\n" +" \n" +" \n" +" 134.231.2.11\n" +" 12345\n" +" TCP\n" +" 12345\n" +" 192.168.1.137\n" +" 1\n" +" dooom A\n" +" 347\n" +" \n" +""; + +#define PORT_MAPPINGS_COUNT 3 +const struct port_mapping port_mappings[PORT_MAPPINGS_COUNT] = { +{347, 12345, 12345, "134.231.2.11", "192.168.1.137", "TCP", "dooom A", 1}, +{346, 2345, 2349, "202.233.2.1", "192.168.1.137", "TCP", "dooom", 1}, +{0, 5002, 4001, "", "192.168.1.123", "UDP", "xxx", 1} +}; + +/* --- main --- */ +int main(void) +{ + int r; + r = test(test_document, sizeof(test_document) - 1, + port_mappings, PORT_MAPPINGS_COUNT); + if(r == 0) { + printf("test of portlistingparse OK\n"); + return 0; + } else { + printf("test FAILED (%d differences counted)\n", r); + return 1; + } +} + diff --git a/libs/miniupnpc/src/testupnpreplyparse.c b/libs/miniupnpc/src/testupnpreplyparse.c new file mode 100644 index 000000000..ed3c3fe0d --- /dev/null +++ b/libs/miniupnpc/src/testupnpreplyparse.c @@ -0,0 +1,115 @@ +/* $Id: testupnpreplyparse.c,v 1.5 2017/12/12 11:18:46 nanard Exp $ */ +/* MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2006-2017 Thomas Bernard + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ +#include +#include +#include +#include "upnpreplyparse.h" + +int +test_parsing(const char * buf, int len, FILE * f) +{ + char line[1024]; + struct NameValueParserData pdata; + int ok = 1; + ParseNameValue(buf, len, &pdata); + /* check result */ + if(f != NULL) + { + while(fgets(line, sizeof(line), f)) + { + char * value; + char * equal; + char * parsedvalue; + int l; + l = strlen(line); + while((l > 0) && ((line[l-1] == '\r') || (line[l-1] == '\n'))) + line[--l] = '\0'; + /* skip empty lines */ + if(l == 0) + continue; + equal = strchr(line, '='); + if(equal == NULL) + { + fprintf(stderr, "Warning, line does not contain '=' : %s\n", line); + continue; + } + *equal = '\0'; + value = equal + 1; + parsedvalue = GetValueFromNameValueList(&pdata, line); + if((parsedvalue == NULL) || (strcmp(parsedvalue, value) != 0)) + { + fprintf(stderr, "Element <%s> : expecting value '%s', got '%s'\n", + line, value, parsedvalue ? parsedvalue : ""); + ok = 0; + } + } + } + ClearNameValueList(&pdata); + return ok; +} + +int main(int argc, char * * argv) +{ + FILE * f; + char * buffer; + long l; + int ok; + + if(argc<2) + { + fprintf(stderr, "Usage: %s file.xml [file.namevalues]\n", argv[0]); + return 1; + } + f = fopen(argv[1], "r"); + if(!f) + { + fprintf(stderr, "Error : can not open file %s\n", argv[1]); + return 2; + } + if(fseek(f, 0, SEEK_END) < 0) { + perror("fseek"); + return 1; + } + l = (int)ftell(f); + if(l < 0) { + perror("ftell"); + return 1; + } + if(fseek(f, 0, SEEK_SET) < 0) { + perror("fseek"); + return 1; + } + buffer = malloc(l + 1); + if(buffer == NULL) { + fprintf(stderr, "Error: failed to allocate %ld bytes\n", l+1); + return 1; + } + l = fread(buffer, 1, l, f); + fclose(f); + f = NULL; + buffer[l] = '\0'; + if(argc > 2) + { + f = fopen(argv[2], "r"); + if(!f) + { + fprintf(stderr, "Error : can not open file %s\n", argv[2]); + return 2; + } + } +#ifdef DEBUG + DisplayNameValueList(buffer, l); +#endif + ok = test_parsing(buffer, l, f); + if(f) + { + fclose(f); + } + free(buffer); + return ok ? 0 : 3; +} + diff --git a/libs/miniupnpc/upnpc.c b/libs/miniupnpc/src/upnpc.c similarity index 59% rename from libs/miniupnpc/upnpc.c rename to libs/miniupnpc/src/upnpc.c index b136d9d1f..23bb27830 100644 --- a/libs/miniupnpc/upnpc.c +++ b/libs/miniupnpc/src/upnpc.c @@ -1,7 +1,7 @@ -/* $Id: upnpc.c,v 1.88 2011/06/17 23:31:01 nanard Exp $ */ +/* $Id: upnpc.c,v 1.134 2023/06/11 23:23:10 nanard Exp $ */ /* Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2023 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -9,16 +9,22 @@ #include #include #include -#ifdef WIN32 +#ifdef _WIN32 #include -#define snprintf _snprintf +#include "win32_snprintf.h" +#else +/* for IPPROTO_TCP / IPPROTO_UDP */ +#include #endif +#include #include "miniwget.h" #include "miniupnpc.h" #include "upnpcommands.h" +#include "portlistingparse.h" #include "upnperrors.h" +#include "miniupnpcstrings.h" -/* protofix() checks if protocol is "UDP" or "TCP" +/* protofix() checks if protocol is "UDP" or "TCP" * returns NULL if not */ const char * protofix(const char * proto) { @@ -26,7 +32,7 @@ const char * protofix(const char * proto) static const char proto_udp[4] = { 'U', 'D', 'P', 0}; int i, b; for(i=0, b=1; i<4; i++) - b = b && ( (proto[i] == proto_tcp[i]) + b = b && ( (proto[i] == proto_tcp[i]) || (proto[i] == (proto_tcp[i] | 32)) ); if(b) return proto_tcp; @@ -38,6 +44,22 @@ const char * protofix(const char * proto) return 0; } +/* is_int() checks if parameter is an integer or not + * 1 for integer + * 0 for not an integer */ +int is_int(char const* s) +{ + if(s == NULL) + return 0; + while(*s) { + /* #define isdigit(c) ((c) >= '0' && (c) <= '9') */ + if(!isdigit(*s)) + return 0; + s++; + } + return 1; +} + static void DisplayInfos(struct UPNPUrls * urls, struct IGDdatas * data) { @@ -45,48 +67,55 @@ static void DisplayInfos(struct UPNPUrls * urls, char connectionType[64]; char status[64]; char lastconnerr[64]; - unsigned int uptime; + unsigned int uptime = 0; unsigned int brUp, brDown; time_t timenow, timestarted; int r; - UPNP_GetConnectionTypeInfo(urls->controlURL, - data->first.servicetype, - connectionType); - if(connectionType[0]) - printf("Connection Type : %s\n", connectionType); - else + if(UPNP_GetConnectionTypeInfo(urls->controlURL, + data->first.servicetype, + connectionType) != UPNPCOMMAND_SUCCESS) printf("GetConnectionTypeInfo failed.\n"); - UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype, - status, &uptime, lastconnerr); - printf("Status : %s, uptime=%us, LastConnectionError : %s\n", - status, uptime, lastconnerr); - timenow = time(NULL); - timestarted = timenow - uptime; - printf(" Time started : %s", ctime(×tarted)); - UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype, - &brDown, &brUp); - printf("MaxBitRateDown : %u bps", brDown); - if(brDown >= 1000000) { - printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10); - } else if(brDown >= 1000) { - printf(" (%u Kbps)", brDown / 1000); + else + printf("Connection Type : %s\n", connectionType); + if(UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype, + status, &uptime, lastconnerr) != UPNPCOMMAND_SUCCESS) + printf("GetStatusInfo failed.\n"); + else + printf("Status : %s, uptime=%us, LastConnectionError : %s\n", + status, uptime, lastconnerr); + if(uptime > 0) { + timenow = time(NULL); + timestarted = timenow - uptime; + printf(" Time started : %s", ctime(×tarted)); } - printf(" MaxBitRateUp %u bps", brUp); - if(brUp >= 1000000) { - printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10); - } else if(brUp >= 1000) { - printf(" (%u Kbps)", brUp / 1000); + if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype, + &brDown, &brUp) != UPNPCOMMAND_SUCCESS) { + printf("GetLinkLayerMaxBitRates failed.\n"); + } else { + printf("MaxBitRateDown : %u bps", brDown); + if(brDown >= 1000000) { + printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10); + } else if(brDown >= 1000) { + printf(" (%u Kbps)", brDown / 1000); + } + printf(" MaxBitRateUp %u bps", brUp); + if(brUp >= 1000000) { + printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10); + } else if(brUp >= 1000) { + printf(" (%u Kbps)", brUp / 1000); + } + printf("\n"); } - printf("\n"); r = UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, externalIPAddress); - if(r != UPNPCOMMAND_SUCCESS) - printf("GetExternalIPAddress() returned %d\n", r); - if(externalIPAddress[0]) + if(r != UPNPCOMMAND_SUCCESS) { + printf("GetExternalIPAddress failed. (errorcode=%d)\n", r); + } else if(!externalIPAddress[0]) { + printf("GetExternalIPAddress failed. (empty string)\n"); + } else { printf("ExternalIPAddress = %s\n", externalIPAddress); - else - printf("GetExternalIPAddress failed.\n"); + } } static void GetConnectionStatus(struct UPNPUrls * urls, @@ -106,7 +135,7 @@ static void ListRedirections(struct UPNPUrls * urls, struct IGDdatas * data) { int r; - int i = 0; + unsigned short i = 0; char index[6]; char intClient[40]; char intPort[6]; @@ -119,8 +148,9 @@ static void ListRedirections(struct UPNPUrls * urls, /*unsigned int num=0; UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num); printf("PortMappingNumberOfEntries : %u\n", num);*/ + printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n"); do { - snprintf(index, 6, "%d", i); + snprintf(index, 6, "%hu", i); rHost[0] = '\0'; enabled[0] = '\0'; duration[0] = '\0'; desc[0] = '\0'; extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0'; @@ -132,20 +162,19 @@ static void ListRedirections(struct UPNPUrls * urls, rHost, duration); if(r==0) /* - printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n" + printf("%02hu - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n" " desc='%s' rHost='%s'\n", i, protocol, extPort, intClient, intPort, enabled, duration, desc, rHost); */ - printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n", + printf("%2hu %s %5s->%s:%-5s '%s' '%s' %s\n", i, protocol, extPort, intClient, intPort, desc, rHost, duration); else printf("GetGenericPortMappingEntry() returned %d (%s)\n", r, strupnperror(r)); - i++; - } while(r==0); + } while(r == 0 && i++ < 65535); } static void NewListRedirections(struct UPNPUrls * urls, @@ -159,14 +188,15 @@ static void NewListRedirections(struct UPNPUrls * urls, memset(&pdata, 0, sizeof(struct PortMappingParserData)); r = UPNP_GetListOfPortMappings(urls->controlURL, data->first.servicetype, - "0", + "1", "65535", "TCP", "1000", &pdata); if(r == UPNPCOMMAND_SUCCESS) { - for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next) + printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n"); + for(pm = pdata.l_head; pm != NULL; pm = pm->l_next) { printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n", i, pm->protocol, pm->externalPort, pm->internalClient, @@ -184,14 +214,14 @@ static void NewListRedirections(struct UPNPUrls * urls, } r = UPNP_GetListOfPortMappings(urls->controlURL, data->first.servicetype, - "0", + "1", "65535", "UDP", "1000", &pdata); if(r == UPNPCOMMAND_SUCCESS) { - for(pm = pdata.head.lh_first; pm != NULL; pm = pm->entries.le_next) + for(pm = pdata.l_head; pm != NULL; pm = pm->l_next) { printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n", i, pm->protocol, pm->externalPort, pm->internalClient, @@ -209,87 +239,145 @@ static void NewListRedirections(struct UPNPUrls * urls, } } -/* Test function +/* Test function * 1 - get connection type * 2 - get extenal ip address * 3 - Add port mapping * 4 - get this port mapping from the IGD */ -static void SetRedirectAndTest(struct UPNPUrls * urls, - struct IGDdatas * data, - const char * iaddr, - const char * iport, - const char * eport, - const char * proto, - const char * leaseDuration) +static int SetRedirectAndTest(struct UPNPUrls * urls, + struct IGDdatas * data, + const char * iaddr, + const char * iport, + const char * eport, + const char * proto, + const char * leaseDuration, + const char * remoteHost, + const char * description, + int addAny) { char externalIPAddress[40]; char intClient[40]; char intPort[6]; + char reservedPort[6]; char duration[16]; int r; if(!iaddr || !iport || !eport || !proto) { fprintf(stderr, "Wrong arguments\n"); - return; + return -1; } proto = protofix(proto); if(!proto) { fprintf(stderr, "invalid protocol\n"); - return; + return -1; } - - UPNP_GetExternalIPAddress(urls->controlURL, - data->first.servicetype, - externalIPAddress); - if(externalIPAddress[0]) - printf("ExternalIPAddress = %s\n", externalIPAddress); - else - printf("GetExternalIPAddress failed.\n"); - - r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype, - eport, iport, iaddr, 0, proto, 0, leaseDuration); + + r = UPNP_GetExternalIPAddress(urls->controlURL, + data->first.servicetype, + externalIPAddress); if(r!=UPNPCOMMAND_SUCCESS) - printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", - eport, iport, iaddr, r, strupnperror(r)); + printf("GetExternalIPAddress failed.\n"); + else + printf("ExternalIPAddress = %s\n", externalIPAddress); + + if (addAny) { + r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype, + eport, iport, iaddr, description, + proto, remoteHost, leaseDuration, reservedPort); + if(r==UPNPCOMMAND_SUCCESS) + eport = reservedPort; + else + printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n", + eport, iport, iaddr, r, strupnperror(r)); + } else { + r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype, + eport, iport, iaddr, description, + proto, remoteHost, leaseDuration); + if(r!=UPNPCOMMAND_SUCCESS) { + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + eport, iport, iaddr, r, strupnperror(r)); + return -2; + } + } r = UPNP_GetSpecificPortMappingEntry(urls->controlURL, - data->first.servicetype, - eport, proto, - intClient, intPort, NULL/*desc*/, - NULL/*enabled*/, duration); - if(r!=UPNPCOMMAND_SUCCESS) + data->first.servicetype, + eport, proto, remoteHost, + intClient, intPort, NULL/*desc*/, + NULL/*enabled*/, duration); + if(r!=UPNPCOMMAND_SUCCESS) { printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n", r, strupnperror(r)); - - if(intClient[0]) { + return -2; + } else { printf("InternalIP:Port = %s:%s\n", intClient, intPort); printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n", externalIPAddress, eport, proto, intClient, intPort, duration); } + return 0; } -static void +static int RemoveRedirect(struct UPNPUrls * urls, struct IGDdatas * data, - const char * eport, - const char * proto) + const char * eport, + const char * proto, + const char * remoteHost) { int r; if(!proto || !eport) { fprintf(stderr, "invalid arguments\n"); - return; + return -1; } proto = protofix(proto); if(!proto) { fprintf(stderr, "protocol invalid\n"); - return; + return -1; } - r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, 0); - printf("UPNP_DeletePortMapping() returned : %d\n", r); + r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost); + if(r!=UPNPCOMMAND_SUCCESS) { + printf("UPNP_DeletePortMapping() failed with code : %d\n", r); + return -2; + }else { + printf("UPNP_DeletePortMapping() returned : %d\n", r); + } + return 0; +} + +static int +RemoveRedirectRange(struct UPNPUrls * urls, + struct IGDdatas * data, + const char * ePortStart, char const * ePortEnd, + const char * proto, const char * manage) +{ + int r; + + if (!manage) + manage = "0"; + + if(!proto || !ePortStart || !ePortEnd) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + proto = protofix(proto); + if(!proto) + { + fprintf(stderr, "protocol invalid\n"); + return -1; + } + r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage); + if(r!=UPNPCOMMAND_SUCCESS) { + printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r); + return -2; + }else { + printf("UPNP_DeletePortMappingRange() returned : %d\n", r); + } + return 0; } /* IGD:2, functions for service WANIPv6FirewallControl:1 */ @@ -301,7 +389,7 @@ static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data) UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed); printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed); printf("GetFirewallStatus:\n Firewall Enabled: %s\n Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No"); - + bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype); bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype); packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype); @@ -310,7 +398,7 @@ static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data) printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived); } -/* Test function +/* Test function * 1 - Add pinhole * 2 - Check if pinhole is working from the IGD side */ static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data, @@ -319,27 +407,43 @@ static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data, const char * proto, const char * lease_time) { char uniqueID[8]; - //int isWorking = 0; + /*int isWorking = 0;*/ int r; + char proto_tmp[8]; if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time) { fprintf(stderr, "Wrong arguments\n"); return; } - /*proto = protofix(proto); - if(!proto) + if(atoi(proto) == 0) { - fprintf(stderr, "invalid protocol\n"); - return; - }*/ + const char * protocol; + protocol = protofix(proto); + if(protocol && (strcmp("TCP", protocol) == 0)) + { + snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_TCP); + proto = proto_tmp; + } + else if(protocol && (strcmp("UDP", protocol) == 0)) + { + snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_UDP); + proto = proto_tmp; + } + else + { + fprintf(stderr, "invalid protocol\n"); + return; + } + } r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID); if(r!=UPNPCOMMAND_SUCCESS) printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n", - intaddr, iport, remoteaddr, eport, r, strupnperror(r)); + remoteaddr, eport, intaddr, iport, r, strupnperror(r)); else { - printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n", intaddr, iport, remoteaddr, eport, uniqueID); + printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n", + remoteaddr, eport, intaddr, iport, uniqueID); /*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking); if(r!=UPNPCOMMAND_SUCCESS) printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r)); @@ -361,11 +465,20 @@ static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data, fprintf(stderr, "Wrong arguments\n"); return; } + /* CheckPinholeWorking is an Optional Action, error 602 should be + * returned if it is not implemented */ r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking); - printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No"); - if(r!=UPNPCOMMAND_SUCCESS) - printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r)); - if(isWorking || r==709) + if(r==UPNPCOMMAND_SUCCESS) + printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No"); + else + printf("CheckPinholeWorking(%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r)); + /* 702 FirewallDisabled Firewall is disabled and this action is disabled + * 703 InboundPinholeNotAllowed Creation of inbound pinholes by UPnP CPs + * are not allowed and this action is disabled + * 704 NoSuchEntry There is no pinhole with the specified UniqueID. + * 709 NoTrafficReceived No traffic corresponding to this pinhole has + * been received by the gateway. */ + if(isWorking || (r!=702 && r!=703 && r!=704)) { r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time); printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time); @@ -374,7 +487,7 @@ static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data, } } -/* Test function +/* Test function * Get pinhole timeout */ static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data, @@ -455,16 +568,20 @@ int main(int argc, char ** argv) char ** commandargv = 0; int commandargc = 0; struct UPNPDev * devlist = 0; - char lanaddr[64]; /* my ip address on the LAN */ + char lanaddr[64] = "unset"; /* my ip address on the LAN */ int i; const char * rootdescurl = 0; const char * multicastif = 0; const char * minissdpdpath = 0; + int localport = UPNP_LOCAL_PORT_ANY; int retcode = 0; int error = 0; int ipv6 = 0; + int ignore = 0; + unsigned char ttl = 2; /* defaulting to 2 */ + const char * description = 0; -#ifdef WIN32 +#ifdef _WIN32 WSADATA wsaData; int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); if(nResult != NO_ERROR) @@ -473,22 +590,49 @@ int main(int argc, char ** argv) return -1; } #endif - printf("upnpc : miniupnpc library test client. (c) 2006-2011 Thomas Bernard\n"); - printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n" + printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING); + printf(" (c) 2005-2023 Thomas Bernard.\n"); + printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n" "for more information.\n"); /* command line processing */ for(i=1; i65535 || + (localport >1 && localport < 1024)) + { + fprintf(stderr, "Invalid localport '%s'\n", argv[i]); + localport = UPNP_LOCAL_PORT_ANY; + break; + } + } else if(argv[i][1] == 'p') minissdpdpath = argv[++i]; else if(argv[i][1] == '6') ipv6 = 1; + else if(argv[i][1] == 'e') + description = argv[++i]; + else if(argv[i][1] == 't') + ttl = (unsigned char)atoi(argv[++i]); + else if(argv[i][1] == 'i') + ignore = 1; else { command = argv[i][1]; @@ -504,19 +648,22 @@ int main(int argc, char ** argv) } } - if(!command || (command == 'a' && commandargc<4) + if(!command + || (command == 'a' && commandargc<4) || (command == 'd' && argc<2) || (command == 'r' && argc<2) || (command == 'A' && commandargc<6) || (command == 'U' && commandargc<2) || (command == 'D' && commandargc<1)) { - fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration]\n\t\tAdd port redirection\n", argv[0]); - fprintf(stderr, " \t%s [options] -d external_port protocol [port2 protocol2] [...]\n\t\tDelete port redirection\n", argv[0]); + fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration] [remote host]\n\t\tAdd port redirection\n", argv[0]); + fprintf(stderr, " \t%s [options] -d external_port protocol [remote host]\n\t\tDelete port redirection\n", argv[0]); fprintf(stderr, " \t%s [options] -s\n\t\tGet Connection status\n", argv[0]); fprintf(stderr, " \t%s [options] -l\n\t\tList redirections\n", argv[0]); - fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings, IGD v2)\n", argv[0]); - fprintf(stderr, " \t%s [options] -r port1 protocol1 [port2 protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]); + fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv[0]); + fprintf(stderr, " \t%s [options] -n ip port external_port protocol [duration] [remote host]\n\t\tAdd (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only)\n", argv[0]); + fprintf(stderr, " \t%s [options] -N external_port_start external_port_end protocol [manage]\n\t\tDelete range of port redirections (for IGD:2 only)\n", argv[0]); + fprintf(stderr, " \t%s [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]); fprintf(stderr, " \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]); fprintf(stderr, " \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]); fprintf(stderr, " \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]); @@ -526,17 +673,22 @@ int main(int argc, char ** argv) fprintf(stderr, " \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]); fprintf(stderr, " \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]); fprintf(stderr, "\nprotocol is UDP or TCP\n"); + fprintf(stderr, "@ can be used in option -a, -n, -A and -G to represent local LAN address.\n"); fprintf(stderr, "Options:\n"); + fprintf(stderr, " -e description : set description for port mapping.\n"); fprintf(stderr, " -6 : use ip v6 instead of ip v4.\n"); fprintf(stderr, " -u url : bypass discovery process by providing the XML root description url.\n"); - fprintf(stderr, " -m address/interface : provide ip address (ip v4) or interface name (ip v6) to use for sending SSDP multicast packets.\n"); + fprintf(stderr, " -m address/interface : provide ip address (ip v4) or interface name (ip v4 or v6) to use for sending SSDP multicast packets.\n"); + fprintf(stderr, " -z localport : SSDP packets local (source) port (1024-65535).\n"); fprintf(stderr, " -p path : use this path for MiniSSDPd socket.\n"); + fprintf(stderr, " -t ttl : set multicast TTL. Default value is 2.\n"); + fprintf(stderr, " -i : ignore errors and try to use also disconnected IGD or non-IGD device.\n"); return 1; } if( rootdescurl || (devlist = upnpDiscover(2000, multicastif, minissdpdpath, - 0/*sameport*/, ipv6, &error))) + localport, ipv6, ttl, &error))) { struct UPNPDev * device; struct UPNPUrls urls; @@ -550,7 +702,7 @@ int main(int argc, char ** argv) device->descURL, device->st); } } - else + else if(!rootdescurl) { printf("upnpDiscover() error code=%d\n", error); } @@ -564,16 +716,18 @@ int main(int argc, char ** argv) break; case 2: printf("Found a (not connected?) IGD : %s\n", urls.controlURL); - printf("Trying to continue anyway\n"); + if (ignore) printf("Trying to continue anyway\n"); break; case 3: printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL); - printf("Trying to continue anyway\n"); + if (ignore) printf("Trying to continue anyway\n"); break; default: printf("Found device (igd ?) : %s\n", urls.controlURL); - printf("Trying to continue anyway\n"); + if (ignore) printf("Trying to continue anyway\n"); } + if(i==1 || ignore) { + printf("Local LAN ip address : %s\n", lanaddr); #if 0 printf("getting \"%s\"\n", urls.ipcondescURL); @@ -585,6 +739,12 @@ int main(int argc, char ** argv) } #endif + /* replace '@' with the local LAN ip address */ + if ((command == 'a' || command == 'n') && 0 == strcmp(commandargv[0], "@")) + commandargv[0] = lanaddr; + else if ((command == 'A' || command == 'G') && 0 == strcmp(commandargv[2], "@")) + commandargv[2] = lanaddr; + switch(command) { case 'l': @@ -595,27 +755,65 @@ int main(int argc, char ** argv) NewListRedirections(&urls, &data); break; case 'a': - SetRedirectAndTest(&urls, &data, - commandargv[0], commandargv[1], - commandargv[2], commandargv[3], - (commandargc > 4)?commandargv[4]:"0"); + if (SetRedirectAndTest(&urls, &data, + commandargv[0], commandargv[1], + commandargv[2], commandargv[3], + (commandargc > 4) && is_int(commandargv[4]) ? commandargv[4] : "0", + (commandargc > 4) && !is_int(commandargv[4]) ? commandargv[4] : (commandargc > 5) ? commandargv[5] : NULL, + description, 0) < 0) + retcode = 2; break; case 'd': - for(i=0; i 2 ? commandargv[2] : NULL) < 0) + retcode = 2; + break; + case 'n': /* aNy */ + if (SetRedirectAndTest(&urls, &data, + commandargv[0], commandargv[1], + commandargv[2], commandargv[3], + (commandargc > 4) && is_int(commandargv[4]) ? commandargv[4] : "0", + (commandargc > 4) && !is_int(commandargv[4]) ? commandargv[4] : (commandargc > 5) ? commandargv[5] : NULL, + description, 1) < 0) + retcode = 2; + break; + case 'N': + if (commandargc < 3) + fprintf(stderr, "too few arguments\n"); + + if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2], + commandargc > 3 ? commandargv[3] : NULL) < 0) + retcode = 2; break; case 's': GetConnectionStatus(&urls, &data); break; case 'r': - for(i=0; i */ + if (SetRedirectAndTest(&urls, &data, + lanaddr, commandargv[i], + commandargv[i+1], commandargv[i+2], "0", NULL, + description, 0) < 0) + retcode = 2; + i+=3; /* 3 parameters parsed */ + } else { + /* 2nd parameter not an integer : */ + if (SetRedirectAndTest(&urls, &data, + lanaddr, commandargv[i], + commandargv[i], commandargv[i+1], "0", NULL, + description, 0) < 0) + retcode = 2; + i+=2; /* 2 parameters parsed */ + } } break; case 'A': @@ -664,6 +862,10 @@ int main(int argc, char ** argv) retcode = 1; } + } else { + fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n"); + retcode = 1; + } FreeUPNPUrls(&urls); } else @@ -678,6 +880,12 @@ int main(int argc, char ** argv) fprintf(stderr, "No IGD UPnP Device found on the network !\n"); retcode = 1; } +#ifdef _WIN32 + nResult = WSACleanup(); + if(nResult != NO_ERROR) { + fprintf(stderr, "WSACleanup() failed.\n"); + } +#endif /* _WIN32 */ return retcode; } diff --git a/libs/miniupnpc/upnpcommands.c b/libs/miniupnpc/src/upnpcommands.c similarity index 79% rename from libs/miniupnpc/upnpcommands.c rename to libs/miniupnpc/src/upnpcommands.c index 1114759a0..1e1ee6786 100644 --- a/libs/miniupnpc/upnpcommands.c +++ b/libs/miniupnpc/src/upnpcommands.c @@ -1,7 +1,8 @@ -/* $Id: upnpcommands.c,v 1.37 2011/06/04 15:56:23 nanard Exp $ */ -/* Project : miniupnp +/* $Id: upnpcommands.c,v 1.51 2019/04/23 11:45:15 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2011 Thomas Bernard + * Copyright (c) 2005-2018 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ @@ -11,6 +12,7 @@ #include "upnpcommands.h" #include "miniupnpc.h" #include "portlistingparse.h" +#include "upnpreplyparse.h" static UNSIGNED_INTEGER my_atoui(const char * s) @@ -20,7 +22,7 @@ my_atoui(const char * s) /* * */ -LIBSPEC UNSIGNED_INTEGER +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalBytesSent(const char * controlURL, const char * servicetype) { @@ -31,11 +33,11 @@ UPNP_GetTotalBytesSent(const char * controlURL, char * p; if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesSent", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -44,7 +46,7 @@ UPNP_GetTotalBytesSent(const char * controlURL, /* * */ -LIBSPEC UNSIGNED_INTEGER +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalBytesReceived(const char * controlURL, const char * servicetype) { @@ -55,11 +57,11 @@ UPNP_GetTotalBytesReceived(const char * controlURL, char * p; if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesReceived", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -68,7 +70,7 @@ UPNP_GetTotalBytesReceived(const char * controlURL, /* * */ -LIBSPEC UNSIGNED_INTEGER +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalPacketsSent(const char * controlURL, const char * servicetype) { @@ -79,11 +81,11 @@ UPNP_GetTotalPacketsSent(const char * controlURL, char * p; if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsSent", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -92,7 +94,7 @@ UPNP_GetTotalPacketsSent(const char * controlURL, /* * */ -LIBSPEC UNSIGNED_INTEGER +MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalPacketsReceived(const char * controlURL, const char * servicetype) { @@ -103,11 +105,11 @@ UPNP_GetTotalPacketsReceived(const char * controlURL, char * p; if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsReceived", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -116,10 +118,10 @@ UPNP_GetTotalPacketsReceived(const char * controlURL, /* UPNP_GetStatusInfo() call the corresponding UPNP method * returns the current status and uptime */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetStatusInfo(const char * controlURL, const char * servicetype, - char * status, + char * status, unsigned int * uptime, char * lastconnerror) { @@ -140,7 +142,7 @@ UPNP_GetStatusInfo(const char * controlURL, } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); up = GetValueFromNameValueList(&pdata, "NewUptime"); p = GetValueFromNameValueList(&pdata, "NewConnectionStatus"); err = GetValueFromNameValueList(&pdata, "NewLastConnectionError"); @@ -159,7 +161,7 @@ UPNP_GetStatusInfo(const char * controlURL, if(up) sscanf(up,"%u",uptime); else - uptime = 0; + *uptime = 0; } if(lastconnerror) { @@ -181,7 +183,7 @@ UPNP_GetStatusInfo(const char * controlURL, /* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method * returns the connection type */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetConnectionTypeInfo(const char * controlURL, const char * servicetype, char * connectionType) @@ -200,7 +202,7 @@ UPNP_GetConnectionTypeInfo(const char * controlURL, return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewConnectionType"); /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/ /* PossibleConnectionTypes will have several values.... */ @@ -221,10 +223,10 @@ UPNP_GetConnectionTypeInfo(const char * controlURL, /* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method. * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth. - * One of the values can be null - * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only + * One of the values can be null + * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetLinkLayerMaxBitRates(const char * controlURL, const char * servicetype, unsigned int * bitrateDown, @@ -249,7 +251,7 @@ UPNP_GetLinkLayerMaxBitRates(const char * controlURL, } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/ /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/ down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate"); @@ -285,7 +287,7 @@ UPNP_GetLinkLayerMaxBitRates(const char * controlURL, /* UPNP_GetExternalIPAddress() call the corresponding UPNP method. * if the third arg is not null the value is copied to it. * at least 16 bytes must be available - * + * * Return values : * 0 : SUCCESS * NON ZERO : ERROR Either an UPnP error code or an unknown error. @@ -293,7 +295,7 @@ UPNP_GetLinkLayerMaxBitRates(const char * controlURL, * 402 Invalid Args - See UPnP Device Architecture section on Control. * 501 Action Failed - See UPnP Device Architecture section on Control. */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetExternalIPAddress(const char * controlURL, const char * servicetype, char * extIpAdd) @@ -313,7 +315,7 @@ UPNP_GetExternalIPAddress(const char * controlURL, } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/ p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress"); if(p) { @@ -333,15 +335,15 @@ UPNP_GetExternalIPAddress(const char * controlURL, return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_AddPortMapping(const char * controlURL, const char * servicetype, - const char * extPort, - const char * inPort, - const char * inClient, - const char * desc, - const char * proto, - const char * remoteHost, - const char * leaseDuration) + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration) { struct UPNParg * AddPortMappingArgs; char * buffer; @@ -354,6 +356,8 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, return UPNPCOMMAND_INVALID_ARGS; AddPortMappingArgs = calloc(9, sizeof(struct UPNParg)); + if(AddPortMappingArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; AddPortMappingArgs[0].elt = "NewRemoteHost"; AddPortMappingArgs[0].val = remoteHost; AddPortMappingArgs[1].elt = "NewExternalPort"; @@ -370,17 +374,18 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, AddPortMappingArgs[6].val = desc?desc:"libminiupnpc"; AddPortMappingArgs[7].elt = "NewLeaseDuration"; AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0"; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "AddPortMapping", AddPortMappingArgs, - &bufsize))) { - free(AddPortMappingArgs); + buffer = simpleUPnPcommand(-1, controlURL, servicetype, + "AddPortMapping", AddPortMappingArgs, + &bufsize); + free(AddPortMappingArgs); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ /*buffer[bufsize] = '\0';*/ /*puts(buffer);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { /*printf("AddPortMapping errorCode = '%s'\n", resVal); */ @@ -390,11 +395,79 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(AddPortMappingArgs); return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int +UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration, + char * reservedPort) +{ + struct UPNParg * AddPortMappingArgs; + char * buffer; + int bufsize; + struct NameValueParserData pdata; + const char * resVal; + int ret; + + if(!inPort || !inClient || !proto || !extPort) + return UPNPCOMMAND_INVALID_ARGS; + + AddPortMappingArgs = calloc(9, sizeof(struct UPNParg)); + if(AddPortMappingArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; + AddPortMappingArgs[0].elt = "NewRemoteHost"; + AddPortMappingArgs[0].val = remoteHost; + AddPortMappingArgs[1].elt = "NewExternalPort"; + AddPortMappingArgs[1].val = extPort; + AddPortMappingArgs[2].elt = "NewProtocol"; + AddPortMappingArgs[2].val = proto; + AddPortMappingArgs[3].elt = "NewInternalPort"; + AddPortMappingArgs[3].val = inPort; + AddPortMappingArgs[4].elt = "NewInternalClient"; + AddPortMappingArgs[4].val = inClient; + AddPortMappingArgs[5].elt = "NewEnabled"; + AddPortMappingArgs[5].val = "1"; + AddPortMappingArgs[6].elt = "NewPortMappingDescription"; + AddPortMappingArgs[6].val = desc?desc:"libminiupnpc"; + AddPortMappingArgs[7].elt = "NewLeaseDuration"; + AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0"; + buffer = simpleUPnPcommand(-1, controlURL, servicetype, + "AddAnyPortMapping", AddPortMappingArgs, + &bufsize); + free(AddPortMappingArgs); + if(!buffer) { + return UPNPCOMMAND_HTTP_ERROR; + } + ParseNameValue(buffer, bufsize, &pdata); + free(buffer); + resVal = GetValueFromNameValueList(&pdata, "errorCode"); + if(resVal) { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf(resVal, "%d", &ret); + } else { + char *p; + + p = GetValueFromNameValueList(&pdata, "NewReservedPort"); + if(p) { + strncpy(reservedPort, p, 6); + reservedPort[5] = '\0'; + ret = UPNPCOMMAND_SUCCESS; + } else { + ret = UPNPCOMMAND_INVALID_RESPONSE; + } + } + ClearNameValueList(&pdata); + return ret; +} + +MINIUPNP_LIBSPEC int UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, const char * extPort, const char * proto, const char * remoteHost) @@ -411,21 +484,24 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, return UPNPCOMMAND_INVALID_ARGS; DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg)); + if(DeletePortMappingArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; DeletePortMappingArgs[0].elt = "NewRemoteHost"; DeletePortMappingArgs[0].val = remoteHost; DeletePortMappingArgs[1].elt = "NewExternalPort"; DeletePortMappingArgs[1].val = extPort; DeletePortMappingArgs[2].elt = "NewProtocol"; DeletePortMappingArgs[2].val = proto; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "DeletePortMapping", - DeletePortMappingArgs, &bufsize))) { - free(DeletePortMappingArgs); + buffer = simpleUPnPcommand(-1, controlURL, servicetype, + "DeletePortMapping", + DeletePortMappingArgs, &bufsize); + free(DeletePortMappingArgs); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { ret = UPNPCOMMAND_UNKNOWN_ERROR; @@ -434,11 +510,58 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(DeletePortMappingArgs); return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int +UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, + const char * extPortStart, const char * extPortEnd, + const char * proto, + const char * manage) +{ + struct UPNParg * DeletePortMappingArgs; + char * buffer; + int bufsize; + struct NameValueParserData pdata; + const char * resVal; + int ret; + + if(!extPortStart || !extPortEnd || !proto || !manage) + return UPNPCOMMAND_INVALID_ARGS; + + DeletePortMappingArgs = calloc(5, sizeof(struct UPNParg)); + if(DeletePortMappingArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; + DeletePortMappingArgs[0].elt = "NewStartPort"; + DeletePortMappingArgs[0].val = extPortStart; + DeletePortMappingArgs[1].elt = "NewEndPort"; + DeletePortMappingArgs[1].val = extPortEnd; + DeletePortMappingArgs[2].elt = "NewProtocol"; + DeletePortMappingArgs[2].val = proto; + DeletePortMappingArgs[3].elt = "NewManage"; + DeletePortMappingArgs[3].val = manage; + + buffer = simpleUPnPcommand(-1, controlURL, servicetype, + "DeletePortMappingRange", + DeletePortMappingArgs, &bufsize); + free(DeletePortMappingArgs); + if(!buffer) { + return UPNPCOMMAND_HTTP_ERROR; + } + ParseNameValue(buffer, bufsize, &pdata); + free(buffer); + resVal = GetValueFromNameValueList(&pdata, "errorCode"); + if(resVal) { + ret = UPNPCOMMAND_UNKNOWN_ERROR; + sscanf(resVal, "%d", &ret); + } else { + ret = UPNPCOMMAND_SUCCESS; + } + ClearNameValueList(&pdata); + return ret; +} + +MINIUPNP_LIBSPEC int UPNP_GetGenericPortMappingEntry(const char * controlURL, const char * servicetype, const char * index, @@ -462,16 +585,19 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, intClient[0] = '\0'; intPort[0] = '\0'; GetPortMappingArgs = calloc(2, sizeof(struct UPNParg)); + if(GetPortMappingArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; GetPortMappingArgs[0].elt = "NewPortMappingIndex"; GetPortMappingArgs[0].val = index; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "GetGenericPortMappingEntry", - GetPortMappingArgs, &bufsize))) { - free(GetPortMappingArgs); + buffer = simpleUPnPcommand(-1, controlURL, servicetype, + "GetGenericPortMappingEntry", + GetPortMappingArgs, &bufsize); + free(GetPortMappingArgs); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewRemoteHost"); if(p && rHost) @@ -493,14 +619,14 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, protocol[3] = '\0'; } p = GetValueFromNameValueList(&pdata, "NewInternalClient"); - if(p && intClient) + if(p) { strncpy(intClient, p, 16); intClient[15] = '\0'; r = 0; } p = GetValueFromNameValueList(&pdata, "NewInternalPort"); - if(p && intPort) + if(p) { strncpy(intPort, p, 6); intPort[5] = '\0'; @@ -529,11 +655,10 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, sscanf(p, "%d", &r); } ClearNameValueList(&pdata); - free(GetPortMappingArgs); return r; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetPortMappingNumberOfEntries(const char * controlURL, const char * servicetype, unsigned int * numEntries) @@ -552,7 +677,7 @@ UPNP_GetPortMappingNumberOfEntries(const char * controlURL, DisplayNameValueList(buffer, bufsize); #endif ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries"); if(numEntries && p) { @@ -574,11 +699,12 @@ UPNP_GetPortMappingNumberOfEntries(const char * controlURL, /* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping * the result is returned in the intClient and intPort strings * please provide 16 and 6 bytes of data */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetSpecificPortMappingEntry(const char * controlURL, const char * servicetype, const char * extPort, - const char * proto, + const char * proto, + const char * remoteHost, char * intClient, char * intPort, char * desc, @@ -596,21 +722,24 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, return UPNPCOMMAND_INVALID_ARGS; GetPortMappingArgs = calloc(4, sizeof(struct UPNParg)); + if(GetPortMappingArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; GetPortMappingArgs[0].elt = "NewRemoteHost"; - /* TODO : add remote host ? */ + GetPortMappingArgs[0].val = remoteHost; GetPortMappingArgs[1].elt = "NewExternalPort"; GetPortMappingArgs[1].val = extPort; GetPortMappingArgs[2].elt = "NewProtocol"; GetPortMappingArgs[2].val = proto; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "GetSpecificPortMappingEntry", - GetPortMappingArgs, &bufsize))) { - free(GetPortMappingArgs); + buffer = simpleUPnPcommand(-1, controlURL, servicetype, + "GetSpecificPortMappingEntry", + GetPortMappingArgs, &bufsize); + free(GetPortMappingArgs); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewInternalClient"); if(p) { @@ -653,7 +782,6 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, } ClearNameValueList(&pdata); - free(GetPortMappingArgs); return ret; } @@ -665,7 +793,7 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, * 733 InconsistantParameters - NewStartPort and NewEndPort values are not * consistent. */ -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetListOfPortMappings(const char * controlURL, const char * servicetype, const char * startPort, @@ -685,6 +813,8 @@ UPNP_GetListOfPortMappings(const char * controlURL, return UPNPCOMMAND_INVALID_ARGS; GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg)); + if(GetListOfPortMappingsArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; GetListOfPortMappingsArgs[0].elt = "NewStartPort"; GetListOfPortMappingsArgs[0].val = startPort; GetListOfPortMappingsArgs[1].elt = "NewEndPort"; @@ -696,17 +826,17 @@ UPNP_GetListOfPortMappings(const char * controlURL, GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts"; GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000"; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "GetListOfPortMappings", - GetListOfPortMappingsArgs, &bufsize))) { - free(GetListOfPortMappingsArgs); + buffer = simpleUPnPcommand(-1, controlURL, servicetype, + "GetListOfPortMappings", + GetListOfPortMappingsArgs, &bufsize); + free(GetListOfPortMappingsArgs); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } - free(GetListOfPortMappingsArgs); /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); /*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/ /*if(p) { @@ -741,16 +871,16 @@ UPNP_GetListOfPortMappings(const char * controlURL, } ClearNameValueList(&pdata); - //printf("%.*s", bufsize, buffer); + /*printf("%.*s", bufsize, buffer);*/ return ret; } -/* IGD:2, functions for service WANIPv6FirewallControl:1 */ -LIBSPEC int +/* IGD:2, functions for service WANIPv6FirewallControl:1 */ +MINIUPNP_LIBSPEC int UPNP_GetFirewallStatus(const char * controlURL, const char * servicetype, - int * firewallEnabled, + int * firewallEnabled, int * inboundPinholeAllowed) { struct NameValueParserData pdata; @@ -759,7 +889,7 @@ UPNP_GetFirewallStatus(const char * controlURL, char * fe, *ipa, *p; int ret = UPNPCOMMAND_UNKNOWN_ERROR; - if(!firewallEnabled && !inboundPinholeAllowed) + if(!firewallEnabled || !inboundPinholeAllowed) return UPNPCOMMAND_INVALID_ARGS; buffer = simpleUPnPcommand(-1, controlURL, servicetype, @@ -768,7 +898,7 @@ UPNP_GetFirewallStatus(const char * controlURL, return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); fe = GetValueFromNameValueList(&pdata, "FirewallEnabled"); ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed"); if(ipa && fe) @@ -791,7 +921,7 @@ UPNP_GetFirewallStatus(const char * controlURL, return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype, const char * remoteHost, const char * remotePort, @@ -805,13 +935,14 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype int bufsize; struct NameValueParserData pdata; const char * resVal; - char * p; int ret; if(!intPort || !intClient || !proto || !remotePort || !remoteHost) return UPNPCOMMAND_INVALID_ARGS; GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg)); + if(GetOutboundPinholeTimeoutArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost"; GetOutboundPinholeTimeoutArgs[0].val = remoteHost; GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort"; @@ -824,10 +955,11 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype GetOutboundPinholeTimeoutArgs[4].val = intClient; buffer = simpleUPnPcommand(-1, controlURL, servicetype, "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize); + free(GetOutboundPinholeTimeoutArgs); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { @@ -836,17 +968,16 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype } else { - ret = UPNPCOMMAND_SUCCESS; - p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout"); + const char * p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout"); if(p) *opTimeout = my_atoui(p); + ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(GetOutboundPinholeTimeoutArgs); return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_AddPinhole(const char * controlURL, const char * servicetype, const char * remoteHost, const char * remotePort, @@ -868,7 +999,9 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, return UPNPCOMMAND_INVALID_ARGS; AddPinholeArgs = calloc(7, sizeof(struct UPNParg)); - // RemoteHost can be wilcarded + if(AddPinholeArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; + /* RemoteHost can be wilcarded */ if(strncmp(remoteHost, "empty", 5)==0) { AddPinholeArgs[0].elt = "RemoteHost"; @@ -899,10 +1032,11 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, AddPinholeArgs[5].val = leaseTime; buffer = simpleUPnPcommand(-1, controlURL, servicetype, "AddPinhole", AddPinholeArgs, &bufsize); + free(AddPinholeArgs); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "UniqueID"); if(p) { @@ -912,7 +1046,7 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { - //printf("AddPortMapping errorCode = '%s'\n", resVal); + /*printf("AddPortMapping errorCode = '%s'\n", resVal);*/ ret = UPNPCOMMAND_UNKNOWN_ERROR; sscanf(resVal, "%d", &ret); } @@ -921,11 +1055,10 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(AddPinholeArgs); return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, const char * uniqueID, const char * leaseTime) @@ -941,16 +1074,19 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, return UPNPCOMMAND_INVALID_ARGS; UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg)); + if(UpdatePinholeArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; UpdatePinholeArgs[0].elt = "UniqueID"; UpdatePinholeArgs[0].val = uniqueID; UpdatePinholeArgs[1].elt = "NewLeaseTime"; UpdatePinholeArgs[1].val = leaseTime; buffer = simpleUPnPcommand(-1, controlURL, servicetype, "UpdatePinhole", UpdatePinholeArgs, &bufsize); + free(UpdatePinholeArgs); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { @@ -963,11 +1099,10 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(UpdatePinholeArgs); return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID) { /*struct NameValueParserData pdata;*/ @@ -982,15 +1117,18 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char return UPNPCOMMAND_INVALID_ARGS; DeletePinholeArgs = calloc(2, sizeof(struct UPNParg)); + if(DeletePinholeArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; DeletePinholeArgs[0].elt = "UniqueID"; DeletePinholeArgs[0].val = uniqueID; buffer = simpleUPnPcommand(-1, controlURL, servicetype, "DeletePinhole", DeletePinholeArgs, &bufsize); + free(DeletePinholeArgs); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { @@ -1002,11 +1140,10 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(DeletePinholeArgs); return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, const char * uniqueID, int * isWorking) { @@ -1021,14 +1158,19 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, return UPNPCOMMAND_INVALID_ARGS; CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg)); + if(CheckPinholeWorkingArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; CheckPinholeWorkingArgs[0].elt = "UniqueID"; CheckPinholeWorkingArgs[0].val = uniqueID; buffer = simpleUPnPcommand(-1, controlURL, servicetype, "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize); + free(CheckPinholeWorkingArgs); if(!buffer) + { return UPNPCOMMAND_HTTP_ERROR; + } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "IsWorking"); if(p) @@ -1047,11 +1189,10 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, } ClearNameValueList(&pdata); - free(CheckPinholeWorkingArgs); return ret; } -LIBSPEC int +MINIUPNP_LIBSPEC int UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, const char * uniqueID, int * packets) { @@ -1066,14 +1207,17 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, return UPNPCOMMAND_INVALID_ARGS; GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg)); + if(GetPinholePacketsArgs == NULL) + return UPNPCOMMAND_MEM_ALLOC_ERROR; GetPinholePacketsArgs[0].elt = "UniqueID"; GetPinholePacketsArgs[0].val = uniqueID; buffer = simpleUPnPcommand(-1, controlURL, servicetype, "GetPinholePackets", GetPinholePacketsArgs, &bufsize); + free(GetPinholePacketsArgs); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "PinholePackets"); if(p) @@ -1090,8 +1234,5 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, } ClearNameValueList(&pdata); - free(GetPinholePacketsArgs); return ret; } - - diff --git a/libs/miniupnpc/src/upnpdev.c b/libs/miniupnpc/src/upnpdev.c new file mode 100644 index 000000000..d89a9934c --- /dev/null +++ b/libs/miniupnpc/src/upnpdev.c @@ -0,0 +1,23 @@ +/* $Id: upnpdev.c,v 1.1 2015/08/28 12:14:19 nanard Exp $ */ +/* Project : miniupnp + * Web : http://miniupnp.free.fr/ + * Author : Thomas BERNARD + * copyright (c) 2005-2015 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#include +#include "upnpdev.h" + +/* freeUPNPDevlist() should be used to + * free the chained list returned by upnpDiscover() */ +void freeUPNPDevlist(struct UPNPDev * devlist) +{ + struct UPNPDev * next; + while(devlist) + { + next = devlist->pNext; + free(devlist); + devlist = next; + } +} + diff --git a/libs/miniupnpc/upnperrors.c b/libs/miniupnpc/src/upnperrors.c similarity index 69% rename from libs/miniupnpc/upnperrors.c rename to libs/miniupnpc/src/upnperrors.c index a48ae10d3..eec403738 100644 --- a/libs/miniupnpc/upnperrors.c +++ b/libs/miniupnpc/src/upnperrors.c @@ -1,9 +1,10 @@ -/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */ -/* Project : miniupnp +/* $Id: upnperrors.c,v 1.11 2023/05/29 21:59:15 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas BERNARD - * copyright (c) 2007 Thomas Bernard + * copyright (c) 2007-2023 Thomas Bernard * All Right reserved. - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include @@ -24,10 +25,17 @@ const char * strupnperror(int err) case UPNPCOMMAND_INVALID_ARGS: s = "Miniupnpc Invalid Arguments"; break; + case UPNPCOMMAND_INVALID_RESPONSE: + s = "Miniupnpc Invalid response"; + break; + case UPNPCOMMAND_HTTP_ERROR: + s = "Miniupnpc HTTP error"; + break; case UPNPDISCOVER_SOCKET_ERROR: s = "Miniupnpc Socket error"; break; case UPNPDISCOVER_MEMORY_ERROR: + case UPNPCOMMAND_MEM_ALLOC_ERROR: s = "Miniupnpc Memory allocation error"; break; case 401: @@ -39,6 +47,24 @@ const char * strupnperror(int err) case 501: s = "Action Failed"; break; + case 600: + s = "Argument Value Invalid"; + break; + case 601: + s = "Argument Value Out of Range"; + break; + case 602: + s = "Optional Action Not Implemented"; + break; + case 603: + s = "Out of Memory"; + break; + case 604: + s = "Human Intervention Required"; + break; + case 605: + s = "String Argument Too Long"; + break; case 606: s = "Action not authorized"; break; @@ -64,7 +90,7 @@ const char * strupnperror(int err) s = "ProtocolWildcardingNotAllowed"; break; case 708: - s = "WildcardNotPermittedInSrcIP"; + s = "InvalidLayer2Address"; break; case 709: s = "NoPacketSent"; @@ -97,7 +123,8 @@ const char * strupnperror(int err) s = "ExternalPortOnlySupportsWildcard"; break; default: - s = NULL; + s = "UnknownError"; + break; } return s; } diff --git a/libs/miniupnpc/upnpreplyparse.c b/libs/miniupnpc/src/upnpreplyparse.c similarity index 58% rename from libs/miniupnpc/upnpreplyparse.c rename to libs/miniupnpc/src/upnpreplyparse.c index 482030b35..afbfcc1af 100644 --- a/libs/miniupnpc/upnpreplyparse.c +++ b/libs/miniupnpc/src/upnpreplyparse.c @@ -1,7 +1,8 @@ -/* $Id: upnpreplyparse.c,v 1.11 2011/02/07 16:17:06 nanard Exp $ */ -/* MiniUPnP project +/* $Id: upnpreplyparse.c,v 1.21 2019/04/08 13:30:51 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2011 Thomas Bernard + * (c) 2006-2019 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -15,25 +16,77 @@ static void NameValueParserStartElt(void * d, const char * name, int l) { - struct NameValueParserData * data = (struct NameValueParserData *)d; + struct NameValueParserData * data = (struct NameValueParserData *)d; + data->topelt = 1; if(l>63) l = 63; memcpy(data->curelt, name, l); data->curelt[l] = '\0'; + data->cdata = NULL; + data->cdatalen = 0; +} + +static void +NameValueParserEndElt(void * d, const char * name, int namelen) +{ + struct NameValueParserData * data = (struct NameValueParserData *)d; + struct NameValue * nv; + (void)name; + (void)namelen; + if(!data->topelt) + return; + if(strcmp(data->curelt, "NewPortListing") != 0) + { + int l; + /* standard case. Limited to n chars strings */ + l = data->cdatalen; + nv = malloc(sizeof(struct NameValue)); + if(nv == NULL) + { + /* malloc error */ +#ifdef DEBUG + fprintf(stderr, "%s: error allocating memory", + "NameValueParserEndElt"); +#endif /* DEBUG */ + return; + } + if(l>=(int)sizeof(nv->value)) + l = sizeof(nv->value) - 1; + strncpy(nv->name, data->curelt, 64); + nv->name[63] = '\0'; + if(data->cdata != NULL) + { + memcpy(nv->value, data->cdata, l); + nv->value[l] = '\0'; + } + else + { + nv->value[0] = '\0'; + } + nv->l_next = data->l_head; /* insert in list */ + data->l_head = nv; + } + data->cdata = NULL; + data->cdatalen = 0; + data->topelt = 0; } static void NameValueParserGetData(void * d, const char * datas, int l) { struct NameValueParserData * data = (struct NameValueParserData *)d; - struct NameValue * nv; if(strcmp(data->curelt, "NewPortListing") == 0) { /* specific case for NewPortListing which is a XML Document */ + free(data->portListing); data->portListing = malloc(l + 1); if(!data->portListing) { /* malloc error */ +#ifdef DEBUG + fprintf(stderr, "%s: error allocating memory", + "NameValueParserGetData"); +#endif /* DEBUG */ return; } memcpy(data->portListing, datas, l); @@ -42,15 +95,9 @@ NameValueParserGetData(void * d, const char * datas, int l) } else { - /* standard case. Limited to 63 chars strings */ - nv = malloc(sizeof(struct NameValue)); - if(l>63) - l = 63; - strncpy(nv->name, data->curelt, 64); - nv->name[63] = '\0'; - memcpy(nv->value, datas, l); - nv->value[l] = '\0'; - LIST_INSERT_HEAD( &(data->head), nv, entries); + /* standard case. */ + data->cdata = datas; + data->cdatalen = l; } } @@ -58,19 +105,17 @@ void ParseNameValue(const char * buffer, int bufsize, struct NameValueParserData * data) { - struct xmlparser parser; - LIST_INIT(&(data->head)); - data->portListing = NULL; - data->portListingLength = 0; - /* init xmlparser object */ - parser.xmlstart = buffer; - parser.xmlsize = bufsize; - parser.data = data; - parser.starteltfunc = NameValueParserStartElt; - parser.endeltfunc = 0; - parser.datafunc = NameValueParserGetData; + struct xmlparser parser; + memset(data, 0, sizeof(struct NameValueParserData)); + /* init xmlparser object */ + parser.xmlstart = buffer; + parser.xmlsize = bufsize; + parser.data = data; + parser.starteltfunc = NameValueParserStartElt; + parser.endeltfunc = NameValueParserEndElt; + parser.datafunc = NameValueParserGetData; parser.attfunc = 0; - parsexml(&parser); + parsexml(&parser); } void @@ -83,22 +128,22 @@ ClearNameValueList(struct NameValueParserData * pdata) pdata->portListing = NULL; pdata->portListingLength = 0; } - while((nv = pdata->head.lh_first) != NULL) + while((nv = pdata->l_head) != NULL) { - LIST_REMOVE(nv, entries); + pdata->l_head = nv->l_next; free(nv); } } -char * +char * GetValueFromNameValueList(struct NameValueParserData * pdata, const char * Name) { struct NameValue * nv; char * p = NULL; - for(nv = pdata->head.lh_first; + for(nv = pdata->l_head; (nv != NULL) && (p == NULL); - nv = nv->entries.le_next) + nv = nv->l_next) { if(strcmp(nv->name, Name) == 0) p = nv->value; @@ -131,7 +176,7 @@ GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, } #endif -/* debug all-in-one function +/* debug all-in-one function * do parsing then display to stdout */ #ifdef DEBUG void @@ -140,13 +185,13 @@ DisplayNameValueList(char * buffer, int bufsize) struct NameValueParserData pdata; struct NameValue * nv; ParseNameValue(buffer, bufsize, &pdata); - for(nv = pdata.head.lh_first; + for(nv = pdata.l_head; nv != NULL; - nv = nv->entries.le_next) + nv = nv->l_next) { printf("%s = %s\n", nv->name, nv->value); } ClearNameValueList(&pdata); } -#endif +#endif /* DEBUG */ diff --git a/libs/miniupnpc/src/win32_snprintf.h b/libs/miniupnpc/src/win32_snprintf.h new file mode 100644 index 000000000..1fc284ecf --- /dev/null +++ b/libs/miniupnpc/src/win32_snprintf.h @@ -0,0 +1,71 @@ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * MiniUPnP project + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * (c) 2020 Pali RohĂĄr + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ + +#ifndef WIN32_SNPRINTF_H +#define WIN32_SNPRINTF_H + +#ifdef _WIN32 + +#include + +/* snprintf is supported by: + * - Visual Studio 2015 or new + * - mingw32 with iso c ext + * - mingw-w64 with ansi stdio + * - mingw-w64 6.0.0 or new with ucrt + * - mingw-w64 8.0.0 or new with iso c ext + */ +#if ( \ + (defined(_MSC_VER) && _MSC_VER < 1900) /* Visual Studio older than 2015 */ || \ + (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) && defined(__NO_ISOCEXT)) /* mingw32 without iso c ext */ || \ + (defined(__MINGW64_VERSION_MAJOR) && /* mingw-w64 not ... */ !( \ + (defined (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO != 0)) /* ... with ansi stdio */ || \ + (__MINGW64_VERSION_MAJOR >= 6 && defined(_UCRT)) /* ... at least 6.0.0 with ucrt */ || \ + (__MINGW64_VERSION_MAJOR >= 8 && !defined(__NO_ISOCEXT)) /* ... at least 8.0.0 with iso c ext */ || \ + 0) || \ +0) + +/* _scprintf is supported by: + * - Visual Studio 2002 or new + * - msvcr70.dll or new + * - msvcrt.dll on Windows XP or new + */ +#if ( \ + (defined(_MSC_VER) && _MSC_VER < 1300) /* Visual Studio older than 2002 */ || \ + (defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x700) /* msvcrt older than 7.0 */ || \ +0) +#define CHECK_SCPRINTF 0 +#define IF_SCPRINTF(expr) 0 +#define ELSE_SCPRINTF(expr) expr +#else +#define CHECK_SCPRINTF 1 +#define IF_SCPRINTF(expr) expr +#define ELSE_SCPRINTF(expr) 0 +#endif + +/* Emulation of snprintf for win32 */ +#define snprintf(buf, size, fmt, ...) ( \ + (((size) != 0 && (buf) != NULL) ? ( /* _snprintf does not work with NULL buffer */ \ + _snprintf((buf), (size), (fmt), __VA_ARGS__), /* _snprintf returns -1 on overflow, so ignore its value */ \ + (((char *)buf)[(size_t)(size)-1] = 0), /* _snprintf does not fill nul byte on overflow */ \ + 0) : 0), \ + (CHECK_SCPRINTF ? IF_SCPRINTF( \ + _scprintf((fmt), __VA_ARGS__) /* calculate return value for snprintf via _scprintf */ \ + ) : ELSE_SCPRINTF( \ + ((size) != 0 && (buf) != NULL) ? \ + strlen((buf)) /* return just length of buffer */ \ + : \ + 1 /* no buffer, impossible to calculate, return just non-zero number */ \ + ) \ + ) \ +) + +#endif + +#endif /* _WIN32 */ + +#endif /* WIN32_SNPRINTF_H */ diff --git a/libs/miniupnpc/testdesc/linksys_WAG200G_desc.values b/libs/miniupnpc/testdesc/linksys_WAG200G_desc.values new file mode 100644 index 000000000..cf4222187 --- /dev/null +++ b/libs/miniupnpc/testdesc/linksys_WAG200G_desc.values @@ -0,0 +1,14 @@ +# values for linksys_WAG200G_desc.xml + +CIF: + servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + controlurl = /upnp/control/WANCommonIFC1 + eventsuburl = /upnp/event/WANCommonIFC1 + scpdurl = /cmnicfg.xml + +first: + servicetype = urn:schemas-upnp-org:service:WANPPPConnection:1 + controlurl = /upnp/control/WANPPPConn1 + eventsuburl = /upnp/event/WANPPPConn1 + scpdurl = /pppcfg.xml + diff --git a/libs/miniupnpc/testdesc/linksys_WAG200G_desc.xml b/libs/miniupnpc/testdesc/linksys_WAG200G_desc.xml new file mode 100644 index 000000000..d428d73b0 --- /dev/null +++ b/libs/miniupnpc/testdesc/linksys_WAG200G_desc.xml @@ -0,0 +1,110 @@ + + + +1 +0 + +http://192.168.1.1:49152 + +urn:schemas-upnp-org:device:InternetGatewayDevice:1 +LINKSYS WAG200G Gateway +LINKSYS +http://www.linksys.com +LINKSYS WAG200G Gateway +Wireless-G ADSL Home Gateway +WAG200G +http://www.linksys.com +123456789 +uuid:8ca2eb37-1dd2-11b2-86f1-001a709b5aa8 +WAG200G + + +urn:schemas-upnp-org:service:Layer3Forwarding:1 +urn:upnp-org:serviceId:L3Forwarding1 +/upnp/control/L3Forwarding1 +/upnp/event/L3Forwarding1 +/l3frwd.xml + + + + +urn:schemas-upnp-org:device:WANDevice:1 +WANDevice +LINKSYS +http://www.linksys.com/ +Residential Gateway +Internet Connection Sharing +1 +http://www.linksys.com/ +0000001 +uuid:8ca2eb36-1dd2-11b2-86f1-001a709b5aa8 +WAG200G + + +urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 +urn:upnp-org:serviceId:WANCommonIFC1 +/upnp/control/WANCommonIFC1 +/upnp/event/WANCommonIFC1 +/cmnicfg.xml + + + + +urn:schemas-upnp-org:device:WANConnectionDevice:1 +WANConnectionDevice +LINKSYS +http://www.linksys.com/ +Residential Gateway +Internet Connection Sharing +1 +http://www.linksys.com/ +0000001 +uuid:8ca2eb37-1dd2-11b2-86f0-001a709b5aa8 +WAG200G + + +urn:schemas-upnp-org:service:WANEthernetLinkConfig:1 +urn:upnp-org:serviceId:WANEthLinkC1 +/upnp/control/WANEthLinkC1 +/upnp/event/WANEthLinkC1 +/wanelcfg.xml + + +urn:schemas-upnp-org:service:WANPPPConnection:1 +urn:upnp-org:serviceId:WANPPPConn1 +/upnp/control/WANPPPConn1 +/upnp/event/WANPPPConn1 +/pppcfg.xml + + + + + + +urn:schemas-upnp-org:device:LANDevice:1 +LANDevice +LINKSYS +http://www.linksys.com/ +Residential Gateway +Residential Gateway +1 +http://www.linksys.com/ +0000001 +uuid:8ca2eb36-1dd2-11b2-86f0-001a709b5aa +8 +WAG200G + + +urn:schemas-upnp-org:service:LANHostConfigManagement:1 +urn:upnp-org:serviceId:LANHostCfg1 +/upnp/control/LANHostCfg1 +/upnp/event/LANHostCfg1 +/lanhostc.xml + + + + +http://192.168.1.1/index.htm + + + diff --git a/libs/miniupnpc/testdesc/new_LiveBox_desc.values b/libs/miniupnpc/testdesc/new_LiveBox_desc.values new file mode 100644 index 000000000..c55552e58 --- /dev/null +++ b/libs/miniupnpc/testdesc/new_LiveBox_desc.values @@ -0,0 +1,20 @@ +# values for new_LiveBox_desc.xml + +CIF: + servicetype = urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + controlurl = /87895a19/upnp/control/WANCommonIFC1 + eventsuburl = /87895a19/upnp/control/WANCommonIFC1 + scpdurl = /87895a19/gateicfgSCPD.xml + +first: + servicetype = urn:schemas-upnp-org:service:WANPPPConnection:2 + controlurl = /87895a19/upnp/control/WANIPConn1 + eventsuburl = /87895a19/upnp/control/WANIPConn1 + scpdurl = /87895a19/gateconnSCPD_PPP.xml + +IPv6FC: + servicetype = urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + controlurl = /87895a19/upnp/control/WANIPv6FwCtrl1 + eventsuburl = /87895a19/upnp/control/WANIPv6FwCtrl1 + scpdurl = /87895a19/wanipv6fwctrlSCPD.xml + diff --git a/libs/miniupnpc/testdesc/new_LiveBox_desc.xml b/libs/miniupnpc/testdesc/new_LiveBox_desc.xml new file mode 100644 index 000000000..3e522df59 --- /dev/null +++ b/libs/miniupnpc/testdesc/new_LiveBox_desc.xml @@ -0,0 +1,90 @@ + + + + 1 + 0 + + + VEN_0129&DEV_0000&SUBSYS_03&REV_250417 + GenericUmPass + NetworkInfrastructure.Gateway + Network.Gateway + urn:schemas-upnp-org:device:InternetGatewayDevice:2 + Orange Livebox + Sagemcom + http://www.sagemcom.com/ + Residential Livebox,(DSL,WAN Ethernet) + uuid:87895a19-50f9-3736-a87f-115c230155f8 + Sagemcom,fr,SG30_sip-fr-4.28.35.1 + 3 + LK14129DP441489 + http://192.168.1.1 + + + + image/png + 16 + 16 + 8 + /87895a19/ligd.png + + + + + urn:schemas-upnp-org:device:WANDevice:2 + WANDevice + Sagemcom + http://www.sagemcom.com/ + WAN Device on Sagemcom,fr,SG30_sip-fr-4.28.35.1 + Residential Livebox,(DSL,WAN Ethernet) + 3 + http://www.sagemcom.com/ + LK14129DP441489 + http://192.168.1.1 + uuid:e2397374-53d8-3fc6-8306-593ba1a34625 + + + + urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + urn:upnp-org:serviceId:WANCommonIFC1 + /87895a19/upnp/control/WANCommonIFC1 + /87895a19/upnp/control/WANCommonIFC1 + /87895a19/gateicfgSCPD.xml + + + + + urn:schemas-upnp-org:device:WANConnectionDevice:2 + WANConnectionDevice + Sagemcom + http://www.sagemcom.com/ + WanConnectionDevice on Sagemcom,fr,SG30_sip-fr-4.28.35.1 + Residential Livebox,(DSL,WAN Ethernet) + 3 + http://www.sagemcom.com/ + LK14129DP441489 + http://192.168.1.1 + uuid:44598a08-288e-32c9-8a4d-d3c008ede331 + + + + urn:schemas-upnp-org:service:WANPPPConnection:2 + urn:upnp-org:serviceId:WANIPConn1 + /87895a19/upnp/control/WANIPConn1 + /87895a19/upnp/control/WANIPConn1 + /87895a19/gateconnSCPD_PPP.xml + + + urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + urn:upnp-org:serviceId:WANIPv6FwCtrl1 + /87895a19/upnp/control/WANIPv6FwCtrl1 + /87895a19/upnp/control/WANIPv6FwCtrl1 + /87895a19/wanipv6fwctrlSCPD.xml + + + + + + + + \ No newline at end of file diff --git a/libs/miniupnpc/testigddescparse.c b/libs/miniupnpc/testigddescparse.c deleted file mode 100644 index 1b0cde90e..000000000 --- a/libs/miniupnpc/testigddescparse.c +++ /dev/null @@ -1,64 +0,0 @@ -/* $Id: testigddescparse.c,v 1.2 2009/12/03 13:50:06 nanard Exp $ */ -/* Project : miniupnp - * http://miniupnp.free.fr/ - * Author : Thomas Bernard - * Copyright (c) 2008-2009 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * */ -#include -#include -#include -#include "igd_desc_parse.h" -#include "minixml.h" -#include "miniupnpc.h" - -int test_igd_desc_parse(char * buffer, int len) -{ - struct IGDdatas igd; - struct xmlparser parser; - struct UPNPUrls urls; - memset(&igd, 0, sizeof(struct IGDdatas)); - memset(&parser, 0, sizeof(struct xmlparser)); - parser.xmlstart = buffer; - parser.xmlsize = len; - parser.data = &igd; - parser.starteltfunc = IGDstartelt; - parser.endeltfunc = IGDendelt; - parser.datafunc = IGDdata; - parsexml(&parser); - printIGD(&igd); - GetUPNPUrls(&urls, &igd, "http://fake/desc/url/file.xml"); - printf("ipcondescURL='%s'\n", urls.ipcondescURL); - printf("controlURL='%s'\n", urls.controlURL); - printf("controlURL_CIF='%s'\n", urls.controlURL_CIF); - FreeUPNPUrls(&urls); - return 0; -} - -int main(int argc, char * * argv) -{ - FILE * f; - char * buffer; - int len; - int r = 0; - if(argc<2) { - fprintf(stderr, "Usage: %s file.xml\n", argv[0]); - return 1; - } - f = fopen(argv[1], "r"); - if(!f) { - fprintf(stderr, "Cannot open %s for reading.\n", argv[1]); - return 1; - } - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, 0, SEEK_SET); - buffer = malloc(len); - fread(buffer, 1, len, f); - fclose(f); - r = test_igd_desc_parse(buffer, len); - free(buffer); - return r; -} - diff --git a/libs/miniupnpc/testminiwget.sh b/libs/miniupnpc/testminiwget.sh index c048e5bd1..80f90481e 100755 --- a/libs/miniupnpc/testminiwget.sh +++ b/libs/miniupnpc/testminiwget.sh @@ -1,42 +1,91 @@ #!/bin/sh -# $Id: testminiwget.sh,v 1.4 2011/05/09 08:53:15 nanard Exp $ +# $Id: testminiwget.sh,v 1.20 2022/10/21 21:09:42 nanard Exp $ +# vim: tabstop=4 shiftwidth=4 noexpandtab # project miniupnp : http://miniupnp.free.fr/ -# (c) 2011 Thomas Bernard +# or https://miniupnp.tuxfamily.org/ +# (c) 2011-2022 Thomas Bernard # # test program for miniwget.c # is usually invoked by "make check" # # This test program : # 1 - launches a local HTTP server (minihttptestserver) -# 2 - uses testminiwget to retreive data from this server +# 2 - uses testminiwget to retrieve data from this server # 3 - compares served and received data # 4 - kills the local HTTP server and exits # +# The script was tested and works with ksh, bash +# it should now also run with dash -HTTPSERVEROUT=/tmp/httpserverout -EXPECTEDFILE=/tmp/expectedfile -DOWNLOADEDFILE=/tmp/downloadedfile -#ADDR=localhost -ADDR="[::1]" +TMPD=`mktemp -d -t miniwgetXXXXXXXXXX` +if [ -z "$TESTSERVER" ] ; then + TESTSERVER=./build/minihttptestserver +fi +if [ -z "$TESTMINIWGET" ] ; then + TESTMINIWGET=./build/testminiwget +fi +HTTPSERVEROUT="${TMPD}/httpserverout" +EXPECTEDFILE="${TMPD}/expectedfile" +DOWNLOADEDFILE="${TMPD}/downloadedfile" PORT= RET=0 +IPCONFIG=$(which ifconfig) +IP=$(which ip) +if [ "$IP" ] ; then + if ! $IP addr | grep inet6 ; then + HAVE_IPV6=no + fi +else + if [ -z "$IPCONFIG" ] ; then + IPCONFIG="/sbin/ifconfig" + fi -#make minihttptestserver -#make testminiwget + if ! $IPCONFIG -a | grep inet6 ; then + HAVE_IPV6=no + fi +fi + +case "$HAVE_IPV6" in + n|no|0) + ADDR=localhost + SERVERARGS="" + ;; + *) + ADDR="[::1]" + SERVERARGS="-6" + ;; + +esac + +if [ ! -x "$TESTSERVER" ] || [ ! -x "$TESTMINIWGET" ] ; then + echo "Please build $TESTSERVER and $TESTMINIWGET" + #make minihttptestserver + #make testminiwget + exit 1 +fi # launching the test HTTP server -./minihttptestserver -6 -e $EXPECTEDFILE > $HTTPSERVEROUT & -while [ "$PORT" == "" ]; do +$TESTSERVER $SERVERARGS -e $EXPECTEDFILE > $HTTPSERVEROUT & +SERVERPID=$! +while [ -z "$PORT" ]; do + sleep 1 PORT=`cat $HTTPSERVEROUT | sed 's/Listening on port \([0-9]*\)/\1/' ` done +if [ "$PORT" = "*** ERROR ***" ]; then + echo "HTTP test server error" + echo "Network config :" + $IPCONFIG -a + exit 2 +fi echo "Test HTTP server is listening on $PORT" URL1="http://$ADDR:$PORT/index.html" URL2="http://$ADDR:$PORT/chunked" URL3="http://$ADDR:$PORT/addcrap" +URL4="http://$ADDR:$PORT/malformed" echo "standard test ..." -./testminiwget $URL1 "${DOWNLOADEDFILE}.1" +$TESTMINIWGET $URL1 "${DOWNLOADEDFILE}.1" if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.1" ; then echo "ok" else @@ -45,7 +94,7 @@ else fi echo "chunked transfert encoding test ..." -./testminiwget $URL2 "${DOWNLOADEDFILE}.2" +$TESTMINIWGET $URL2 "${DOWNLOADEDFILE}.2" if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.2" ; then echo "ok" else @@ -54,7 +103,7 @@ else fi echo "response too long test ..." -./testminiwget $URL3 "${DOWNLOADEDFILE}.3" +$TESTMINIWGET $URL3 "${DOWNLOADEDFILE}.3" if cmp $EXPECTEDFILE "${DOWNLOADEDFILE}.3" ; then echo "ok" else @@ -62,9 +111,12 @@ else RET=1 fi +echo "malformed response test ..." +$TESTMINIWGET $URL4 "${DOWNLOADEDFILE}.4" + # kill the test HTTP server -kill %1 -wait %1 +kill $SERVERPID +wait $SERVERPID # remove temporary files (for success cases) if [ $RET -eq 0 ]; then @@ -72,8 +124,10 @@ if [ $RET -eq 0 ]; then rm -f "${DOWNLOADEDFILE}.2" rm -f "${DOWNLOADEDFILE}.3" rm -f $EXPECTEDFILE $HTTPSERVEROUT + rmdir ${TMPD} else echo "at least one of the test FAILED" + echo "directory ${TMPD} is left intact" fi exit $RET diff --git a/libs/miniupnpc/testreplyparse/DeletePortMapping.namevalue b/libs/miniupnpc/testreplyparse/DeletePortMapping.namevalue new file mode 100644 index 000000000..48ca0cccb --- /dev/null +++ b/libs/miniupnpc/testreplyparse/DeletePortMapping.namevalue @@ -0,0 +1,3 @@ +NewRemoteHost= +NewExternalPort=123 +NewProtocol=TCP diff --git a/libs/miniupnpc/testreplyparse/DeletePortMapping.xml b/libs/miniupnpc/testreplyparse/DeletePortMapping.xml new file mode 100644 index 000000000..a955c53fc --- /dev/null +++ b/libs/miniupnpc/testreplyparse/DeletePortMapping.xml @@ -0,0 +1,6 @@ + +123 +TCP + + + diff --git a/libs/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue b/libs/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue new file mode 100644 index 000000000..5aa75f882 --- /dev/null +++ b/libs/miniupnpc/testreplyparse/GetExternalIPAddress.namevalue @@ -0,0 +1,2 @@ +NewExternalIPAddress=1.2.3.4 + diff --git a/libs/miniupnpc/testreplyparse/GetExternalIPAddress.xml b/libs/miniupnpc/testreplyparse/GetExternalIPAddress.xml new file mode 100644 index 000000000..db7ec1f9c --- /dev/null +++ b/libs/miniupnpc/testreplyparse/GetExternalIPAddress.xml @@ -0,0 +1,2 @@ +1.2.3.4 + diff --git a/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue new file mode 100644 index 000000000..26b169c35 --- /dev/null +++ b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.namevalue @@ -0,0 +1,3 @@ +NewProtocol=UDP +NewExternalPort=12345 +NewRemoteHost= diff --git a/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml new file mode 100644 index 000000000..bbb540eac --- /dev/null +++ b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryReq.xml @@ -0,0 +1,3 @@ + +12345UDP + diff --git a/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue new file mode 100644 index 000000000..2189789b4 --- /dev/null +++ b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.namevalue @@ -0,0 +1,5 @@ +NewInternalPort=12345 +NewInternalClient=192.168.10.110 +NewEnabled=1 +NewPortMappingDescription=libminiupnpc +NewLeaseDuration=0 diff --git a/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml new file mode 100644 index 000000000..77e8d9c7c --- /dev/null +++ b/libs/miniupnpc/testreplyparse/GetSpecificPortMappingEntryResp.xml @@ -0,0 +1,2 @@ +12345192.168.10.1101libminiupnpc0 + diff --git a/libs/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue b/libs/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue new file mode 100644 index 000000000..f78c7e2ae --- /dev/null +++ b/libs/miniupnpc/testreplyparse/SetDefaultConnectionService.namevalue @@ -0,0 +1 @@ +NewDefaultConnectionService=uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID diff --git a/libs/miniupnpc/testreplyparse/SetDefaultConnectionService.xml b/libs/miniupnpc/testreplyparse/SetDefaultConnectionService.xml new file mode 100644 index 000000000..ac04c07a9 --- /dev/null +++ b/libs/miniupnpc/testreplyparse/SetDefaultConnectionService.xml @@ -0,0 +1 @@ +uuid:c6c05a33-f704-48df-9910-e099b3471d81:WANConnectionDevice:1,INVALID_SERVICE_ID diff --git a/libs/miniupnpc/testreplyparse/readme.txt b/libs/miniupnpc/testreplyparse/readme.txt new file mode 100644 index 000000000..3eb1f015f --- /dev/null +++ b/libs/miniupnpc/testreplyparse/readme.txt @@ -0,0 +1,7 @@ +This directory contains files used for validation of upnpreplyparse.c code. + +Each .xml file to parse should give the results which are in the .namevalue +file. + +A .namevalue file contain name=value lines. + diff --git a/libs/miniupnpc/testupnpigd.py b/libs/miniupnpc/testupnpigd.py index 6d167a4ce..806b4f4f8 100644 --- a/libs/miniupnpc/testupnpigd.py +++ b/libs/miniupnpc/testupnpigd.py @@ -1,14 +1,18 @@ -#! /usr/bin/python -# $Id: testupnpigd.py,v 1.4 2008/10/11 10:27:20 nanard Exp $ +#! /usr/bin/env python +# $Id: testupnpigd.py,v 1.7 2020/04/06 10:23:02 nanard Exp $ # MiniUPnP project # Author : Thomas Bernard # This Sample code is public domain. -# website : http://miniupnp.tuxfamily.org/ +# website : https://miniupnp.tuxfamily.org/ # import the python miniupnpc module import miniupnpc import socket -import BaseHTTPServer + +try: + from http.server import BaseHTTPRequestHandler, HTTPServer +except ImportError: + from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer # function definition def list_redirections(): @@ -17,15 +21,15 @@ def list_redirections(): p = u.getgenericportmapping(i) if p==None: break - print i, p + print(i, p) i = i + 1 #define the handler class for HTTP connections -class handler_class(BaseHTTPServer.BaseHTTPRequestHandler): +class handler_class(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() - self.wfile.write("OK MON GARS") + self.wfile.write(b"OK MON GARS") # create the object u = miniupnpc.UPnP() @@ -37,20 +41,20 @@ u = miniupnpc.UPnP() u.discoverdelay = 200; try: - print 'Discovering... delay=%ums' % u.discoverdelay + print('Discovering... delay=%ums' % u.discoverdelay) ndevices = u.discover() - print ndevices, 'device(s) detected' + print(ndevices, 'device(s) detected') # select an igd u.selectigd() # display information about the IGD and the internet connection - print 'local ip address :', u.lanaddr + print('local ip address :', u.lanaddr) externalipaddress = u.externalipaddress() - print 'external ip address :', externalipaddress - print u.statusinfo(), u.connectiontype() + print('external ip address :', externalipaddress) + print(u.statusinfo(), u.connectiontype()) #instanciate a HTTPd object. The port is assigned by the system. - httpd = BaseHTTPServer.HTTPServer((u.lanaddr, 0), handler_class) + httpd = HTTPServer((u.lanaddr, 0), handler_class) eport = httpd.server_port # find a free port for the redirection @@ -59,26 +63,26 @@ try: eport = eport + 1 r = u.getspecificportmapping(eport, 'TCP') - print 'trying to redirect %s port %u TCP => %s port %u TCP' % (externalipaddress, eport, u.lanaddr, httpd.server_port) + print('trying to redirect %s port %u TCP => %s port %u TCP' % (externalipaddress, eport, u.lanaddr, httpd.server_port)) b = u.addportmapping(eport, 'TCP', u.lanaddr, httpd.server_port, 'UPnP IGD Tester port %u' % eport, '') if b: - print 'Success. Now waiting for some HTTP request on http://%s:%u' % (externalipaddress ,eport) + print('Success. Now waiting for some HTTP request on http://%s:%u' % (externalipaddress ,eport)) try: httpd.handle_request() httpd.server_close() - except KeyboardInterrupt, details: - print "CTRL-C exception!", details + except KeyboardInterrupt as details: + print("CTRL-C exception!", details) b = u.deleteportmapping(eport, 'TCP') if b: - print 'Successfully deleted port mapping' + print('Successfully deleted port mapping') else: - print 'Failed to remove port mapping' + print('Failed to remove port mapping') else: - print 'Failed' + print('Failed') httpd.server_close() -except Exception, e: - print 'Exception :', e +except Exception as e: + print('Exception :', e) diff --git a/libs/miniupnpc/testupnpreplyparse.c b/libs/miniupnpc/testupnpreplyparse.c deleted file mode 100644 index a02e8f6a6..000000000 --- a/libs/miniupnpc/testupnpreplyparse.c +++ /dev/null @@ -1,44 +0,0 @@ -/* $Id: testupnpreplyparse.c,v 1.2 2008/02/21 13:05:27 nanard Exp $ */ -/* MiniUPnP project - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2007 Thomas Bernard - * This software is subject to the conditions detailed - * in the LICENCE file provided within the distribution */ -#include -#include -#include "upnpreplyparse.h" - -void -test_parsing(const char * buf, int len) -{ - struct NameValueParserData pdata; - ParseNameValue(buf, len, &pdata); - ClearNameValueList(&pdata); -} - -int main(int argc, char * * argv) -{ - FILE * f; - char buffer[4096]; - int l; - if(argc<2) - { - fprintf(stderr, "Usage: %s file.xml\n", argv[0]); - return 1; - } - f = fopen(argv[1], "r"); - if(!f) - { - fprintf(stderr, "Error : can not open file %s\n", argv[1]); - return 2; - } - l = fread(buffer, 1, sizeof(buffer)-1, f); - fclose(f); - buffer[l] = '\0'; -#ifdef DEBUG - DisplayNameValueList(buffer, l); -#endif - test_parsing(buffer, l); - return 0; -} - diff --git a/libs/miniupnpc/testupnpreplyparse.sh b/libs/miniupnpc/testupnpreplyparse.sh new file mode 100644 index 000000000..a917c4b84 --- /dev/null +++ b/libs/miniupnpc/testupnpreplyparse.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +if [ -z "$TESTUPNPREPLYPARSE" ] ; then + TESTUPNPREPLYPARSE=./build/testupnpreplyparse +fi + +for f in testreplyparse/*.xml ; do + bf="`dirname $f`/`basename $f .xml`" + if $TESTUPNPREPLYPARSE $f $bf.namevalue ; then + echo "$f : passed" + else + echo "$f : FAILED" + exit 1 + fi +done + +exit 0 diff --git a/libs/miniupnpc/updateminiupnpcstrings.sh b/libs/miniupnpc/updateminiupnpcstrings.sh index dde4354a8..7c42eea5b 100755 --- a/libs/miniupnpc/updateminiupnpcstrings.sh +++ b/libs/miniupnpc/updateminiupnpcstrings.sh @@ -1,12 +1,23 @@ #! /bin/sh -# $Id: updateminiupnpcstrings.sh,v 1.7 2011/01/04 11:41:53 nanard Exp $ +# $Id: updateminiupnpcstrings.sh,v 1.10 2022/10/16 05:28:15 nanard Exp $ # project miniupnp : http://miniupnp.free.fr/ -# (c) 2009 Thomas Bernard +# (c) 2009-2021 Thomas Bernard FILE=miniupnpcstrings.h -TMPFILE=miniupnpcstrings.h.tmp TEMPLATE_FILE=${FILE}.in +if [ -n "$1" ] ; then + FILE="$1" +fi +if [ -n "$2" ] ; then + TEMPLATE_FILE="$2" +fi +TMPFILE=`mktemp -t miniupnpcstringsXXXXXX` +if [ ! -f "$TMPFILE" ] ; then + echo "mktemp failure" + exit 1 +fi + # detecting the OS name and version OS_NAME=`uname -s` OS_VERSION=`uname -r` @@ -14,11 +25,14 @@ if [ -f /etc/debian_version ]; then OS_NAME=Debian OS_VERSION=`cat /etc/debian_version` fi + # use lsb_release (Linux Standard Base) when available LSB_RELEASE=`which lsb_release` if [ 0 -eq $? -a -x "${LSB_RELEASE}" ]; then - OS_NAME=`${LSB_RELEASE} -i -s` - OS_VERSION=`${LSB_RELEASE} -r -s` + # On NixOS, lsb_release returns strings such as "NixOS" (with quotes), + # so we need to stript them with the following xargs trick: + OS_NAME=`${LSB_RELEASE} -i -s | xargs echo` + OS_VERSION=`${LSB_RELEASE} -r -s | xargs echo` case $OS_NAME in Debian) #OS_VERSION=`${LSB_RELEASE} -c -s` @@ -49,5 +63,5 @@ sed -e "$EXPR" < $TEMPLATE_FILE > $TMPFILE EXPR="s|MINIUPNPC_VERSION_STRING \".*\"|MINIUPNPC_VERSION_STRING \"${MINIUPNPC_VERSION}\"|" echo "setting MINIUPNPC_VERSION_STRING macro value to ${MINIUPNPC_VERSION} in $FILE." sed -e "$EXPR" < $TMPFILE > $FILE -rm $TMPFILE +rm $TMPFILE && echo "$TMPFILE deleted" diff --git a/libs/miniupnpc/wingenminiupnpcstrings.c b/libs/miniupnpc/wingenminiupnpcstrings.c index 38dd01783..6dcbfa4ae 100644 --- a/libs/miniupnpc/wingenminiupnpcstrings.c +++ b/libs/miniupnpc/wingenminiupnpcstrings.c @@ -1,8 +1,8 @@ -/* $Id: wingenminiupnpcstrings.c,v 1.2 2011/01/11 15:31:13 nanard Exp $ */ +/* $Id: wingenminiupnpcstrings.c,v 1.6 2021/08/21 09:43:40 nanard Exp $ */ /* Project: miniupnp - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author: Thomas Bernard - * Copyright (c) 2005-2009 Thomas Bernard + * Copyright (c) 2005-2021 Thomas Bernard * This software is subjects to the conditions detailed * in the LICENSE file provided within this distribution */ #include @@ -26,7 +26,7 @@ int main(int argc, char * * argv) { dwBuildNumber : The build number of the operating system. dwPlatformId - The operating system platform. This member can be the following value. + The operating system platform. This member can be the following value. szCSDVersion A null-terminated string, such as "Service Pack 3", that indicates the latest Service Pack installed on the system. If no Service Pack has @@ -44,7 +44,7 @@ int main(int argc, char * * argv) { fin = fopen("VERSION", "r"); fgets(miniupnpcVersion, sizeof(miniupnpcVersion), fin); fclose(fin); - for(n = 0; n < sizeof(miniupnpcVersion); n++) { + for(n = 0; n < (int)sizeof(miniupnpcVersion); n++) { if(miniupnpcVersion[n] < ' ') miniupnpcVersion[n] = '\0'; } @@ -59,6 +59,7 @@ int main(int argc, char * * argv) { fout = fopen(argv[2], "w"); if(!fout) { fprintf(stderr, "Cannot open %s for writing.\n", argv[2]); + fclose(fin); return 1; } n = 0; @@ -78,5 +79,37 @@ int main(int argc, char * * argv) { fclose(fout); printf("%d lines written to %s.\n", n, argv[2]); } + if(argc >= 4) { + fout = fopen(argv[3], "w"); + if(fout == NULL) { + fprintf(stderr, "Cannot open %s for writing.\n", argv[2]); + return 1; + } else { + char * cur, * next; + fprintf(fout, "#define LIBMINIUPNPC_DOTTED_VERSION \"%s\"\n", miniupnpcVersion); + next = strchr(miniupnpcVersion, '.'); + if (next && *next) { + *next = '\0'; + next++; + } + fprintf(fout, "#define LIBMINIUPNPC_MAJOR_VERSION %s\n", miniupnpcVersion); + cur = next; + next = strchr(cur, '.'); + if (next && *next) { + *next = '\0'; + next++; + } + fprintf(fout, "#define LIBMINIUPNPC_MINOR_VERSION %s\n", cur); + cur = next; + next = strchr(cur, '.'); + if (next && *next) { + *next = '\0'; + next++; + } + fprintf(fout, "#define LIBMINIUPNPC_MICRO_VERSION %s\n", cur); + fclose(fout); + printf("%s written\n", argv[3]); + } + } return 0; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 15d845c29..49f783722 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -254,6 +254,7 @@ target_compile_options(SRB2SDL2 PRIVATE -Winline -Wformat-y2k -Wformat-security + -fwrapv $<$,2.9.5>: -Wno-div-by-zero @@ -277,7 +278,7 @@ target_compile_options(SRB2SDL2 PRIVATE $<$,4.5.0>: -Wlogical-op - -Wno-error=array-bounds + #-Wno-error=array-bounds > $<$,4.6.0>: @@ -323,6 +324,7 @@ target_compile_options(SRB2SDL2 PRIVATE -Wno-error=non-literal-null-conversion -Wno-error=constant-conversion -Wno-error=unused-but-set-variable + -fwrapv > # C, MSVC @@ -376,6 +378,11 @@ endif() if(SRB2_CONFIG_PACKETDROP) target_compile_definitions(SRB2SDL2 PRIVATE -DPACKETDROP) endif() +if(SRB2_CONFIG_EXECINFO) +else() + target_compile_definitions(SRB2SDL2 PRIVATE -DNOEXECINFO) + message(STATUS "You have disabled stack trace dump support") +endif() if(SRB2_CONFIG_ZDEBUG) target_compile_definitions(SRB2SDL2 PRIVATE -DZDEBUG) endif() diff --git a/src/Makefile b/src/Makefile index a0a18be76..6dba19c24 100644 --- a/src/Makefile +++ b/src/Makefile @@ -66,6 +66,7 @@ # NOPOSTPROCESSING=1 - ? # MOBJCONSISTANCY=1 - ?? # PACKETDROP=1 - ?? +# NOEXECINFO=1 - Disable stack trace dump support # DEBUGMODE=1 - Enable various debugging capabilities. # Also disables optimizations. # NOZLIB=1 - Disable some compression capability. Implies @@ -164,7 +165,7 @@ sources:= makedir:=../make # -DCOMPVERSION: flag to use comptime.h -opts:=-DCOMPVERSION -g +opts:=-DCOMPVERSION -g -fwrapv libs:= # This is a list of variables names, of which if defined, diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk index 8b713718c..d132ecc9e 100644 --- a/src/Makefile.d/features.mk +++ b/src/Makefile.d/features.mk @@ -5,7 +5,7 @@ passthru_opts+=\ NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ MOBJCONSISTANCY PACKETDROP ZDEBUG\ - HAVE_MINIUPNPC\ + NOUPNP NOEXECINFO\ # build with debugging information ifdef DEBUGMODE @@ -45,13 +45,17 @@ $(eval $(call Configure,CURL,$(CURLCONFIG))) opts+=-DHAVE_CURL endif -ifdef HAVE_MINIUPNPC -libs+=-lminiupnpc +ifndef NOUPNP +MINIUPNPC_PKGCONFIG?=miniupnpc +$(eval $(call Use_pkg_config,MINIUPNPC)) +HAVE_MINIUPNPC=1 +opts+=-DHAVE_MINIUPNPC endif # (Valgrind is a memory debugger.) ifdef VALGRIND VALGRIND_PKGCONFIG?=valgrind +VALGRIND_LDFLAGS= $(eval $(call Use_pkg_config,VALGRIND)) ZDEBUG=1 opts+=-DHAVE_VALGRIND diff --git a/src/Makefile.d/versions.mk b/src/Makefile.d/versions.mk index d2877b374..b639ad9a1 100644 --- a/src/Makefile.d/versions.mk +++ b/src/Makefile.d/versions.mk @@ -84,6 +84,9 @@ endif WFLAGS+=-Wnested-externs #WFLAGS+=-Wunreachable-code WFLAGS+=-Winline +ifdef DEBUGMODE + WFLAGS+=-Wno-error=inline +endif ifdef GCC43 WFLAGS+=-funit-at-a-time WFLAGS+=-Wlogical-op @@ -116,7 +119,7 @@ ifdef GCC43 #WFLAGS+=-Wno-error=clobbered endif ifdef GCC44 - WFLAGS+=-Wno-error=array-bounds +#WFLAGS+=-Wno-error=array-bounds endif ifdef GCC46 WFLAGS+=-Wno-error=suggest-attribute=noreturn diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk index a858881c0..3e60e2416 100644 --- a/src/Makefile.d/win32.mk +++ b/src/Makefile.d/win32.mk @@ -33,11 +33,6 @@ libs+=-lws2_32 endif endif -ifndef MINGW64 # miniupnc is broken with MINGW64 -opts+=-I../libs -DSTATIC_MINIUPNPC -libs+=-L../libs/miniupnpc/mingw$(32) -lws2_32 -liphlpapi -endif - ifndef MINGW64 32=32 x86=x86 @@ -100,3 +95,8 @@ lib:=../libs/curl CURL_opts:=-I$(lib)/include CURL_libs:=-L$(lib)/lib$(32) -lcurl $(eval $(call _set,CURL)) + +lib:=../libs/miniupnpc +MINIUPNPC_opts:=-I$(lib)/include -DMINIUPNP_STATICLIB +MINIUPNPC_libs:=-L$(lib)/mingw$(32) -lminiupnpc -lws2_32 -liphlpapi +$(eval $(call _set,MINIUPNPC)) diff --git a/src/b_bot.c b/src/b_bot.c index 033288a86..c8228e840 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -584,11 +584,11 @@ void B_RespawnBot(INT32 playernum) P_SetOrigin(tails, x, y, z); if (player->charability == CA_FLY) { - P_SetPlayerMobjState(tails, S_PLAY_FLY); + P_SetMobjState(tails, S_PLAY_FLY); tails->player->powers[pw_tailsfly] = (UINT16)-1; } else - P_SetPlayerMobjState(tails, S_PLAY_FALL); + P_SetMobjState(tails, S_PLAY_FALL); P_SetScale(tails, sonic->scale); tails->destscale = sonic->destscale; } @@ -614,6 +614,9 @@ void B_HandleFlightIndicator(player_t *player) // otherwise, spawn it P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY)); + if (P_MobjWasRemoved(tails->hnext)) + return; // we can't spawn one, so it can't exist + P_SetTarget(&tails->hnext->target, tails); P_SetTarget(&tails->hnext->hprev, tails); P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR); diff --git a/src/blua/lbaselib.c b/src/blua/lbaselib.c index 0fc222038..2bb3d9cf0 100644 --- a/src/blua/lbaselib.c +++ b/src/blua/lbaselib.c @@ -275,18 +275,36 @@ static int luaB_dofile (lua_State *L) { int n = lua_gettop(L); if (!W_FileHasFolders(wadfiles[numwadfiles - 1])) - luaL_error(L, "dofile() only works with PK3 files"); + luaL_error(L, "dofile() only works with PK3 files and folders"); snprintf(fullfilename, sizeof(fullfilename), "Lua/%s", filename); lumpnum = W_CheckNumForFullNamePK3(fullfilename, numwadfiles - 1, 0); if (lumpnum == INT16_MAX) luaL_error(L, "can't find script " LUA_QS, fullfilename); - LUA_LoadLump(numwadfiles - 1, lumpnum, false); + LUA_DoLump(numwadfiles - 1, lumpnum, false); return lua_gettop(L) - n; } +// Edited to load PK3 entries instead +static int luaB_loadfile (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + char fullfilename[256]; + UINT16 lumpnum; + + if (!W_FileHasFolders(wadfiles[numwadfiles - 1])) + luaL_error(L, "loadfile() only works with PK3 files and folders"); + + snprintf(fullfilename, sizeof(fullfilename), "Lua/%s", filename); + lumpnum = W_CheckNumForFullNamePK3(fullfilename, numwadfiles - 1, 0); + if (lumpnum == INT16_MAX) + luaL_error(L, "can't find script " LUA_QS, fullfilename); + + LUA_LoadLump(numwadfiles - 1, lumpnum); + + return 1; +} static int luaB_assert (lua_State *L) { luaL_checkany(L, 1); @@ -406,6 +424,7 @@ static const luaL_Reg base_funcs[] = { {"collectgarbage", luaB_collectgarbage}, {"error", luaB_error}, {"dofile", luaB_dofile}, + {"loadfile", luaB_loadfile}, {"gcinfo", luaB_gcinfo}, {"getfenv", luaB_getfenv}, {"getmetatable", luaB_getmetatable}, diff --git a/src/command.c b/src/command.c index d41a55153..5f6b1c21c 100644 --- a/src/command.c +++ b/src/command.c @@ -2056,7 +2056,7 @@ static void CV_SetValueMaybeStealth(consvar_t *var, INT32 value, boolean stealth if ((value < 0) || (value >= numskins)) tmpskin = "None"; else - tmpskin = skins[value].name; + tmpskin = skins[value]->name; strncpy(val, tmpskin, SKINNAMESIZE); } else diff --git a/src/console.c b/src/console.c index a4b9ac5e9..7d01abcee 100644 --- a/src/console.c +++ b/src/console.c @@ -217,13 +217,16 @@ static char *bindtable[NUMINPUTS]; static void CONS_Bind_f(void) { size_t na; + char *newcmd; + //size_t newlen = 0; + unsigned int i; INT32 key; na = COM_Argc(); - if (na != 2 && na != 3) + if (na < 2) { - CONS_Printf(M_GetText("bind []: create shortcut keys to command(s)\n")); + CONS_Printf(M_GetText("bind [] [] [...]: create shortcut keys to command(s)\n")); CONS_Printf("\x82%s", M_GetText("Bind table :\n")); na = 0; for (key = 0; key < NUMINPUTS; key++) @@ -247,8 +250,36 @@ static void CONS_Bind_f(void) Z_Free(bindtable[key]); bindtable[key] = NULL; - if (na == 3) - bindtable[key] = Z_StrDup(COM_Argv(2)); + if (na < 3) + return; + + for (i = 2; i < na; ++i) + { + const char *arg = COM_Argv(i); + + // on the second iteration, and after + if (i > 2) + { + size_t newlen = strlen(bindtable[key]) + strlen(arg) + 1; // new length, allow space for ' ' and '\0' + size_t curpos = newcmd - bindtable[key]; // offset from newcmd to original pointer + + newcmd = bindtable[key] = Z_Realloc(bindtable[key], newlen, PU_STATIC, NULL); + newcmd += curpos; // reapply offset + + newcmd[0] = ' '; // replace previous '\0' w/ ' ' + ++newcmd; // make sure later strcpy doesnt overwrite ' ' + } + // first iteration + else + // allocate space for argument and a ' ' or '\0' + newcmd = bindtable[key] = Z_Calloc(strlen(arg) + 1, PU_STATIC, NULL); + + // the copy + strcpy(newcmd, arg); + + // move window past copied argument for next iteration + newcmd += strlen(arg); + } } //====================================================================== @@ -934,7 +965,7 @@ boolean CON_Responder(event_t *ev) return false; // let go keyup events, don't eat them - if (ev->type != ev_keydown && ev->type != ev_console) + if (ev->type != ev_keydown && ev->type != ev_text && ev->type != ev_console) { if (ev->key == gamecontrol[GC_CONSOLE][0] || ev->key == gamecontrol[GC_CONSOLE][1]) consdown = false; @@ -949,7 +980,7 @@ boolean CON_Responder(event_t *ev) if (modeattacking || metalrecording || marathonmode) return false; - if (key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) + if ((key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) && !shiftdown) { if (consdown) // ignore repeat return true; @@ -961,7 +992,7 @@ boolean CON_Responder(event_t *ev) // check other keys only if console prompt is active if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!! { - if (! menuactive && bindtable[key]) + if (ev->type == ev_keydown && !menuactive && bindtable[key]) { COM_BufAddText(bindtable[key]); COM_BufAddText("\n"); @@ -978,6 +1009,13 @@ boolean CON_Responder(event_t *ev) } } + if (ev->type == ev_text) + { + if (!consoletoggle) + CON_InputAddChar(key); + return true; + } + // Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas if (key == KEY_LSHIFT || key == KEY_RSHIFT || key == KEY_LCTRL || key == KEY_RCTRL @@ -1292,21 +1330,12 @@ boolean CON_Responder(event_t *ev) else if (key == KEY_KPADSLASH) key = '/'; - if (key >= 'a' && key <= 'z') - { - if (capslock ^ shiftdown) - key = shiftxform[key]; - } - else if (shiftdown) - key = shiftxform[key]; - // enter a char into the command prompt if (key < 32 || key > 127) return true; if (input_sel != input_cur) CON_InputDelSelection(); - CON_InputAddChar(key); return true; } @@ -1844,9 +1873,9 @@ void CON_Drawer(void) if (con_curlines > 0) CON_DrawConsole(); - else if (gamestate == GS_LEVEL - || gamestate == GS_INTERMISSION || gamestate == GS_ENDING || gamestate == GS_CUTSCENE - || gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS) + else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CREDITS + || gamestate == GS_EVALUATION || gamestate == GS_ENDING || gamestate == GS_CUTSCENE + || gamestate == GS_WAITINGPLAYERS || cv_debug) CON_DrawHudlines(); Unlock_state(); diff --git a/src/d_event.h b/src/d_event.h index 5aa435060..7743d8609 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -22,6 +22,7 @@ typedef enum { ev_keydown, ev_keyup, + ev_text, ev_console, ev_mouse, ev_joystick, diff --git a/src/d_main.c b/src/d_main.c index fef1cfeb6..bf8fa6d87 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -192,19 +192,19 @@ void D_ProcessEvents(void) ev = &events[eventtail]; // Set mouse buttons early in case event is eaten later - if (ev->type == ev_keydown || ev->type == ev_keyup) + if (ev->type == ev_keydown || ev->type == ev_keyup || ev->type == ev_text) { // Mouse buttons if ((UINT32)(ev->key - KEY_MOUSE1) < MOUSEBUTTONS) { - if (ev->type == ev_keydown) + if (ev->type == ev_keydown || ev->type == ev_text) mouse.buttons |= 1 << (ev->key - KEY_MOUSE1); else mouse.buttons &= ~(1 << (ev->key - KEY_MOUSE1)); } else if ((UINT32)(ev->key - KEY_2MOUSE1) < MOUSEBUTTONS) { - if (ev->type == ev_keydown) + if (ev->type == ev_keydown || ev->type == ev_text) mouse2.buttons |= 1 << (ev->key - KEY_2MOUSE1); else mouse2.buttons &= ~(1 << (ev->key - KEY_2MOUSE1)); @@ -1420,10 +1420,6 @@ void D_SRB2Main(void) // Make backups of some SOCcable tables. P_BackupTables(); - // Setup character tables - // Have to be done here before files are loaded - M_InitCharacterTables(); - mainwads = 3; // doesn't include music.dta #ifdef USE_PATCH_DTA mainwads++; diff --git a/src/d_player.h b/src/d_player.h index 1409e28b3..62383f53a 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -451,10 +451,10 @@ typedef struct player_s UINT16 flashcount; UINT16 flashpal; - // Player skin colorshift, 0-15 for which color to draw player. + // Player skin colorshift, which color to draw player. UINT16 skincolor; - INT32 skin; + UINT8 skin; UINT32 availabilities; UINT32 score; // player score (total) diff --git a/src/d_think.h b/src/d_think.h index efc1589bf..589124587 100644 --- a/src/d_think.h +++ b/src/d_think.h @@ -51,6 +51,7 @@ typedef struct thinker_s // killough 11/98: count of how many other objects reference // this one using pointers. Used for garbage collection. INT32 references; + boolean cachable; #ifdef PARANOIA INT32 debug_mobjtype; diff --git a/src/deh_lua.c b/src/deh_lua.c index b8daa0430..8552360e0 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -600,14 +600,9 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word) return 0; } -static inline int lib_getenum(lua_State *L) +FUNCINLINE static ATTRINLINE int getEnum(lua_State *L, boolean mathlib, const char *word) { - const char *word; fixed_t i; - boolean mathlib = lua_toboolean(L, lua_upvalueindex(1)); - if (lua_type(L,2) != LUA_TSTRING) - return 0; - word = lua_tostring(L,2); // check actions, super and globals first, as they don't have _G caching implemented // so they benefit from being checked first @@ -674,6 +669,46 @@ static inline int lib_getenum(lua_State *L) else if ((!mathlib && LUA_PushGlobals(L, word)) || ScanConstants(L, mathlib, word)) return 1; + return -1; +} + +static int constants_get(lua_State *L) +{ + const char *key; + int ret; + + if (!lua_isstring(L, 2)) + return 0; + + key = luaL_checkstring(L, 2); + + // In Lua, mathlib is never there + ret = getEnum(L, false, key); + + if (ret != -1) + // Don't allow A_* or super. + // All userdata is meant to be considered global variables, + // so no need to get more specific than "is it userdata?" + if (!lua_isuserdata(L, -1) && !lua_isfunction(L, -1)) + return ret; + + return 0; +} + +static inline int lib_getenum(lua_State *L) +{ + const char *word; + int ret; + boolean mathlib = lua_toboolean(L, lua_upvalueindex(1)); + if (lua_type(L,2) != LUA_TSTRING) + return 0; + word = lua_tostring(L,2); + + ret = getEnum(L, mathlib, word); + + if (ret != -1) + return ret; + if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word); return 0; @@ -776,6 +811,15 @@ int LUA_SOCLib(lua_State *L) LUA_SetCFunctionField(L, "__call", action_call); lua_pop(L, 1); + // Allow access to constants without forcing the use of name comparison checks Lua-side + // This table will not access global variables, only constants + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, constants_get); + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); + lua_setglobal(L, "constants"); + return 0; } diff --git a/src/deh_soc.c b/src/deh_soc.c index 6162034de..41eb28a90 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -188,25 +188,22 @@ void clear_levels(void) P_AllocMapHeader(gamemap-1); } -static boolean findFreeSlot(INT32 *num) +static boolean findCharacterSlot(INT32 *num) { - // Send the character select entry to a free slot. - while (*num < MAXSKINS && (description[*num].used)) - *num = *num+1; + if (description) + { + // Send the character select entry to a free slot. + while (*num < numdescriptions && (description[*num].used)) + (*num)++; + } - // No more free slots. :( - if (*num >= MAXSKINS) + // No more free slots. + if (*num >= MAXCHARACTERSLOTS) return false; + else if (*num >= numdescriptions) + M_InitCharacterTables((*num) + 1); - // Redesign your logo. (See M_DrawSetupChoosePlayerMenu in m_menu.c...) - description[*num].picname[0] = '\0'; - description[*num].nametag[0] = '\0'; - description[*num].displayname[0] = '\0'; - description[*num].oppositecolor = SKINCOLOR_NONE; - description[*num].tagtextcolor = SKINCOLOR_NONE; - description[*num].tagoutlinecolor = SKINCOLOR_NONE; - - // Found one! ^_^ + // Found one! return (description[*num].used = true); } @@ -217,30 +214,43 @@ void readPlayer(MYFILE *f, INT32 num) char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *word; char *word2; - char *displayname = ZZ_Alloc(MAXLINELEN+1); - INT32 i; boolean slotfound = false; + boolean failure = false; + INT32 i; + + if (num < 0 || num >= MAXCHARACTERSLOTS) + { + deh_warning("Character %d out of range (0 - %d)", num, MAXCHARACTERSLOTS-1); + failure = true; + } + + #define FINDSLOT \ + if (!failure && !slotfound && (slotfound = findCharacterSlot(&num)) == false) { \ + failure = true; \ + deh_warning("Too many characters, ignoring"); \ + } #define SLOTFOUND \ - if (!slotfound && (slotfound = findFreeSlot(&num)) == false) \ - goto done; - - displayname[MAXLINELEN] = '\0'; + FINDSLOT \ + if (failure) \ + continue; do { if (myfgets(s, MAXLINELEN, f)) { + char stringvalue[MAXLINELEN]; + if (s[0] == '\n') break; - for (i = 0; i < MAXLINELEN-3; i++) + stringvalue[0] = '\0'; + + for (i = 0; i < MAXLINELEN-3 && !failure; i++) { - char *tmp; if (s[i] == '=') { - tmp = &s[i+2]; - strncpy(displayname, tmp, SKINNAMESIZE); + strlcpy(stringvalue, &s[i+2], sizeof stringvalue); break; } } @@ -255,7 +265,13 @@ void readPlayer(MYFILE *f, INT32 num) { char *playertext = NULL; - SLOTFOUND + FINDSLOT + + if (failure) + { + ignorelinesuntilhash(f); + continue; + } // A friendly neighborhood alias for brevity's sake #define NOTE_SIZE sizeof(description[num].notes) @@ -275,7 +291,7 @@ void readPlayer(MYFILE *f, INT32 num) myhashfgets(playertext, NOTE_SIZE, f), NOTE_SIZE); } else - strcpy(description[num].notes, ""); + description[num].notes[0] = '\0'; // For some reason, cutting the string did not work above. Most likely due to strcpy or strcat... // It works down here, though. @@ -304,37 +320,32 @@ void readPlayer(MYFILE *f, INT32 num) if (word2[strlen(word2)-1] == '\n') word2[strlen(word2)-1] = '\0'; - i = atoi(word2); if (fastcmp(word, "PICNAME")) { SLOTFOUND strncpy(description[num].picname, word2, 8); } - // new character select else if (fastcmp(word, "DISPLAYNAME")) { + char *cur = NULL; + SLOTFOUND - // replace '#' with line breaks - // (also remove any '\n') + + // Remove any line breaks + cur = strchr(stringvalue, '\n'); + if (cur) + *cur = '\0'; + + // Turn '#' into line breaks + cur = strchr(stringvalue, '#'); + while (cur) { - char *cur = NULL; - - // remove '\n' - cur = strchr(displayname, '\n'); - if (cur) - *cur = '\0'; - - // turn '#' into '\n' - cur = strchr(displayname, '#'); - while (cur) - { - *cur = '\n'; - cur = strchr(cur, '#'); - } + *cur = '\n'; + cur = strchr(cur, '#'); } - // copy final string - strncpy(description[num].displayname, displayname, SKINNAMESIZE); + + strlcpy(description[num].displayname, stringvalue, sizeof description[num].displayname); } else if (fastcmp(word, "OPPOSITECOLOR") || fastcmp(word, "OPPOSITECOLOUR")) { @@ -365,10 +376,12 @@ void readPlayer(MYFILE *f, INT32 num) Because of this, you are allowed to edit any previous entries you like, but only if you signal that you are purposely doing so by disabling and then reenabling the slot. */ - if (i && !slotfound && (slotfound = findFreeSlot(&num)) == false) - goto done; + i = atoi(word2); + if (i && !slotfound && (slotfound = findCharacterSlot(&num)) == false) + failure = true; - description[num].used = (!!i); + if (!failure) + description[num].used = (!!i); } else if (fastcmp(word, "SKINNAME")) { @@ -377,13 +390,12 @@ void readPlayer(MYFILE *f, INT32 num) strlcpy(description[num].skinname, word2, sizeof description[num].skinname); strlwr(description[num].skinname); } - else + else if (!failure) deh_warning("readPlayer %d: unknown word '%s'", num, word); } } while (!myfeof(f)); // finish when the line is empty + #undef FINDSLOT #undef SLOTFOUND -done: - Z_Free(displayname); Z_Free(s); } @@ -933,7 +945,7 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) INT32 value; #endif char *lastline; - INT32 skinnumbers[MAXSKINS]; + UINT8 *skinnumbers = NULL; INT32 foundskins = 0; // allocate a spriteinfo @@ -1022,7 +1034,9 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) break; } - skinnumbers[foundskins] = skinnum; + if (skinnumbers == NULL) + skinnumbers = Z_Malloc(sizeof(UINT8) * numskins, PU_STATIC, NULL); + skinnumbers[foundskins] = (UINT8)skinnum; foundskins++; } else if (fastcmp(word, "DEFAULT")) @@ -1065,8 +1079,7 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) } for (i = 0; i < foundskins; i++) { - size_t skinnum = skinnumbers[i]; - skin_t *skin = &skins[skinnum]; + skin_t *skin = skins[skinnumbers[i]]; spriteinfo_t *sprinfo = skin->sprinfo; M_Memcpy(&sprinfo[num], info, sizeof(spriteinfo_t)); } @@ -1085,6 +1098,8 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) Z_Free(s); Z_Free(info); + if (skinnumbers) + Z_Free(skinnumbers); } void readsprite2(MYFILE *f, INT32 num) @@ -1130,7 +1145,6 @@ void readsprite2(MYFILE *f, INT32 num) Z_Free(s); } -// copypasted from readPlayer :] void readgametype(MYFILE *f, char *gtname) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); diff --git a/src/deh_tables.c b/src/deh_tables.c index b53cd00c8..6d29fa2c7 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4291,7 +4291,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_POLYANCHOR", "MT_POLYSPAWN", - // Skybox objects + // Portal objects "MT_SKYBOX", // Debris diff --git a/src/dehacked.c b/src/dehacked.c index fd2a70171..2050a117f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -169,6 +169,20 @@ static void ignorelines(MYFILE *f) Z_Free(s); } +void ignorelinesuntilhash(MYFILE *f) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '#') + break; + } + } while (!myfeof(f)); + Z_Free(s); +} + static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); @@ -226,13 +240,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) i = 0; if (fastcmp(word, "CHARACTER")) { - if (i >= 0 && i < 32) - readPlayer(f, i); - else - { - deh_warning("Character %d out of range (0 - 31)", i); - ignorelines(f); - } + readPlayer(f, i); continue; } else if (fastcmp(word, "EMBLEM")) diff --git a/src/dehacked.h b/src/dehacked.h index b39f09035..d985b14b0 100644 --- a/src/dehacked.h +++ b/src/dehacked.h @@ -61,4 +61,5 @@ typedef struct #define myfeof(a) (a->data + a->size <= a->curpos) char *myfgets(char *buf, size_t bufsize, MYFILE *f); char *myhashfgets(char *buf, size_t bufsize, MYFILE *f); +void ignorelinesuntilhash(MYFILE *f); #endif diff --git a/src/doomdef.h b/src/doomdef.h index 9078c25a7..aa3cfe83b 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -109,11 +109,19 @@ FILE *fopenfile(const char*, const char*); // If you don't disable ALL debug first, you get ALL debug enabled #if !defined (NDEBUG) +#ifndef PACKETDROP #define PACKETDROP +#endif +#ifndef PARANOIA #define PARANOIA +#endif +#ifndef RANGECHECK #define RANGECHECK +#endif +#ifndef ZDEBUG #define ZDEBUG #endif +#endif // Uncheck this to compile debugging code //#define RANGECHECK @@ -233,9 +241,16 @@ extern char logfilename[1024]; // NOTE: it needs more than this to increase the number of players... #define MAXPLAYERS 32 -#define MAXSKINS 32 -#define PLAYERSMASK (MAXPLAYERS-1) #define MAXPLAYERNAME 21 +#define PLAYERSMASK (MAXPLAYERS-1) + +// Don't make MAXSKINS higher than 256, since skin numbers are used with an +// UINT8 in various parts of the codebase. If you do anyway, the data type +// of those variables will have to be changed into at least an UINT16. +// This change must affect code such as demo recording and playback, +// and the structure of some networking packets and commands. +#define MAXSKINS 256 +#define MAXCHARACTERSLOTS (MAXSKINS * 3) // Should be higher than MAXSKINS. #define COLORRAMPSIZE 16 #define MAXCOLORNAME 32 @@ -533,7 +548,7 @@ extern char liveeventbackup[256]; #define M_GetText(x) (x) #endif void M_StartupLocale(void); -extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL; +void *M_Memcpy(void* dest, const void* src, size_t n); char *va(const char *format, ...) FUNCPRINTF; char *M_GetToken(const char *inputString); void M_UnGetToken(void); diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c index 70e1ef4ec..fe33cfe3e 100644 --- a/src/dummy/i_system.c +++ b/src/dummy/i_system.c @@ -1,6 +1,7 @@ #include "../doomdef.h" #include "../doomtype.h" #include "../i_system.h" +#include "../i_time.h" FILE *logstream = NULL; @@ -19,6 +20,11 @@ void I_Sleep(UINT32 ms) (void)ms; } +void I_SleepDuration(precise_t duration) +{ + (void)duration; +} + precise_t I_GetPreciseTime(void) { return 0; diff --git a/src/f_finale.c b/src/f_finale.c index 68e9c3216..cb6461853 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1603,9 +1603,9 @@ void F_GameEvaluationDrawer(void) rtatext = (marathonmode & MA_INGAME) ? "In-game timer" : "RTA timer"; cuttext = (marathonmode & MA_NOCUTSCENES) ? "" : " w/ cutscenes"; if (botskin) - endingtext = va("%s & %s, %s%s", skins[players[consoleplayer].skin].realname, skins[botskin-1].realname, rtatext, cuttext); + endingtext = va("%s & %s, %s%s", skins[players[consoleplayer].skin]->realname, skins[botskin-1]->realname, rtatext, cuttext); else - endingtext = va("%s, %s%s", skins[players[consoleplayer].skin].realname, rtatext, cuttext); + endingtext = va("%s, %s%s", skins[players[consoleplayer].skin]->realname, rtatext, cuttext); V_DrawCenteredString(BASEVIDWIDTH/2, 182, V_SNAPTOBOTTOM|(ultimatemode ? V_REDMAP : V_YELLOWMAP), endingtext); } } @@ -1719,9 +1719,9 @@ static void F_CacheEnding(void) UINT8 skinnum = players[consoleplayer].skin; spritedef_t *sprdef; spriteframe_t *sprframe; - if (skins[skinnum].sprites[SPR2_XTRA].numframes > (XTRA_ENDING+2)) + if (skins[skinnum]->sprites[SPR2_XTRA].numframes > (XTRA_ENDING+2)) { - sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + sprdef = &skins[skinnum]->sprites[SPR2_XTRA]; // character head, skin specific sprframe = &sprdef->spriteframes[XTRA_ENDING]; endfwrk[0] = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH_LOWPRIORITY); @@ -2167,7 +2167,7 @@ void F_EndingDrawer(void) boolean donttouch = false; const char *str; if (goodending) - str = va("[S] %s: Engage.", skins[players[consoleplayer].skin].realname); + str = va("[S] %s: Engage.", skins[players[consoleplayer].skin]->realname); else str = "[S] Eggman: Abscond."; @@ -3554,7 +3554,7 @@ void F_StartContinue(void) S_ChangeMusicInternal("_conti", false); S_StopSounds(); - contskins[0] = &skins[players[consoleplayer].skin]; + contskins[0] = skins[players[consoleplayer].skin]; cont_spr2[0][0] = P_GetSkinSprite2(contskins[0], SPR2_CNT1, NULL); cont_spr2[0][2] = contskins[0]->contangle & 7; contcolormaps[0] = R_GetTranslationColormap(players[consoleplayer].skin, players[consoleplayer].skincolor, GTC_CACHE); @@ -3570,7 +3570,7 @@ void F_StartContinue(void) else // HACK secondplaya = 1; - contskins[1] = &skins[players[secondplaya].skin]; + contskins[1] = skins[players[secondplaya].skin]; cont_spr2[1][0] = P_GetSkinSprite2(contskins[1], SPR2_CNT4, NULL); cont_spr2[1][2] = (contskins[1]->contangle >> 3) & 7; contcolormaps[1] = R_GetTranslationColormap(players[secondplaya].skin, players[secondplaya].skincolor, GTC_CACHE); @@ -4524,7 +4524,7 @@ void F_TextPromptDrawer(void) if (players[j].mo->state == states+S_PLAY_STND && players[j].mo->tics != -1)\ players[j].mo->tics++;\ else if (players[j].mo->state == states+S_PLAY_WAIT)\ - P_SetPlayerMobjState(players[j].mo, S_PLAY_STND);\ + P_SetMobjState(players[j].mo, S_PLAY_STND);\ }\ } diff --git a/src/filesrch.c b/src/filesrch.c index 111dfd6e7..ad0cf02a1 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -23,6 +23,16 @@ #include #endif #include + +#ifndef S_ISLNK +#define IGNORE_SYMLINKS +#endif + +#ifndef IGNORE_SYMLINKS +#include +#include +#include +#endif #include #include "filesrch.h" @@ -461,6 +471,9 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want } else if (!strcasecmp(searchname, dent->d_name)) { +#ifndef IGNORE_SYMLINKS + struct stat statbuf; +#endif switch (checkfilemd5(searchpath, wantedmd5sum)) { case FS_FOUND: @@ -468,6 +481,19 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want strcpy(filename,searchpath); else strcpy(filename,dent->d_name); +#ifndef IGNORE_SYMLINKS + if (lstat(filename, &statbuf) != -1) + { + if (S_ISLNK(statbuf.st_mode)) + { + char *tempbuf = realpath(filename, NULL); + if (!tempbuf) + I_Error("Error parsing link %s: %s", filename, strerror(errno)); + strncpy(filename, tempbuf, MAX_WADPATH); + free(tempbuf); + } + } +#endif retval = FS_FOUND; found = 1; break; diff --git a/src/g_demo.c b/src/g_demo.c index 04a309ea2..9ce249fd9 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -492,7 +492,7 @@ void G_WriteGhostTic(mobj_t *ghost) if (ghost->player->followmobj->colorized) followtic |= FZT_COLORIZED; if (followtic & FZT_SKIN) - WRITEUINT8(demo_p,(UINT8)(((skin_t *)(ghost->player->followmobj->skin))-skins)); + WRITEUINT8(demo_p,(UINT8)(((skin_t *)ghost->player->followmobj->skin)->skinnum)); oldghost.flags2 |= MF2_AMBUSH; } @@ -761,7 +761,7 @@ void G_GhostTicker(void) g->mo->color = SKINCOLOR_WHITE; break; case GHC_NIGHTSSKIN: // not actually a colour - g->mo->skin = &skins[DEFAULTNIGHTSSKIN]; + g->mo->skin = skins[DEFAULTNIGHTSSKIN]; break; } } @@ -798,32 +798,40 @@ void G_GhostTicker(void) if (type == MT_GHOST) { mobj = P_SpawnGhostMobj(g->mo); // does a large portion of the work for us - mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<frame = (mobj->frame & ~FF_FRAMEMASK)|tr_trans60<mo, 0, 0, -FixedDiv(FixedMul(g->mo->info->height, g->mo->scale) - g->mo->height,3*FRACUNIT), MT_THOK); - mobj->sprite = states[mobjinfo[type].spawnstate].sprite; - mobj->frame = (states[mobjinfo[type].spawnstate].frame & FF_FRAMEMASK) | tr_trans60<color = g->mo->color; - mobj->skin = g->mo->skin; - P_SetScale(mobj, (mobj->destscale = g->mo->scale)); - - if (type == MT_THOK) // spintrail-specific modification for MT_THOK + if (!P_MobjWasRemoved(mobj)) { - mobj->frame = FF_TRANS80; - mobj->fuse = mobj->tics; + mobj->sprite = states[mobjinfo[type].spawnstate].sprite; + mobj->frame = (states[mobjinfo[type].spawnstate].frame & FF_FRAMEMASK) | tr_trans60<color = g->mo->color; + mobj->skin = g->mo->skin; + P_SetScale(mobj, (mobj->destscale = g->mo->scale)); + + if (type == MT_THOK) // spintrail-specific modification for MT_THOK + { + mobj->frame = FF_TRANS80; + mobj->fuse = mobj->tics; + } + mobj->tics = -1; // nope. } - mobj->tics = -1; // nope. } - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_UnsetThingPosition(mobj); - mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... - P_SetThingPosition(mobj); - if (!mobj->fuse) - mobj->fuse = 8; - P_SetTarget(&mobj->target, g->mo); + + if (!P_MobjWasRemoved(mobj)) + { + mobj->floorz = mobj->z; + mobj->ceilingz = mobj->z+mobj->height; + P_UnsetThingPosition(mobj); + mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... + P_SetThingPosition(mobj); + if (!mobj->fuse) + mobj->fuse = 8; + P_SetTarget(&mobj->target, g->mo); + } } } if (xziptic & EZT_HIT) @@ -847,6 +855,8 @@ void G_GhostTicker(void) || health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad. continue; poof = P_SpawnMobj(x, y, z, MT_GHOST); + if (P_MobjWasRemoved(poof)) + continue; poof->angle = angle; poof->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... poof->health = 0; @@ -892,19 +902,22 @@ void G_GhostTicker(void) if (follow) P_RemoveMobj(follow); P_SetTarget(&follow, P_SpawnMobjFromMobj(g->mo, 0, 0, 0, MT_GHOST)); - P_SetTarget(&follow->tracer, g->mo); - follow->tics = -1; - temp = READINT16(g->p)<height = FixedMul(follow->scale, temp); + if (!P_MobjWasRemoved(follow)) + { + P_SetTarget(&follow->tracer, g->mo); + follow->tics = -1; + temp = READINT16(g->p)<height = FixedMul(follow->scale, temp); - if (followtic & FZT_LINKDRAW) - follow->flags2 |= MF2_LINKDRAW; + if (followtic & FZT_LINKDRAW) + follow->flags2 |= MF2_LINKDRAW; - if (followtic & FZT_COLORIZED) - follow->colorized = true; + if (followtic & FZT_COLORIZED) + follow->colorized = true; - if (followtic & FZT_SKIN) - follow->skin = &skins[READUINT8(g->p)]; + if (followtic & FZT_SKIN) + follow->skin = skins[READUINT8(g->p)]; + } } if (follow) { @@ -1097,28 +1110,35 @@ void G_ReadMetalTic(mobj_t *metal) else { mobj = P_SpawnMobjFromMobj(metal, 0, 0, -FixedDiv(FixedMul(metal->info->height, metal->scale) - metal->height,3*FRACUNIT), MT_THOK); - mobj->sprite = states[mobjinfo[type].spawnstate].sprite; - mobj->frame = states[mobjinfo[type].spawnstate].frame; - mobj->angle = metal->angle; - mobj->color = metal->color; - mobj->skin = metal->skin; - P_SetScale(mobj, (mobj->destscale = metal->scale)); - - if (type == MT_THOK) // spintrail-specific modification for MT_THOK + if (!P_MobjWasRemoved(mobj)) { - mobj->frame = FF_TRANS70; - mobj->fuse = mobj->tics; + mobj->sprite = states[mobjinfo[type].spawnstate].sprite; + mobj->frame = states[mobjinfo[type].spawnstate].frame; + mobj->angle = metal->angle; + mobj->color = metal->color; + mobj->skin = metal->skin; + P_SetScale(mobj, (mobj->destscale = metal->scale)); + + if (type == MT_THOK) // spintrail-specific modification for MT_THOK + { + mobj->frame = FF_TRANS70; + mobj->fuse = mobj->tics; + } + mobj->tics = -1; // nope. } - mobj->tics = -1; // nope. } - mobj->floorz = mobj->z; - mobj->ceilingz = mobj->z+mobj->height; - P_UnsetThingPosition(mobj); - mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... - P_SetThingPosition(mobj); - if (!mobj->fuse) - mobj->fuse = 8; - P_SetTarget(&mobj->target, metal); + + if (!P_MobjWasRemoved(mobj)) + { + mobj->floorz = mobj->z; + mobj->ceilingz = mobj->z+mobj->height; + P_UnsetThingPosition(mobj); + mobj->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; // make an ATTEMPT to curb crazy SOCs fucking stuff up... + P_SetThingPosition(mobj); + if (!mobj->fuse) + mobj->fuse = 8; + P_SetTarget(&mobj->target, metal); + } } } if (xziptic & EZT_SPRITE) @@ -1140,19 +1160,22 @@ void G_ReadMetalTic(mobj_t *metal) if (follow) P_RemoveMobj(follow); P_SetTarget(&follow, P_SpawnMobjFromMobj(metal, 0, 0, 0, MT_GHOST)); - P_SetTarget(&follow->tracer, metal); - follow->tics = -1; - temp = READINT16(metal_p)<height = FixedMul(follow->scale, temp); + if (!P_MobjWasRemoved(follow)) + { + P_SetTarget(&follow->tracer, metal); + follow->tics = -1; + temp = READINT16(metal_p)<height = FixedMul(follow->scale, temp); - if (followtic & FZT_LINKDRAW) - follow->flags2 |= MF2_LINKDRAW; + if (followtic & FZT_LINKDRAW) + follow->flags2 |= MF2_LINKDRAW; - if (followtic & FZT_COLORIZED) - follow->colorized = true; + if (followtic & FZT_COLORIZED) + follow->colorized = true; - if (followtic & FZT_SKIN) - follow->skin = &skins[READUINT8(metal_p)]; + if (followtic & FZT_SKIN) + follow->skin = skins[READUINT8(metal_p)]; + } } if (follow) { @@ -1339,7 +1362,7 @@ void G_WriteMetalTic(mobj_t *metal) if (metal->player->followmobj->colorized) followtic |= FZT_COLORIZED; if (followtic & FZT_SKIN) - WRITEUINT8(demo_p,(UINT8)(((skin_t *)(metal->player->followmobj->skin))-skins)); + WRITEUINT8(demo_p,(UINT8)(((skin_t *)metal->player->followmobj->skin)->skinnum)); oldmetal.flags2 |= MF2_AMBUSH; } @@ -1492,7 +1515,7 @@ void G_BeginRecording(void) demo_p += 16; // Skin - const char *skinname = skins[players[0].skin].name; + const char *skinname = skins[players[0].skin]->name; for (i = 0; i < 16 && skinname[i]; i++) name[i] = skinname[i]; for (; i < 16; i++) @@ -2241,7 +2264,7 @@ void G_DoPlayDemo(char *defdemoname) G_InitNew(false, G_BuildMapName(gamemap), true, true, false); // Set color - players[0].skincolor = skins[players[0].skin].prefcolor; + players[0].skincolor = skins[players[0].skin]->prefcolor; for (i = 0; i < numskincolors; i++) if (!stricmp(skincolors[i].name,color)) { @@ -2535,7 +2558,9 @@ void G_AddGhost(char *defdemoname) { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. fixed_t z,f,c; fixed_t offset = mthing->z << FRACBITS; - gh->mo = P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST); + P_SetTarget(&gh->mo, P_SpawnMobj(mthing->x << FRACBITS, mthing->y << FRACBITS, 0, MT_GHOST)); + if (P_MobjWasRemoved(gh->mo)) + return; gh->mo->angle = FixedAngle(mthing->angle << FRACBITS); f = gh->mo->floorz; c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height; @@ -2559,11 +2584,11 @@ void G_AddGhost(char *defdemoname) gh->oldmo.z = gh->mo->z; // Set skin - gh->mo->skin = &skins[0]; + gh->mo->skin = skins[0]; for (i = 0; i < numskins; i++) - if (!stricmp(skins[i].name,skin)) + if (!stricmp(skins[i]->name,skin)) { - gh->mo->skin = &skins[i]; + gh->mo->skin = skins[i]; break; } gh->oldmo.skin = gh->mo->skin; diff --git a/src/g_game.c b/src/g_game.c index 1388148e7..90ccf29c1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -308,7 +308,9 @@ consvar_t cv_chatheight= CVAR_INIT ("chatheight", "8", CV_SAVE, chatheight_cons_ consvar_t cv_chatnotifications= CVAR_INIT ("chatnotifications", "On", CV_SAVE, CV_OnOff, NULL); // chat spam protection (why would you want to disable that???) -consvar_t cv_chatspamprotection= CVAR_INIT ("chatspamprotection", "On", CV_SAVE, CV_OnOff, NULL); +consvar_t cv_chatspamprotection= CVAR_INIT ("chatspamprotection", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_chatspamspeed= CVAR_INIT ("chatspamspeed", "35", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); +consvar_t cv_chatspamburst= CVAR_INIT ("chatspamburst", "3", CV_SAVE|CV_NETVAR, CV_Unsigned, NULL); // minichat text background consvar_t cv_chatbacktint = CVAR_INIT ("chatbacktint", "On", CV_SAVE, CV_OnOff, NULL); @@ -586,14 +588,14 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player) I_Error("Out of memory for replay filepath\n"); sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap)); - snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name); + snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1]->name); if (FIL_FileExists(lastdemo)) { UINT8 *buf; size_t len = FIL_ReadFile(lastdemo, &buf); - snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name); + snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1]->name); if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1) { // Better time, save this demo. if (FIL_FileExists(bestdemo)) @@ -602,7 +604,7 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player) CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo); } - snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name); + snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1]->name); if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1))) { // Better score, save this demo. if (FIL_FileExists(bestdemo)) @@ -611,7 +613,7 @@ static void G_SetMainRecords(gamedata_t *data, player_t *player) CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo); } - snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name); + snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1]->name); if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2))) { // Better rings, save this demo. if (FIL_FileExists(bestdemo)) @@ -724,14 +726,14 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player) I_Error("Out of memory for replay filepath\n"); sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap)); - snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name); + snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1]->name); if (FIL_FileExists(lastdemo)) { UINT8 *buf; size_t len = FIL_ReadFile(lastdemo, &buf); - snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);; + snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1]->name); if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1) { // Better time, save this demo. if (FIL_FileExists(bestdemo)) @@ -740,7 +742,7 @@ static void G_SetNightsRecords(gamedata_t *data, player_t *player) CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo); } - snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name); + snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1]->name); if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1))) { // Better score, save this demo. if (FIL_FileExists(bestdemo)) @@ -2149,7 +2151,7 @@ boolean G_Responder(event_t *ev) if (! netgame) F_StartGameEvaluation(); else if (server || IsPlayerAdmin(consoleplayer)) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); return true; } } @@ -2596,7 +2598,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT8 laps; UINT8 mare; UINT16 skincolor; - INT32 skin; + UINT8 skin; UINT32 availabilities; tic_t jointime; tic_t quittime; @@ -3998,14 +4000,137 @@ static void G_HandleSaveLevel(void) } } +// +// G_GetNextMap +// +INT16 G_GetNextMap(boolean ignoretokens, boolean silent) +{ + INT32 i; + INT16 newmapnum; + boolean spec = G_IsSpecialStage(gamemap); + + // go to next level + // newmapnum is 0-based, unlike gamemap + if (nextmapoverride != 0) + newmapnum = (INT16)(nextmapoverride-1); + else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext) + newmapnum = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1); + else + { + newmapnum = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); + if (marathonmode && newmapnum == spmarathon_start-1) + newmapnum = 1100-1; // No infinite loop for you + } + + INT16 gametype_to_use; + + if (nextgametype >= 0 && nextgametype < gametypecount) + gametype_to_use = nextgametype; + else + gametype_to_use = gametype; + + // If newmapnum is actually going to get used, make sure it points to + // a map of the proper gametype -- skip levels that don't support + // the current gametype. (Helps avoid playing boss levels in Race, + // for instance). + if (!spec || nextmapoverride) + { + if (newmapnum >= 0 && newmapnum < NUMMAPS) + { + INT16 cm = newmapnum; + UINT32 tolflag = G_TOLFlag(gametype_to_use); + UINT8 visitedmap[(NUMMAPS+7)/8]; + + memset(visitedmap, 0, sizeof (visitedmap)); + + while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag)) + { + visitedmap[cm/8] |= (1<<(cm&7)); + if (!mapheaderinfo[cm]) + cm = -1; // guarantee error execution + else if (marathonmode && mapheaderinfo[cm]->marathonnext) + cm = (INT16)(mapheaderinfo[cm]->marathonnext-1); + else + cm = (INT16)(mapheaderinfo[cm]->nextlevel-1); + + if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error) + { + cm = newmapnum; //Start the loop again so that the error checking below is executed. + + //Make sure the map actually exists before you try to go to it! + if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR)) + { + if (!silent) + CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1); + cm = 0; + break; + } + } + + if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar + { + // We got stuck in a loop, came back to the map we started on + // without finding one supporting the current gametype. + // Thus, print a warning, and just use this map anyways. + if (!silent) + CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1); + break; + } + } + newmapnum = cm; + } + + // wrap around in race + if (newmapnum >= 1100-1 && newmapnum <= 1102-1 && !(gametyperules & GTR_CAMPAIGN)) + newmapnum = (INT16)(spstage_start-1); + + if (newmapnum < 0 || (newmapnum >= NUMMAPS && newmapnum < 1100-1) || newmapnum > 1103-1) + I_Error("Followed map %d to invalid map %d\n", prevmap + 1, newmapnum + 1); + + if (!spec) + lastmap = newmapnum; // Remember last map for when you come out of the special stage. + } + + if (!ignoretokens && (gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token))) + { + token--; + +// if (!nextmapoverride) // Having a token should pull the player into the special stage before going to the overridden map (Issue #933) + for (i = 0; i < 7; i++) + if (!(emeralds & (1<marathonnext) - nextmap = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1); - else - { - nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); - if (marathonmode && nextmap == spmarathon_start-1) - nextmap = 1100-1; // No infinite loop for you - } - - INT16 gametype_to_use; - - if (nextgametype >= 0 && nextgametype < gametypecount) - gametype_to_use = nextgametype; - else - gametype_to_use = gametype; - - // If nextmap is actually going to get used, make sure it points to - // a map of the proper gametype -- skip levels that don't support - // the current gametype. (Helps avoid playing boss levels in Race, - // for instance). - if (!spec || nextmapoverride) - { - if (nextmap >= 0 && nextmap < NUMMAPS) - { - INT16 cm = nextmap; - UINT32 tolflag = G_TOLFlag(gametype_to_use); - UINT8 visitedmap[(NUMMAPS+7)/8]; - - memset(visitedmap, 0, sizeof (visitedmap)); - - while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag)) - { - visitedmap[cm/8] |= (1<<(cm&7)); - if (!mapheaderinfo[cm]) - cm = -1; // guarantee error execution - else if (marathonmode && mapheaderinfo[cm]->marathonnext) - cm = (INT16)(mapheaderinfo[cm]->marathonnext-1); - else - cm = (INT16)(mapheaderinfo[cm]->nextlevel-1); - - if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error) - { - cm = nextmap; //Start the loop again so that the error checking below is executed. - - //Make sure the map actually exists before you try to go to it! - if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR)) - { - CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1); - cm = 0; - break; - } - } - - if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar - { - // We got stuck in a loop, came back to the map we started on - // without finding one supporting the current gametype. - // Thus, print a warning, and just use this map anyways. - CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1); - break; - } - } - nextmap = cm; - } - - // wrap around in race - if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN)) - nextmap = (INT16)(spstage_start-1); - - if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1) - I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); - - if (!spec) - lastmap = nextmap; // Remember last map for when you come out of the special stage. - } - - if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token))) - { - token--; - -// if (!nextmapoverride) // Having a token should pull the player into the special stage before going to the overridden map (Issue #933) - for (i = 0; i < 7; i++) - if (!(emeralds & (1< 0) { if ((botingame = ((botskin = savedata.botskin) != 0))) - botcolor = skins[botskin-1].prefcolor; + botcolor = skins[botskin-1]->prefcolor; } else if (splitscreen != SSSG) { @@ -5136,7 +5153,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean players[consoleplayer].lives = savedata.lives; players[consoleplayer].score = savedata.score; if ((botingame = ((botskin = savedata.botskin) != 0))) - botcolor = skins[botskin-1].prefcolor; + botcolor = skins[botskin-1]->prefcolor; emeralds = savedata.emeralds; savedata.lives = 0; } @@ -5380,16 +5397,29 @@ void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc) INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) { boolean usemapcode = false; - INT32 newmapnum; - - size_t mapnamelen; - + size_t mapnamelen = strlen(mapname); char *p; - mapnamelen = strlen(mapname); - - if (mapnamelen == 2)/* maybe two digit code */ + if (mapnamelen == 1) + { + if (mapname[0] == '*') // current map + { + usemapcode = true; + newmapnum = gamemap; + } + else if (mapname[0] == '+' && mapheaderinfo[gamemap-1]) // next map + { + usemapcode = true; + newmapnum = mapheaderinfo[gamemap-1]->nextlevel; + if (newmapnum < 1 || newmapnum > NUMMAPS) + { + CONS_Alert(CONS_ERROR, M_GetText("NextLevel (%d) is not a valid map.\n"), newmapnum); + return 0; + } + } + } + else if (mapnamelen == 2)/* maybe two digit code */ { if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) )) usemapcode = true; diff --git a/src/g_game.h b/src/g_game.h index 4d86a0455..80a815f02 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -53,7 +53,7 @@ extern consvar_t cv_instantretry; // used in game menu extern consvar_t cv_tutorialprompt; -extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection, cv_compactscoreboard; +extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection, cv_chatspamspeed, cv_chatspamburst, cv_compactscoreboard; extern consvar_t cv_crosshair, cv_crosshair2; extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_chasefreelook, cv_mousemove; extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_chasefreelook2, cv_mousemove2; @@ -216,6 +216,7 @@ boolean G_CoopGametype(void); boolean G_TagGametype(void); boolean G_CompetitionGametype(void); boolean G_EnoughPlayersFinished(void); +INT16 G_GetNextMap(boolean ignoretokens, boolean silent); void G_ExitLevel(void); void G_NextLevel(void); void G_Continue(void); diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index b02f2824a..3b660cc70 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -283,6 +283,7 @@ enum hwdsetspecialstate HWD_SET_SHADERS, HWD_SET_TEXTUREFILTERMODE, HWD_SET_TEXTUREANISOTROPICMODE, + HWD_SET_WIREFRAME, HWD_NUMSTATE }; diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 92eb628bd..7610032a5 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -1132,6 +1132,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) FOutVector v[4]; FSurfaceInfo Surf; float fx, fy, fw, fh; + UINT8 alphalevel = ((color & V_ALPHAMASK) >> V_ALPHASHIFT); UINT8 perplayershuffle = 0; @@ -1296,8 +1297,16 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) Surf.PolyColor = V_GetColor(color); + if (alphalevel) + { + if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency]; // V_HUDTRANSHALF + else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency]; // V_HUDTRANS + else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency]; // V_HUDTRANSDOUBLE + else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel]; + } + HWD.pfnDrawPolygon(&Surf, v, 4, - PF_Modulated|PF_NoTexture|PF_NoDepthTest); + PF_Modulated|PF_NoTexture|PF_NoDepthTest|PF_Translucent); } #ifdef HAVE_PNG diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 9a058f289..32cc8b371 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -166,6 +166,11 @@ static boolean HWR_UseShader(void) return (cv_glshaders.value && gl_shadersavailable); } +static boolean HWR_IsWireframeMode(void) +{ + return (cv_glwireframe.value && cv_debug); +} + void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *colormap) { RGBA_t poly_color, tint_color, fade_color; @@ -358,11 +363,9 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool INT32 i; float height; // constant y for all points on the convex flat polygon - float flatxref, flatyref, anglef = 0.0f; + float anglef = 0.0f; float fflatwidth = 64.0f, fflatheight = 64.0f; - UINT16 flatflag = 63; - - boolean texflat = false; + float xscale = 1.0f, yscale = 1.0f; float tempxsow, tempytow; float scrollx = 0.0f, scrolly = 0.0f; @@ -397,11 +400,7 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool slope = gl_frontsector->c_slope; } - // Set fixedheight to the slope's height from our viewpoint, if we have a slope - if (slope) - fixedheight = P_GetSlopeZAt(slope, viewx, viewy); - - height = FIXED_TO_FLOAT(fixedheight); + height = FixedToFloat(fixedheight); // Allocate plane-vertex buffer if we need to if (!planeVerts || nrPlaneVerts > numAllocedPlaneVerts) @@ -417,8 +416,8 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool if (levelflat->type == LEVELFLAT_FLAT) { size_t len = W_LumpLength(levelflat->u.flat.lumpnum); - flatflag = R_GetFlatSize(len) - 1; - fflatwidth = fflatheight = (float)(flatflag + 1); + unsigned flatflag = R_GetFlatSize(len); + fflatwidth = fflatheight = (float)flatflag; } else { @@ -432,29 +431,28 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool fflatwidth = levelflat->width; fflatheight = levelflat->height; } - texflat = true; } } else // set no texture HWR_SetCurrentTexture(NULL); - // reference point for flat texture coord for each vertex around the polygon - flatxref = (float)(((fixed_t)pv->x & (~flatflag)) / fflatwidth); - flatyref = (float)(((fixed_t)pv->y & (~flatflag)) / fflatheight); - // transform if (FOFsector != NULL) { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floorxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->flooryoffset)/fflatheight; + xscale = FixedToFloat(FOFsector->floorxscale); + yscale = FixedToFloat(FOFsector->flooryscale); + scrollx = FixedToFloat(FOFsector->floorxoffset) / fflatwidth; + scrolly = FixedToFloat(FOFsector->flooryoffset) / fflatheight; angle = FOFsector->floorangle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceilingxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->ceilingyoffset)/fflatheight; + xscale = FixedToFloat(FOFsector->ceilingxscale); + yscale = FixedToFloat(FOFsector->ceilingyscale); + scrollx = FixedToFloat(FOFsector->ceilingxoffset) / fflatwidth; + scrolly = FixedToFloat(FOFsector->ceilingyoffset) / fflatheight; angle = FOFsector->ceilingangle; } } @@ -462,41 +460,28 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gl_frontsector->floorxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gl_frontsector->flooryoffset)/fflatheight; + xscale = FixedToFloat(gl_frontsector->floorxscale); + yscale = FixedToFloat(gl_frontsector->flooryscale); + scrollx = FixedToFloat(gl_frontsector->floorxoffset) / fflatwidth; + scrolly = FixedToFloat(gl_frontsector->flooryoffset) / fflatheight; angle = gl_frontsector->floorangle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gl_frontsector->ceilingxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gl_frontsector->ceilingyoffset)/fflatheight; + xscale = FixedToFloat(gl_frontsector->ceilingxscale); + yscale = FixedToFloat(gl_frontsector->ceilingyscale); + scrollx = FixedToFloat(gl_frontsector->ceilingxoffset) / fflatwidth; + scrolly = FixedToFloat(gl_frontsector->ceilingyoffset) / fflatheight; angle = gl_frontsector->ceilingangle; } } - if (angle) // Only needs to be done if there's an altered angle - { - tempxsow = flatxref; - tempytow = flatyref; - - anglef = ANG2RAD(InvAngle(angle)); - - flatxref = (tempxsow * cos(anglef)) - (tempytow * sin(anglef)); - flatyref = (tempxsow * sin(anglef)) + (tempytow * cos(anglef)); - } + anglef = ANG2RAD(InvAngle(angle)); #define SETUP3DVERT(vert, vx, vy) {\ /* Hurdler: add scrolling texture on floor/ceiling */\ - if (texflat)\ - {\ - vert->s = (float)((vx) / fflatwidth) + scrollx;\ - vert->t = -(float)((vy) / fflatheight) + scrolly;\ - }\ - else\ - {\ - vert->s = (float)(((vx) / fflatwidth) - flatxref + scrollx);\ - vert->t = (float)(flatyref - ((vy) / fflatheight) + scrolly);\ - }\ + vert->s = ((vx) / fflatwidth) + (scrollx / xscale);\ + vert->t = -((vy) / fflatheight) + (scrolly / yscale);\ \ /* Need to rotate before translate */\ if (angle) /* Only needs to be done if there's an altered angle */\ @@ -507,15 +492,18 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool vert->t = (tempxsow * sin(anglef)) + (tempytow * cos(anglef));\ }\ \ - vert->x = (vx);\ - vert->y = height;\ - vert->z = (vy);\ + vert->s *= xscale;\ + vert->t *= yscale;\ \ if (slope)\ {\ - fixedheight = P_GetSlopeZAt(slope, FLOAT_TO_FIXED((vx)), FLOAT_TO_FIXED((vy)));\ - vert->y = FIXED_TO_FLOAT(fixedheight);\ + fixedheight = P_GetSlopeZAt(slope, FloatToFixed((vx)), FloatToFixed((vy)));\ + height = FixedToFloat(fixedheight);\ }\ +\ + vert->x = (vx);\ + vert->y = height;\ + vert->z = (vy);\ } for (i = 0, v3d = planeVerts; i < (INT32)nrPlaneVerts; i++,v3d++,pv++) @@ -564,7 +552,7 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool for (i = 0; i < subsector->numlines; i++, line++) { - if (!line->glseg && line->linedef->special == HORIZONSPECIAL && R_PointOnSegSide(dup_viewx, dup_viewy, line) == 0) + if (!line->glseg && line->linedef->special == SPECIAL_HORIZON_LINE && R_PointOnSegSide(dup_viewx, dup_viewy, line) == 0) { P_ClosestPointOnLine(viewx, viewy, line->linedef, &v); dist = FIXED_TO_FLOAT(R_PointToDist(v.x, v.y)); @@ -1032,6 +1020,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom fixed_t h, l; // 3D sides and 2s middle textures fixed_t hS, lS; + float xscale, yscale; gl_sidedef = gl_curline->sidedef; gl_linedef = gl_curline->linedef; @@ -1101,9 +1090,6 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom INT32 gl_toptexture = 0, gl_bottomtexture = 0; fixed_t texturevpeg; - boolean bothceilingssky = false; // turned on if both back and front ceilings are sky - boolean bothfloorssky = false; // likewise, but for floors - SLOPEPARAMS(gl_backsector->c_slope, worldhigh, worldhighslope, gl_backsector->ceilingheight) SLOPEPARAMS(gl_backsector->f_slope, worldlow, worldlowslope, gl_backsector->floorheight) @@ -1130,47 +1116,53 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom // check TOP TEXTURE if ((worldhighslope < worldtopslope || worldhigh < worldtop) && gl_toptexture) { + grTex = HWR_GetTexture(gl_toptexture); + xscale = FixedToFloat(gl_sidedef->scalex_top); + yscale = FixedToFloat(gl_sidedef->scaley_top); + + fixed_t texheight = FixedDiv(textureheight[gl_toptexture], gl_sidedef->scaley_top); + // PEGGING if (gl_linedef->flags & ML_DONTPEGTOP) texturevpeg = 0; else if (gl_linedef->flags & ML_SKEWTD) - texturevpeg = worldhigh + textureheight[gl_toptexture] - worldtop; + texturevpeg = worldhigh + texheight - worldtop; else - texturevpeg = gl_backsector->ceilingheight + textureheight[gl_toptexture] - gl_frontsector->ceilingheight; + texturevpeg = gl_backsector->ceilingheight + texheight - gl_frontsector->ceilingheight; + + texturevpeg *= yscale; texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_top; // This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway - texturevpeg %= textureheight[gl_toptexture]; - - grTex = HWR_GetTexture(gl_toptexture); + texturevpeg %= texheight; wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; - wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_backsector->ceilingheight) * grTex->scaleY; - wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_top) * grTex->scaleX; - wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_top) * grTex->scaleX; + wallVerts[0].t = wallVerts[1].t = (texturevpeg + (gl_frontsector->ceilingheight - gl_backsector->ceilingheight) * yscale) * grTex->scaleY; + wallVerts[0].s = wallVerts[3].s = ((cliplow * xscale) + gl_sidedef->offsetx_top) * grTex->scaleX; + wallVerts[2].s = wallVerts[1].s = ((cliphigh * xscale) + gl_sidedef->offsetx_top) * grTex->scaleX; // Adjust t value for sloped walls if (!(gl_linedef->flags & ML_SKEWTD)) { // Unskewed - wallVerts[3].t -= (worldtop - gl_frontsector->ceilingheight) * grTex->scaleY; - wallVerts[2].t -= (worldtopslope - gl_frontsector->ceilingheight) * grTex->scaleY; - wallVerts[0].t -= (worldhigh - gl_backsector->ceilingheight) * grTex->scaleY; - wallVerts[1].t -= (worldhighslope - gl_backsector->ceilingheight) * grTex->scaleY; + wallVerts[3].t -= (worldtop - gl_frontsector->ceilingheight) * yscale * grTex->scaleY; + wallVerts[2].t -= (worldtopslope - gl_frontsector->ceilingheight) * yscale * grTex->scaleY; + wallVerts[0].t -= (worldhigh - gl_backsector->ceilingheight) * yscale * grTex->scaleY; + wallVerts[1].t -= (worldhighslope - gl_backsector->ceilingheight) * yscale * grTex->scaleY; } else if (gl_linedef->flags & ML_DONTPEGTOP) { // Skewed by top - wallVerts[0].t = (texturevpeg + worldtop - worldhigh) * grTex->scaleY; - wallVerts[1].t = (texturevpeg + worldtopslope - worldhighslope) * grTex->scaleY; + wallVerts[0].t = (texturevpeg + (worldtop - worldhigh) * yscale) * grTex->scaleY; + wallVerts[1].t = (texturevpeg + (worldtopslope - worldhighslope) * yscale) * grTex->scaleY; } else { // Skewed by bottom - wallVerts[0].t = wallVerts[1].t = (texturevpeg + worldtop - worldhigh) * grTex->scaleY; - wallVerts[3].t = wallVerts[0].t - (worldtop - worldhigh) * grTex->scaleY; - wallVerts[2].t = wallVerts[1].t - (worldtopslope - worldhighslope) * grTex->scaleY; + wallVerts[0].t = wallVerts[1].t = (texturevpeg + (worldtop - worldhigh) * yscale) * grTex->scaleY; + wallVerts[3].t = wallVerts[0].t - (worldtop - worldhigh) * yscale * grTex->scaleY; + wallVerts[2].t = wallVerts[1].t - (worldtopslope - worldhighslope) * yscale * grTex->scaleY; } // set top/bottom coords @@ -1190,6 +1182,10 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom // check BOTTOM TEXTURE if ((worldlowslope > worldbottomslope || worldlow > worldbottom) && gl_bottomtexture) { + grTex = HWR_GetTexture(gl_bottomtexture); + xscale = FixedToFloat(gl_sidedef->scalex_bottom); + yscale = FixedToFloat(gl_sidedef->scaley_bottom); + // PEGGING if (!(gl_linedef->flags & ML_DONTPEGBOTTOM)) texturevpeg = 0; @@ -1198,38 +1194,38 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom else texturevpeg = gl_frontsector->floorheight - gl_backsector->floorheight; - texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_bot; + texturevpeg *= yscale; + + texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_bottom; // This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway - texturevpeg %= textureheight[gl_bottomtexture]; - - grTex = HWR_GetTexture(gl_bottomtexture); + texturevpeg %= FixedDiv(textureheight[gl_bottomtexture], gl_sidedef->scaley_bottom); wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; - wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_backsector->floorheight - gl_frontsector->floorheight) * grTex->scaleY; - wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_bot) * grTex->scaleX; - wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_bot) * grTex->scaleX; + wallVerts[0].t = wallVerts[1].t = (texturevpeg + (gl_backsector->floorheight - gl_frontsector->floorheight) * yscale) * grTex->scaleY; + wallVerts[0].s = wallVerts[3].s = ((cliplow * xscale) + gl_sidedef->offsetx_bottom) * grTex->scaleX; + wallVerts[2].s = wallVerts[1].s = ((cliphigh * xscale) + gl_sidedef->offsetx_bottom) * grTex->scaleX; // Adjust t value for sloped walls if (!(gl_linedef->flags & ML_SKEWTD)) { // Unskewed - wallVerts[0].t -= (worldbottom - gl_frontsector->floorheight) * grTex->scaleY; - wallVerts[1].t -= (worldbottomslope - gl_frontsector->floorheight) * grTex->scaleY; - wallVerts[3].t -= (worldlow - gl_backsector->floorheight) * grTex->scaleY; - wallVerts[2].t -= (worldlowslope - gl_backsector->floorheight) * grTex->scaleY; + wallVerts[0].t -= (worldbottom - gl_frontsector->floorheight) * yscale * grTex->scaleY; + wallVerts[1].t -= (worldbottomslope - gl_frontsector->floorheight) * yscale * grTex->scaleY; + wallVerts[3].t -= (worldlow - gl_backsector->floorheight) * yscale * grTex->scaleY; + wallVerts[2].t -= (worldlowslope - gl_backsector->floorheight) * yscale * grTex->scaleY; } else if (gl_linedef->flags & ML_DONTPEGBOTTOM) { // Skewed by bottom - wallVerts[0].t = wallVerts[1].t = (texturevpeg + worldlow - worldbottom) * grTex->scaleY; - wallVerts[2].t = wallVerts[1].t - (worldlowslope - worldbottomslope) * grTex->scaleY; + wallVerts[0].t = wallVerts[1].t = (texturevpeg + (worldlow - worldbottom) * yscale) * grTex->scaleY; + wallVerts[2].t = wallVerts[1].t - (worldlowslope - worldbottomslope) * yscale * grTex->scaleY; } else { // Skewed by top - wallVerts[0].t = (texturevpeg + worldlow - worldbottom) * grTex->scaleY; - wallVerts[1].t = (texturevpeg + worldlowslope - worldbottomslope) * grTex->scaleY; + wallVerts[0].t = (texturevpeg + (worldlow - worldbottom) * yscale) * grTex->scaleY; + wallVerts[1].t = (texturevpeg + (worldlowslope - worldbottomslope) * yscale) * grTex->scaleY; } // set top/bottom coords @@ -1250,6 +1246,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom if (gl_midtexture && HWR_BlendMidtextureSurface(&Surf)) { sector_t *front, *back; + fixed_t texheight = FixedDiv(textureheight[gl_midtexture], gl_sidedef->scaley_mid); INT32 repeats; if (gl_linedef->frontsector->heightsec != -1) @@ -1278,13 +1275,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom else low = back->floorheight; - repeats = (high - low) / textureheight[gl_midtexture]; - if ((high - low) % textureheight[gl_midtexture]) + repeats = (high - low) / texheight; + if ((high - low) % texheight) repeats++; // tile an extra time to fill the gap -- Monster Iestyn } else repeats = 1; + grTex = HWR_GetTexture(gl_midtexture); + xscale = FixedToFloat(gl_sidedef->scalex_mid); + yscale = FixedToFloat(gl_sidedef->scaley_mid); + // SoM: a little note: popentop and popenbottom // record the limits the texture can be displayed in. // polytop and polybottom, are the ideal (i.e. unclipped) @@ -1302,7 +1303,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom popenbottom = popenbottomslope = back->floorheight; } else - { + { popentop = min(worldtop, worldhigh); popenbottom = max(worldbottom, worldlow); popentopslope = min(worldtopslope, worldhighslope); @@ -1310,7 +1311,9 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom } // Find the wall's coordinates - fixed_t midtexheight = textureheight[gl_midtexture] * repeats; + fixed_t midtexheight = texheight * repeats; + + fixed_t rowoffset = FixedDiv(gl_sidedef->rowoffset + gl_sidedef->offsety_mid, gl_sidedef->scaley_mid); // Texture is not skewed if (gl_linedef->flags & ML_NOSKEW) @@ -1318,13 +1321,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom // Peg it to the floor if (gl_linedef->flags & ML_MIDPEG) { - polybottom = max(front->floorheight, back->floorheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polybottom = max(front->floorheight, back->floorheight) + rowoffset; polytop = polybottom + midtexheight; } // Peg it to the ceiling else { - polytop = min(front->ceilingheight, back->ceilingheight) + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polytop = min(front->ceilingheight, back->ceilingheight) + rowoffset; polybottom = polytop - midtexheight; } @@ -1335,17 +1338,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom // Skew the texture, but peg it to the floor else if (gl_linedef->flags & ML_MIDPEG) { - polybottom = popenbottom + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polybottom = popenbottom + rowoffset; polytop = polybottom + midtexheight; - polybottomslope = popenbottomslope + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + polybottomslope = popenbottomslope + rowoffset; polytopslope = polybottomslope + midtexheight; } // Skew it according to the ceiling's slope else { - polytop = popentop + gl_sidedef->rowoffset; + polytop = popentop + rowoffset; polybottom = polytop - midtexheight; - polytopslope = popentopslope + gl_sidedef->rowoffset; + polytopslope = popentopslope + rowoffset; polybottomslope = polytopslope - midtexheight; } @@ -1387,17 +1390,15 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom texturevpegslope = polytopslope - hS; } - grTex = HWR_GetTexture(gl_midtexture); - // Left side - wallVerts[3].t = texturevpeg * grTex->scaleY; - wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY; - wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX; + wallVerts[3].t = texturevpeg * yscale * grTex->scaleY; + wallVerts[0].t = (h - l + texturevpeg) * yscale * grTex->scaleY; + wallVerts[0].s = wallVerts[3].s = ((cliplow * xscale) + gl_sidedef->offsetx_mid) * grTex->scaleX; // Right side - wallVerts[2].t = texturevpegslope * grTex->scaleY; - wallVerts[1].t = (hS - lS + texturevpegslope) * grTex->scaleY; - wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX; + wallVerts[2].t = texturevpegslope * yscale * grTex->scaleY; + wallVerts[1].t = (hS - lS + texturevpegslope) * yscale * grTex->scaleY; + wallVerts[2].s = wallVerts[1].s = ((cliphigh * xscale) + gl_sidedef->offsetx_mid) * grTex->scaleX; // set top/bottom coords // Take the texture peg into account, rather than changing the offsets past @@ -1453,38 +1454,42 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom else { // Single sided line... Deal only with the middletexture (if one exists) - if (gl_midtexture && gl_linedef->special != HORIZONSPECIAL) // (Ignore horizon line for OGL) + if (gl_midtexture && gl_linedef->special != SPECIAL_HORIZON_LINE) // (Ignore horizon line for OGL) { + grTex = HWR_GetTexture(gl_midtexture); + xscale = FixedToFloat(gl_sidedef->scalex_mid); + yscale = FixedToFloat(gl_sidedef->scaley_mid); + fixed_t texturevpeg; // PEGGING if ((gl_linedef->flags & (ML_DONTPEGBOTTOM|ML_NOSKEW)) == (ML_DONTPEGBOTTOM|ML_NOSKEW)) - texturevpeg = gl_frontsector->floorheight + textureheight[gl_sidedef->midtexture] - gl_frontsector->ceilingheight + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + texturevpeg = (gl_frontsector->floorheight + textureheight[gl_sidedef->midtexture] - gl_frontsector->ceilingheight) * yscale; else if (gl_linedef->flags & ML_DONTPEGBOTTOM) - texturevpeg = worldbottom + textureheight[gl_sidedef->midtexture] - worldtop + gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + texturevpeg = (worldbottom + textureheight[gl_sidedef->midtexture] - worldtop) * yscale; else // top of texture at top - texturevpeg = gl_sidedef->rowoffset + gl_sidedef->offsety_mid; + texturevpeg = 0; - grTex = HWR_GetTexture(gl_midtexture); + texturevpeg += gl_sidedef->rowoffset + gl_sidedef->offsety_mid; wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; wallVerts[0].t = wallVerts[1].t = (texturevpeg + gl_frontsector->ceilingheight - gl_frontsector->floorheight) * grTex->scaleY; - wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX; - wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX; + wallVerts[0].s = wallVerts[3].s = ((cliplow * xscale) + gl_sidedef->offsetx_mid) * grTex->scaleX; + wallVerts[2].s = wallVerts[1].s = ((cliphigh * xscale) + gl_sidedef->offsetx_mid) * grTex->scaleX; // Texture correction for slopes if (gl_linedef->flags & ML_NOSKEW) { - wallVerts[3].t += (gl_frontsector->ceilingheight - worldtop) * grTex->scaleY; - wallVerts[2].t += (gl_frontsector->ceilingheight - worldtopslope) * grTex->scaleY; - wallVerts[0].t += (gl_frontsector->floorheight - worldbottom) * grTex->scaleY; - wallVerts[1].t += (gl_frontsector->floorheight - worldbottomslope) * grTex->scaleY; + wallVerts[3].t += (gl_frontsector->ceilingheight - worldtop) * yscale * grTex->scaleY; + wallVerts[2].t += (gl_frontsector->ceilingheight - worldtopslope) * yscale * grTex->scaleY; + wallVerts[0].t += (gl_frontsector->floorheight - worldbottom) * yscale * grTex->scaleY; + wallVerts[1].t += (gl_frontsector->floorheight - worldbottomslope) * yscale * grTex->scaleY; } else if (gl_linedef->flags & ML_DONTPEGBOTTOM) { - wallVerts[3].t = wallVerts[0].t + (worldbottom-worldtop) * grTex->scaleY; - wallVerts[2].t = wallVerts[1].t + (worldbottomslope-worldtopslope) * grTex->scaleY; + wallVerts[3].t = wallVerts[0].t + ((worldbottom - worldtop) * yscale) * grTex->scaleY; + wallVerts[2].t = wallVerts[1].t + ((worldbottomslope - worldtopslope) * yscale) * grTex->scaleY; } else { - wallVerts[0].t = wallVerts[3].t - (worldbottom-worldtop) * grTex->scaleY; - wallVerts[1].t = wallVerts[2].t - (worldbottomslope-worldtopslope) * grTex->scaleY; + wallVerts[0].t = wallVerts[3].t - ((worldbottom - worldtop) * yscale) * grTex->scaleY; + wallVerts[1].t = wallVerts[2].t - ((worldbottomslope - worldtopslope) * yscale) * grTex->scaleY; } //Set textures properly on single sided walls that are sloped @@ -1569,14 +1574,21 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom if ((high1 < lowcut && highslope1 < lowcutslope) || (low1 > highcut && lowslope1 > highcutslope)) continue; - texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture); + side_t *side = &sides[rover->master->sidenum[0]]; + + INT16 lineflags; if (rover->master->flags & ML_TFERLINE) { size_t linenum = gl_curline->linedef-gl_backsector->lines[0]; newline = rover->master->frontsector->lines[0] + linenum; - texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture); + side = &sides[newline->sidenum[0]]; + lineflags = newline->flags; } + else + lineflags = gl_curline->linedef->flags; + + texnum = R_GetTextureNum(side->midtexture); h = P_GetFFloorTopZAt (rover, v1x, v1y); hS = P_GetFFloorTopZAt (rover, v2x, v2y); @@ -1593,14 +1605,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom l = lowcut; lS = lowcutslope; } - //Hurdler: HW code starts here - //FIXME: check if peging is correct - // set top/bottom coords + // set top/bottom coords wallVerts[3].y = FIXED_TO_FLOAT(h); wallVerts[2].y = FIXED_TO_FLOAT(hS); wallVerts[0].y = FIXED_TO_FLOAT(l); wallVerts[1].y = FIXED_TO_FLOAT(lS); + if (rover->fofflags & FOF_FOG) { wallVerts[3].t = wallVerts[2].t = 0; @@ -1610,56 +1621,46 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom } else { - fixed_t texturevpeg; - boolean attachtobottom = false; - boolean slopeskew = false; // skew FOF walls with slopes? - // Wow, how was this missing from OpenGL for so long? // ...Oh well, anyway, Lower Unpegged now changes pegging of FOFs like in software // -- Monster Iestyn 26/06/18 - if (newline) - { - texturevpeg = sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid; - attachtobottom = !!(newline->flags & ML_DONTPEGBOTTOM); - slopeskew = !!(newline->flags & ML_SKEWTD); - } - else - { - texturevpeg = sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid; - attachtobottom = !!(gl_linedef->flags & ML_DONTPEGBOTTOM); - slopeskew = !!(rover->master->flags & ML_SKEWTD); - } + fixed_t texturevpeg = side->rowoffset + side->offsety_mid; + boolean attachtobottom = !!(lineflags & ML_DONTPEGBOTTOM); grTex = HWR_GetTexture(texnum); + xscale = FixedToFloat(side->scalex_mid); + yscale = FixedToFloat(side->scaley_mid); - if (!slopeskew) // no skewing + if (!(lineflags & ML_SKEWTD)) // no skewing { if (attachtobottom) - texturevpeg -= *rover->topheight - *rover->bottomheight; - wallVerts[3].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY; - wallVerts[2].t = (*rover->topheight - hS + texturevpeg) * grTex->scaleY; - wallVerts[0].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY; - wallVerts[1].t = (*rover->topheight - lS + texturevpeg) * grTex->scaleY; + texturevpeg -= (*rover->topheight - *rover->bottomheight) * yscale; + + wallVerts[3].t = (((*rover->topheight - h) * yscale) + texturevpeg) * grTex->scaleY; + wallVerts[2].t = (((*rover->topheight - hS) * yscale) + texturevpeg) * grTex->scaleY; + wallVerts[0].t = (((*rover->topheight - l) * yscale) + texturevpeg) * grTex->scaleY; + wallVerts[1].t = (((*rover->topheight - lS) * yscale) + texturevpeg) * grTex->scaleY; } else { if (!attachtobottom) // skew by top { wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; - wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY; - wallVerts[1].t = (hS - lS + texturevpeg) * grTex->scaleY; + wallVerts[0].t = (((h - l) * yscale) + texturevpeg) * grTex->scaleY; + wallVerts[1].t = (((hS - lS) * yscale) + texturevpeg) * grTex->scaleY; } else // skew by bottom { wallVerts[0].t = wallVerts[1].t = texturevpeg * grTex->scaleY; - wallVerts[3].t = wallVerts[0].t - (h - l) * grTex->scaleY; - wallVerts[2].t = wallVerts[1].t - (hS - lS) * grTex->scaleY; + wallVerts[3].t = wallVerts[0].t - ((h - l) * yscale) * grTex->scaleY; + wallVerts[2].t = wallVerts[1].t - ((hS - lS) * yscale) * grTex->scaleY; } } - wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX; - wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX; + wallVerts[0].s = wallVerts[3].s = ((cliplow * xscale) + side->offsetx_mid) * grTex->scaleX; + wallVerts[2].s = wallVerts[1].s = ((cliphigh * xscale) + side->offsetx_mid) * grTex->scaleX; } + if (rover->fofflags & FOF_FOG) { FBITFIELD blendmode; @@ -1726,14 +1727,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom if ((high1 < lowcut && highslope1 < lowcutslope) || (low1 > highcut && lowslope1 > highcutslope)) continue; - texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture); + side_t *side = &sides[rover->master->sidenum[0]]; if (rover->master->flags & ML_TFERLINE) { size_t linenum = gl_curline->linedef-gl_backsector->lines[0]; newline = rover->master->frontsector->lines[0] + linenum; - texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture); + side = &sides[newline->sidenum[0]]; } + + texnum = R_GetTextureNum(side->midtexture); + h = P_GetFFloorTopZAt (rover, v1x, v1y); hS = P_GetFFloorTopZAt (rover, v2x, v2y); l = P_GetFFloorBottomZAt(rover, v1x, v1y); @@ -1767,20 +1771,16 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom else { grTex = HWR_GetTexture(texnum); + xscale = FixedToFloat(side->scalex_mid); + yscale = FixedToFloat(side->scaley_mid); - if (newline) - { - wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY; - wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[newline->sidenum[0]].rowoffset) + sides[newline->sidenum[0]].offsety_mid) * grTex->scaleY; - } - else - { - wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid) * grTex->scaleY; - wallVerts[0].t = wallVerts[1].t = (h - l + (*rover->topheight - h + sides[rover->master->sidenum[0]].rowoffset + sides[rover->master->sidenum[0]].offsety_mid)) * grTex->scaleY; - } + fixed_t diff = (*rover->topheight - h) * yscale; - wallVerts[0].s = wallVerts[3].s = (cliplow + gl_sidedef->offsetx_mid) * grTex->scaleX; - wallVerts[2].s = wallVerts[1].s = (cliphigh + gl_sidedef->offsetx_mid) * grTex->scaleX; + wallVerts[3].t = wallVerts[2].t = (diff + side->rowoffset + side->offsety_mid) * grTex->scaleY; + wallVerts[0].t = wallVerts[1].t = (((h - l) * yscale) + (diff + side->rowoffset + side->offsety_mid)) * grTex->scaleY; + + wallVerts[0].s = wallVerts[3].s = ((cliplow * xscale) + side->offsetx_mid) * grTex->scaleX; + wallVerts[2].s = wallVerts[1].s = ((cliphigh * xscale) + side->offsetx_mid) * grTex->scaleX; } if (rover->fofflags & FOF_FOG) @@ -1837,12 +1837,6 @@ static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacks { fixed_t frontf1,frontf2, frontc1, frontc2; // front floor/ceiling ends fixed_t backf1, backf2, backc1, backc2; // back floor ceiling ends - boolean bothceilingssky = false, bothfloorssky = false; - - if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum) - bothceilingssky = true; - if (abacksector->floorpic == skyflatnum && afrontsector->floorpic == skyflatnum) - bothfloorssky = true; // GZDoom method of sloped line clipping @@ -1986,6 +1980,7 @@ static void HWR_AddLine(seg_t * line) checkforemptylines = true; gl_backsector = line->backsector; + bothceilingssky = bothfloorssky = false; if (!line->backsector) { @@ -1993,13 +1988,14 @@ static void HWR_AddLine(seg_t * line) } else { - boolean bothceilingssky = false, bothfloorssky = false; - gl_backsector = R_FakeFlat(gl_backsector, &tempsec, NULL, NULL, true); - if (gl_backsector->ceilingpic == skyflatnum && gl_frontsector->ceilingpic == skyflatnum) + if (gl_backsector->ceilingpic == skyflatnum && gl_frontsector->ceilingpic == skyflatnum + && !(P_SectorHasCeilingPortal(gl_backsector) || P_SectorHasCeilingPortal(gl_frontsector))) bothceilingssky = true; - if (gl_backsector->floorpic == skyflatnum && gl_frontsector->floorpic == skyflatnum) + + if (gl_backsector->floorpic == skyflatnum && gl_frontsector->floorpic == skyflatnum + && !(P_SectorHasFloorPortal(gl_backsector) || P_SectorHasFloorPortal(gl_frontsector))) bothfloorssky = true; if (bothceilingssky && bothfloorssky) // everything's sky? let's save us a bit of time then @@ -2126,11 +2122,8 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, INT32 i; float height = FIXED_TO_FLOAT(fixedheight); // constant y for all points on the convex flat polygon - float flatxref, flatyref; float fflatwidth = 64.0f, fflatheight = 64.0f; - UINT16 flatflag = 63; - - boolean texflat = false; + float xscale = 1.0f, yscale = 1.0f; float scrollx = 0.0f, scrolly = 0.0f; float tempxsow, tempytow, anglef = 0.0f; @@ -2161,8 +2154,8 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, if (levelflat->type == LEVELFLAT_FLAT) { size_t len = W_LumpLength(levelflat->u.flat.lumpnum); - flatflag = R_GetFlatSize(len) - 1; - fflatwidth = fflatheight = (float)(flatflag + 1); + unsigned flatflag = R_GetFlatSize(len); + fflatwidth = fflatheight = (float)flatflag; } else { @@ -2176,19 +2169,11 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, fflatwidth = levelflat->width; fflatheight = levelflat->height; } - texflat = true; } } else // set no texture HWR_SetCurrentTexture(NULL); - // reference point for flat texture coord for each vertex around the polygon - flatxref = FIXED_TO_FLOAT(polysector->origVerts[0].x); - flatyref = FIXED_TO_FLOAT(polysector->origVerts[0].y); - - flatxref = (float)(((fixed_t)flatxref & (~flatflag)) / fflatwidth); - flatyref = (float)(((fixed_t)flatyref & (~flatflag)) / fflatheight); - // transform v3d = planeVerts; @@ -2196,14 +2181,18 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(FOFsector->floorxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->flooryoffset)/fflatheight; + xscale = FixedToFloat(FOFsector->floorxscale); + yscale = FixedToFloat(FOFsector->flooryscale); + scrollx = FixedToFloat(FOFsector->floorxoffset) / fflatwidth; + scrolly = FixedToFloat(FOFsector->flooryoffset) / fflatheight; angle = FOFsector->floorangle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(FOFsector->ceilingxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(FOFsector->ceilingyoffset)/fflatheight; + xscale = FixedToFloat(FOFsector->ceilingxscale); + yscale = FixedToFloat(FOFsector->ceilingyscale); + scrollx = FixedToFloat(FOFsector->ceilingxoffset) / fflatwidth; + scrolly = FixedToFloat(FOFsector->ceilingyoffset) / fflatheight; angle = FOFsector->ceilingangle; } } @@ -2211,43 +2200,30 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, { if (!isceiling) // it's a floor { - scrollx = FIXED_TO_FLOAT(gl_frontsector->floorxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gl_frontsector->flooryoffset)/fflatheight; + xscale = FixedToFloat(gl_frontsector->floorxscale); + yscale = FixedToFloat(gl_frontsector->flooryscale); + scrollx = FixedToFloat(gl_frontsector->floorxoffset) / fflatwidth; + scrolly = FixedToFloat(gl_frontsector->flooryoffset) / fflatheight; angle = gl_frontsector->floorangle; } else // it's a ceiling { - scrollx = FIXED_TO_FLOAT(gl_frontsector->ceilingxoffset)/fflatwidth; - scrolly = FIXED_TO_FLOAT(gl_frontsector->ceilingyoffset)/fflatheight; + xscale = FixedToFloat(gl_frontsector->ceilingxscale); + yscale = FixedToFloat(gl_frontsector->ceilingyscale); + scrollx = FixedToFloat(gl_frontsector->ceilingxoffset) / fflatwidth; + scrolly = FixedToFloat(gl_frontsector->ceilingyoffset) / fflatheight; angle = gl_frontsector->ceilingangle; } } - if (angle) // Only needs to be done if there's an altered angle - { - tempxsow = flatxref; - tempytow = flatyref; - - anglef = ANG2RAD(InvAngle(angle)); - - flatxref = (tempxsow * cos(anglef)) - (tempytow * sin(anglef)); - flatyref = (tempxsow * sin(anglef)) + (tempytow * cos(anglef)); - } + anglef = ANG2RAD(InvAngle(angle)); for (i = 0; i < (INT32)nrPlaneVerts; i++,v3d++) { // Go from the polysector's original vertex locations // Means the flat is offset based on the original vertex locations - if (texflat) - { - v3d->s = (float)(FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) + scrollx; - v3d->t = -(float)(FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly; - } - else - { - v3d->s = (float)((FIXED_TO_FLOAT(polysector->origVerts[i].x) / fflatwidth) - flatxref + scrollx); - v3d->t = (float)(flatyref - (FIXED_TO_FLOAT(polysector->origVerts[i].y) / fflatheight) + scrolly); - } + v3d->s = (FixedToFloat(polysector->origVerts[i].x) / fflatwidth) + (scrollx / xscale); + v3d->t = -(FixedToFloat(polysector->origVerts[i].y) / fflatheight) + (scrolly / yscale); // Need to rotate before translate if (angle) // Only needs to be done if there's an altered angle @@ -2259,6 +2235,9 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, v3d->t = (tempxsow * sin(anglef)) + (tempytow * cos(anglef)); } + v3d->s *= xscale; + v3d->t *= yscale; + v3d->x = FIXED_TO_FLOAT(polysector->vertices[i]->x); v3d->y = height; v3d->z = FIXED_TO_FLOAT(polysector->vertices[i]->y); @@ -2357,6 +2336,46 @@ static FBITFIELD HWR_RippleBlend(sector_t *sector, ffloor_t *rover, boolean ceil return /*R_IsRipplePlane(sector, rover, ceiling)*/ (rover->fofflags & FOF_RIPPLE) ? PF_Ripple : 0; } +// +// HWR_DoCulling +// Hardware version of R_DoCulling +// (see r_main.c) +static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float vz, float bottomh, float toph) +{ + float cullplane; + + if (!cullheight) + return false; + + cullplane = FIXED_TO_FLOAT(cullheight->frontsector->floorheight); + if (cullheight->args[1]) // Group culling + { + if (!viewcullheight) + return false; + + // Make sure this is part of the same group + if (viewcullheight->frontsector == cullheight->frontsector) + { + // OK, we can cull + if (vz > cullplane && toph < cullplane) // Cull if below plane + return true; + + if (bottomh > cullplane && vz <= cullplane) // Cull if above plane + return true; + } + } + else // Quick culling + { + if (vz > cullplane && toph < cullplane) // Cull if below plane + return true; + + if (bottomh > cullplane && vz <= cullplane) // Cull if above plane + return true; + } + + return false; +} + // -----------------+ // HWR_Subsector : Determine floor/ceiling planes. // : Add sprites of things in sector. @@ -2410,64 +2429,16 @@ static void HWR_Subsector(size_t num) } //SoM: 4/7/2000: Test to make Boom water work in Hardware mode. - gl_frontsector = R_FakeFlat(gl_frontsector, &tempsec, &floorlightlevel, - &ceilinglightlevel, false); - //FIXME: Use floorlightlevel and ceilinglightlevel insted of lightlevel. + gl_frontsector = R_FakeFlat(gl_frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, false); floorcolormap = ceilingcolormap = gl_frontsector->extra_colormap; - // ------------------------------------------------------------------------ - // sector lighting, DISABLED because it's done in HWR_StoreWallRange - // ------------------------------------------------------------------------ - /// \todo store a RGBA instead of just intensity, allow coloured sector lighting - //light = (FUBYTE)(sub->sector->lightlevel & 0xFF) / 255.0f; - //gl_cursectorlight.red = light; - //gl_cursectorlight.green = light; - //gl_cursectorlight.blue = light; - //gl_cursectorlight.alpha = light; - -// ----- end special tricks ----- cullFloorHeight = P_GetSectorFloorZAt (gl_frontsector, viewx, viewy); cullCeilingHeight = P_GetSectorCeilingZAt(gl_frontsector, viewx, viewy); locFloorHeight = P_GetSectorFloorZAt (gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y); locCeilingHeight = P_GetSectorCeilingZAt(gl_frontsector, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y); - if (gl_frontsector->ffloors) - { - boolean anyMoved = gl_frontsector->moved; - - if (anyMoved == false) - { - for (rover = gl_frontsector->ffloors; rover; rover = rover->next) - { - sector_t *controlSec = §ors[rover->secnum]; - if (controlSec->moved == true) - { - anyMoved = true; - break; - } - } - } - - if (anyMoved == true) - { - gl_frontsector->numlights = sub->sector->numlights = 0; - R_Prep3DFloors(gl_frontsector); - sub->sector->lightlist = gl_frontsector->lightlist; - sub->sector->numlights = gl_frontsector->numlights; - sub->sector->moved = gl_frontsector->moved = false; - } - - light = R_GetPlaneLight(gl_frontsector, locFloorHeight, false); - if (gl_frontsector->floorlightsec == -1 && !gl_frontsector->floorlightabsolute) - floorlightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->floorlightlevel)); - floorcolormap = *gl_frontsector->lightlist[light].extra_colormap; - - light = R_GetPlaneLight(gl_frontsector, locCeilingHeight, false); - if (gl_frontsector->ceilinglightsec == -1 && !gl_frontsector->ceilinglightabsolute) - ceilinglightlevel = max(0, min(255, *gl_frontsector->lightlist[light].lightlevel + gl_frontsector->ceilinglightlevel)); - ceilingcolormap = *gl_frontsector->lightlist[light].extra_colormap; - } + R_CheckSectorLightLists(sub->sector, gl_frontsector, &floorlightlevel, &ceilinglightlevel, &floorcolormap, &ceilingcolormap); sub->sector->extra_colormap = gl_frontsector->extra_colormap; @@ -2531,27 +2502,36 @@ static void HWR_Subsector(size_t num) for (rover = gl_frontsector->ffloors; rover; rover = rover->next) { - fixed_t cullHeight, centerHeight; - - // bottom plane - cullHeight = P_GetFFloorBottomZAt(rover, viewx, viewy); - centerHeight = P_GetFFloorBottomZAt(rover, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y); + fixed_t bottomCullHeight, topCullHeight, centerHeight; if (!(rover->fofflags & FOF_EXISTS) || !(rover->fofflags & FOF_RENDERPLANES)) continue; if (sub->validcount == validcount) continue; + + // rendering heights for bottom and top planes + bottomCullHeight = P_GetFFloorBottomZAt(rover, viewx, viewy); + topCullHeight = P_GetFFloorTopZAt(rover, viewx, viewy); + + if (gl_frontsector->cullheight) + { + if (HWR_DoCulling(gl_frontsector->cullheight, viewsector->cullheight, gl_viewz, FIXED_TO_FLOAT(bottomCullHeight), FIXED_TO_FLOAT(topCullHeight))) + continue; + } + + // bottom plane + centerHeight = P_GetFFloorBottomZAt(rover, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y); if (centerHeight <= locCeilingHeight && centerHeight >= locFloorHeight && - ((dup_viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || - (dup_viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) + ((dup_viewz < bottomCullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || + (dup_viewz > bottomCullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) { if (rover->fofflags & FOF_FOG) { UINT8 alpha; - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < bottomCullHeight ? true : false); alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, @@ -2564,7 +2544,7 @@ static void HWR_Subsector(size_t num) } else if ((rover->fofflags & FOF_TRANSLUCENT && !(rover->fofflags & FOF_SPLAT)) || rover->blend) // SoM: Flags are more efficient { - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < bottomCullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->bottompic], &extrasubsectors[num], @@ -2578,26 +2558,25 @@ static void HWR_Subsector(size_t num) else { HWR_GetLevelFlat(&levelflats[*rover->bottompic]); - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < bottomCullHeight ? true : false); HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } } // top plane - cullHeight = P_GetFFloorTopZAt(rover, viewx, viewy); centerHeight = P_GetFFloorTopZAt(rover, gl_frontsector->soundorg.x, gl_frontsector->soundorg.y); if (centerHeight >= locFloorHeight && centerHeight <= locCeilingHeight && - ((dup_viewz > cullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || - (dup_viewz < cullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) + ((dup_viewz > topCullHeight && (rover->fofflags & FOF_BOTHPLANES || !(rover->fofflags & FOF_INVERTPLANES))) || + (dup_viewz < topCullHeight && (rover->fofflags & FOF_BOTHPLANES || rover->fofflags & FOF_INVERTPLANES)))) { if (rover->fofflags & FOF_FOG) { UINT8 alpha; - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < topCullHeight ? true : false); alpha = HWR_FogBlockAlpha(*gl_frontsector->lightlist[light].lightlevel, rover->master->frontsector->extra_colormap); HWR_AddTransparentFloor(0, @@ -2610,7 +2589,7 @@ static void HWR_Subsector(size_t num) } else if ((rover->fofflags & FOF_TRANSLUCENT && !(rover->fofflags & FOF_SPLAT)) || rover->blend) { - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < topCullHeight ? true : false); HWR_AddTransparentFloor(&levelflats[*rover->toppic], &extrasubsectors[num], @@ -2624,7 +2603,7 @@ static void HWR_Subsector(size_t num) else { HWR_GetLevelFlat(&levelflats[*rover->toppic]); - light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false); + light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < topCullHeight ? true : false); HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic], rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap); } @@ -2884,46 +2863,6 @@ static void HWR_LinkDrawHackFinish(void) linkdrawcount = 0; } -// -// HWR_DoCulling -// Hardware version of R_DoCulling -// (see r_main.c) -static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float vz, float bottomh, float toph) -{ - float cullplane; - - if (!cullheight) - return false; - - cullplane = FIXED_TO_FLOAT(cullheight->frontsector->floorheight); - if (cullheight->args[1]) // Group culling - { - if (!viewcullheight) - return false; - - // Make sure this is part of the same group - if (viewcullheight->frontsector == cullheight->frontsector) - { - // OK, we can cull - if (vz > cullplane && toph < cullplane) // Cull if below plane - return true; - - if (bottomh > cullplane && vz <= cullplane) // Cull if above plane - return true; - } - } - else // Quick culling - { - if (vz > cullplane && toph < cullplane) // Cull if below plane - return true; - - if (bottomh > cullplane && vz <= cullplane) // Cull if above plane - return true; - } - - return false; -} - static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) { patch_t *gpatch; @@ -4324,7 +4263,7 @@ static void HWR_DrawSprites(void) if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) { - if (!cv_glmodels.value || md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f) + if (!cv_glmodels.value || !md2_playermodels[((skin_t*)spr->mobj->skin)->skinnum].found || md2_playermodels[((skin_t*)spr->mobj->skin)->skinnum].scale < 0.0f) HWR_DrawSprite(spr); else { @@ -4334,7 +4273,7 @@ static void HWR_DrawSprites(void) } else { - if (!cv_glmodels.value || md2_models[spr->mobj->sprite].notfound || md2_models[spr->mobj->sprite].scale < 0.0f) + if (!cv_glmodels.value || !md2_models[spr->mobj->sprite].found || md2_models[spr->mobj->sprite].scale < 0.0f) HWR_DrawSprite(spr); else { @@ -4497,6 +4436,8 @@ static void HWR_ProjectSprite(mobj_t *thing) spriteyscale = FIXED_TO_FLOAT(interp.spriteyscale); // transform the origin point + if (thing->type == MT_OVERLAY) // Handle overlays + R_ThingOffsetOverlay(thing, &interp.x, &interp.y); tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx; tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy; @@ -4509,11 +4450,11 @@ static void HWR_ProjectSprite(mobj_t *thing) if (cv_glmodels.value) //Yellow: Only MD2's dont disappear { if (thing->skin && thing->sprite == SPR_PLAY) - md2 = &md2_playermodels[( (skin_t *)thing->skin - skins )]; + md2 = &md2_playermodels[((skin_t *)thing->skin)->skinnum]; else md2 = &md2_models[thing->sprite]; - if (md2->notfound || md2->scale < 0.0f) + if (!md2->found || md2->scale < 0.0f) return; } else @@ -4813,6 +4754,8 @@ static void HWR_ProjectSprite(mobj_t *thing) // calculate tz for tracer, same way it is calculated for this sprite // transform the origin point + if (thing->tracer->type == MT_OVERLAY) // Handle overlays + R_ThingOffsetOverlay(thing->tracer, &tracer_interp.x, &tracer_interp.y); tr_x = FIXED_TO_FLOAT(tracer_interp.x) - gl_viewx; tr_y = FIXED_TO_FLOAT(tracer_interp.y) - gl_viewy; @@ -4900,8 +4843,8 @@ static void HWR_ProjectSprite(mobj_t *thing) } else if (thing->skin && thing->sprite == SPR_PLAY) // This thing is a player! { - size_t skinnum = (skin_t*)thing->skin-skins; - vis->colormap = R_GetTranslationColormap((INT32)skinnum, vis->color, GTC_CACHE); + UINT8 skinnum = ((skin_t*)thing->skin)->skinnum; + vis->colormap = R_GetTranslationColormap(skinnum, vis->color, GTC_CACHE); } else vis->colormap = R_GetTranslationColormap(TC_DEFAULT, vis->color ? vis->color : SKINCOLOR_CYAN, GTC_CACHE); @@ -5256,6 +5199,9 @@ void HWR_BuildSkyDome(void) static void HWR_DrawSkyBackground(player_t *player) { + if (HWR_IsWireframeMode()) + return; + HWD.pfnSetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated); if (cv_glskydome.value) @@ -5621,6 +5567,9 @@ static void HWR_RenderSkyboxView(player_t *player) // Reset the shader state. HWR_SetShaderState(); + if (HWR_IsWireframeMode()) + HWD.pfnSetSpecialState(HWD_SET_WIREFRAME, 1); + validcount++; if (cv_glbatching.value) @@ -5654,6 +5603,9 @@ static void HWR_RenderSkyboxView(player_t *player) HWR_CreateDrawNodes(); } + if (HWR_IsWireframeMode()) + HWD.pfnSetSpecialState(HWD_SET_WIREFRAME, 0); + HWD.pfnSetTransform(NULL); HWD.pfnUnSetShader(); @@ -5802,6 +5754,9 @@ void HWR_RenderPlayerView(player_t *player) // Reset the shader state. HWR_SetShaderState(); + if (HWR_IsWireframeMode()) + HWD.pfnSetSpecialState(HWD_SET_WIREFRAME, 1); + ps_numbspcalls.value.i = 0; ps_numpolyobjects.value.i = 0; PS_START_TIMING(ps_bsptime); @@ -5849,6 +5804,9 @@ void HWR_RenderPlayerView(player_t *player) HWR_CreateDrawNodes(); } + if (HWR_IsWireframeMode()) + HWD.pfnSetSpecialState(HWD_SET_WIREFRAME, 0); + HWD.pfnSetTransform(NULL); HWD.pfnUnSetShader(); @@ -5926,6 +5884,8 @@ consvar_t cv_glsolvetjoin = CVAR_INIT ("gr_solvetjoin", "On", 0, CV_OnOff, NULL) consvar_t cv_glbatching = CVAR_INIT ("gr_batching", "On", 0, CV_OnOff, NULL); +consvar_t cv_glwireframe = CVAR_INIT ("gr_wireframe", "Off", 0, CV_OnOff, NULL); + static void CV_glfiltermode_OnChange(void) { if (rendermode == render_opengl) @@ -5966,6 +5926,8 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glsolvetjoin); CV_RegisterVar(&cv_glbatching); + + CV_RegisterVar(&cv_glwireframe); } // -------------------------------------------------------------------------- diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 1f93eddc2..dac2131e8 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -104,6 +104,8 @@ extern consvar_t cv_glslopecontrast; extern consvar_t cv_glbatching; +extern consvar_t cv_glwireframe; + extern float gl_viewwidth, gl_viewheight, gl_baseviewwindowy; extern float gl_viewwindowx, gl_basewindowcentery; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 3e080105c..18648a499 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -35,6 +35,7 @@ #include "../m_misc.h" #include "../w_wad.h" #include "../z_zone.h" +#include "../r_state.h" #include "../r_things.h" #include "../r_draw.h" #include "../p_tick.h" @@ -72,7 +73,8 @@ #endif md2_t md2_models[NUMSPRITES]; -md2_t md2_playermodels[MAXSKINS]; +md2_t *md2_playermodels = NULL; +size_t md2_numplayermodels = 0; /* @@ -484,24 +486,7 @@ static boolean nomd2s = false; void HWR_InitModels(void) { size_t i; - INT32 s; - FILE *f; - char name[26], filename[32]; - float scale, offset; - size_t prefixlen; - CONS_Printf("HWR_InitModels()...\n"); - for (s = 0; s < MAXSKINS; s++) - { - md2_playermodels[s].scale = -1.0f; - md2_playermodels[s].model = NULL; - md2_playermodels[s].grpatch = NULL; - md2_playermodels[s].notexturefile = false; - md2_playermodels[s].noblendfile = false; - md2_playermodels[s].skin = -1; - md2_playermodels[s].notfound = true; - md2_playermodels[s].error = false; - } for (i = 0; i < NUMSPRITES; i++) { md2_models[i].scale = -1.0f; @@ -509,11 +494,48 @@ void HWR_InitModels(void) md2_models[i].grpatch = NULL; md2_models[i].notexturefile = false; md2_models[i].noblendfile = false; - md2_models[i].skin = -1; - md2_models[i].notfound = true; + md2_models[i].found = false; md2_models[i].error = false; } + if (numsprites && numskins) + HWR_LoadModels(); +} + +void HWR_LoadModels(void) +{ + size_t i; + INT32 s; + FILE *f; + + char name[26], filename[32]; + // name[24] is used to check for names in the models.dat file that match with sprites or player skins + // sprite names are always 4 characters long, and names is for player skins can be up to 19 characters long + // PLAYERMODELPREFIX is 6 characters long + float scale, offset; + size_t prefixlen; + + if (nomd2s) + return; + + // realloc player models table + if (numskins != (INT32)md2_numplayermodels) + { + md2_numplayermodels = (size_t)numskins; + md2_playermodels = Z_Realloc(md2_playermodels, sizeof(md2_t) * md2_numplayermodels, PU_STATIC, NULL); + + for (s = 0; s < numskins; s++) + { + md2_playermodels[s].scale = -1.0f; + md2_playermodels[s].model = NULL; + md2_playermodels[s].grpatch = NULL; + md2_playermodels[s].notexturefile = false; + md2_playermodels[s].noblendfile = false; + md2_playermodels[s].found = false; + md2_playermodels[s].error = false; + } + } + // read the models.dat file //Filename checking fixed ~Monster Iestyn and Golden f = fopen(va("%s"PATHSEP"%s", srb2home, "models.dat"), "rt"); @@ -537,23 +559,24 @@ void HWR_InitModels(void) char *skinname = name; size_t len = strlen(name); - // check for the player model prefix. + // Check for the player model prefix. if (!strnicmp(name, PLAYERMODELPREFIX, prefixlen) && (len > prefixlen)) { skinname += prefixlen; goto addskinmodel; } - // add sprite model - if (len == 4) // must be 4 characters long exactly. otherwise it's not a sprite name. + // Add sprite models. + // Must be 4 characters long exactly. Otherwise, it's not a sprite name. + if (len == 4) { - for (i = 0; i < NUMSPRITES; i++) + for (i = 0; i < numsprites; i++) { if (stricmp(name, sprnames[i]) == 0) { md2_models[i].scale = scale; md2_models[i].offset = offset; - md2_models[i].notfound = false; + md2_models[i].found = true; strcpy(md2_models[i].filename, filename); goto modelfound; } @@ -561,137 +584,24 @@ void HWR_InitModels(void) } addskinmodel: - // add player model - for (s = 0; s < MAXSKINS; s++) + // Add player models. + for (s = 0; s < numskins; s++) { - if (stricmp(skinname, skins[s].name) == 0) + if (stricmp(skinname, skins[s]->name) == 0) { - md2_playermodels[s].skin = s; md2_playermodels[s].scale = scale; md2_playermodels[s].offset = offset; - md2_playermodels[s].notfound = false; + md2_playermodels[s].found = true; strcpy(md2_playermodels[s].filename, filename); goto modelfound; } } modelfound: - // move on to next line... + // Move on to the next line... continue; } - fclose(f); -} -void HWR_AddPlayerModel(int skin) // For skins that were added after startup -{ - FILE *f; - char name[26], filename[32]; - float scale, offset; - size_t prefixlen; - - if (nomd2s) - return; - - //CONS_Printf("HWR_AddPlayerModel()...\n"); - - // read the models.dat file - //Filename checking fixed ~Monster Iestyn and Golden - f = fopen(va("%s"PATHSEP"%s", srb2home, "models.dat"), "rt"); - - if (!f) - { - f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt"); - if (!f) - { - CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno)); - nomd2s = true; - return; - } - } - - // length of the player model prefix - prefixlen = strlen(PLAYERMODELPREFIX); - - // Check for any models that match the names of player skins! - while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4) - { - char *skinname = name; - size_t len = strlen(name); - - // ignore the player model prefix. - if (!strnicmp(name, PLAYERMODELPREFIX, prefixlen) && (len > prefixlen)) - skinname += prefixlen; - - if (stricmp(skinname, skins[skin].name) == 0) - { - md2_playermodels[skin].skin = skin; - md2_playermodels[skin].scale = scale; - md2_playermodels[skin].offset = offset; - md2_playermodels[skin].notfound = false; - strcpy(md2_playermodels[skin].filename, filename); - goto playermodelfound; - } - } - - md2_playermodels[skin].notfound = true; -playermodelfound: - fclose(f); -} - -void HWR_AddSpriteModel(size_t spritenum) // For sprites that were added after startup -{ - FILE *f; - // name[24] is used to check for names in the models.dat file that match with sprites or player skins - // sprite names are always 4 characters long, and names is for player skins can be up to 19 characters long - // PLAYERMODELPREFIX is 6 characters long - char name[26], filename[32]; - float scale, offset; - - if (nomd2s) - return; - - if (spritenum == SPR_PLAY) // Handled already NEWMD2: Per sprite, per-skin check - return; - - // Read the models.dat file - //Filename checking fixed ~Monster Iestyn and Golden - f = fopen(va("%s"PATHSEP"%s", srb2home, "models.dat"), "rt"); - - if (!f) - { - f = fopen(va("%s"PATHSEP"%s", srb2path, "models.dat"), "rt"); - if (!f) - { - CONS_Printf("%s %s\n", M_GetText("Error while loading models.dat:"), strerror(errno)); - nomd2s = true; - return; - } - } - - // Check for any models that match the names of sprite names! - while (fscanf(f, "%25s %31s %f %f", name, filename, &scale, &offset) == 4) - { - // length of the sprite name - size_t len = strlen(name); - if (len != 4) // must be 4 characters long exactly. otherwise it's not a sprite name. - continue; - - // check for the player model prefix. - if (!strnicmp(name, PLAYERMODELPREFIX, strlen(PLAYERMODELPREFIX))) - continue; // that's not a sprite... - - if (stricmp(name, sprnames[spritenum]) == 0) - { - md2_models[spritenum].scale = scale; - md2_models[spritenum].offset = offset; - md2_models[spritenum].notfound = false; - strcpy(md2_models[spritenum].filename, filename); - goto spritemodelfound; - } - } - - md2_models[spritenum].notfound = true; -spritemodelfound: fclose(f); } @@ -1380,8 +1290,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) // 2. draw model with correct position, rotation,... if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) // Use the player MD2 list if the mobj has a skin and is using the player sprites { - md2 = &md2_playermodels[(skin_t*)spr->mobj->skin-skins]; - md2->skin = (skin_t*)spr->mobj->skin-skins; + UINT8 skinnum = ((skin_t*)spr->mobj->skin)->skinnum; + md2 = &md2_playermodels[skinnum]; } else { @@ -1476,7 +1386,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) skinnum = TC_RAINBOW; } else if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) - skinnum = (INT32)((skin_t*)spr->mobj->skin-skins); + skinnum = ((skin_t*)spr->mobj->skin)->skinnum; else skinnum = TC_DEFAULT; } @@ -1577,6 +1487,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) #undef INTERPOLERATION_LIMIT #endif + if (spr->mobj->type == MT_OVERLAY) // Handle overlays + R_ThingOffsetOverlay(spr->mobj, &interp.x, &interp.y); //Hurdler: it seems there is still a small problem with mobj angle p.x = FIXED_TO_FLOAT(interp.x); p.y = FIXED_TO_FLOAT(interp.y)+md2->offset; diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h index f1cca763c..473f21cb7 100644 --- a/src/hardware/hw_md2.h +++ b/src/hardware/hw_md2.h @@ -31,17 +31,17 @@ typedef struct boolean notexturefile; // true if texture file was not found void *blendgrpatch; boolean noblendfile; // true if blend texture file was not found - boolean notfound; - INT32 skin; + boolean found; boolean error; } md2_t; extern md2_t md2_models[NUMSPRITES]; -extern md2_t md2_playermodels[MAXSKINS]; +extern md2_t *md2_playermodels; +extern size_t md2_numplayermodels; void HWR_InitModels(void); -void HWR_AddPlayerModel(INT32 skin); -void HWR_AddSpriteModel(size_t spritenum); +void HWR_LoadModels(void); + boolean HWR_DrawModel(gl_vissprite_t *spr); #define PLAYERMODELPREFIX "PLAYER" diff --git a/src/hardware/hw_md2load.c b/src/hardware/hw_md2load.c index fed81e411..ce8eb35f3 100644 --- a/src/hardware/hw_md2load.c +++ b/src/hardware/hw_md2load.c @@ -328,8 +328,8 @@ model_t *MD2_LoadModel(const char *fileName, int ztag, boolean useFloat) texcoords = (md2texcoord_t*)&buffer[header->offsetST]; frames = (md2frame_t*)&buffer[header->offsetFrames]; - retModel->framenames = (char*)Z_Calloc(header->numFrames*16, ztag, 0); - fname = retModel->framenames; + retModel->frameNames = (char*)Z_Calloc(header->numFrames*16, ztag, 0); + fname = retModel->frameNames; for (i = 0; i < header->numFrames; i++) { md2frame_t *fr = (md2frame_t*)&buffer[header->offsetFrames + foffset]; diff --git a/src/hardware/hw_md3load.c b/src/hardware/hw_md3load.c index 87931d27b..eccc48424 100644 --- a/src/hardware/hw_md3load.c +++ b/src/hardware/hw_md3load.c @@ -230,8 +230,8 @@ model_t *MD3_LoadModel(const char *fileName, int ztag, boolean useFloat) retModel->meshes = (mesh_t*)Z_Calloc(sizeof(mesh_t)*retModel->numMeshes, ztag, 0); frames = (md3Frame*)&buffer[mdh->offsetFrames]; - retModel->framenames = (char*)Z_Calloc(mdh->numFrames*16, ztag, 0); - fname = retModel->framenames; + retModel->frameNames = (char*)Z_Calloc(mdh->numFrames*16, ztag, 0); + fname = retModel->frameNames; for (i = 0; i < mdh->numFrames; i++) { memcpy(fname, frames->name, 16); diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c index 4b6bce6f7..0d656f35a 100644 --- a/src/hardware/hw_model.c +++ b/src/hardware/hw_model.c @@ -10,6 +10,8 @@ #include "../doomdef.h" #include "../doomtype.h" #include "../info.h" +#include "../r_skins.h" +#include "../r_state.h" #include "../z_zone.h" #include "hw_model.h" #include "hw_md2load.h" @@ -141,9 +143,7 @@ tag_t *GetTagByName(model_t *model, char *name, int frame) // // LoadModel // -// Load a model and -// convert it to the -// internal format. +// Load a model and convert it to the internal format. // model_t *LoadModel(const char *filename, int ztag) { @@ -193,9 +193,6 @@ model_t *LoadModel(const char *filename, int ztag) return NULL; } - model->mdlFilename = (char*)Z_Malloc(strlen(filename)+1, ztag, 0); - strcpy(model->mdlFilename, filename); - Optimize(model); GeneratePolygonNormals(model, ztag); LoadModelSprite2(model); @@ -236,15 +233,16 @@ model_t *LoadModel(const char *filename, int ztag) void HWR_ReloadModels(void) { size_t i; - INT32 s; - for (s = 0; s < MAXSKINS; s++) + HWR_LoadModels(); + + for (i = 0; i < md2_numplayermodels; i++) { - if (md2_playermodels[s].model) - LoadModelSprite2(md2_playermodels[s].model); + if (md2_playermodels[i].model) + LoadModelSprite2(md2_playermodels[i].model); } - for (i = 0; i < NUMSPRITES; i++) + for (i = 0; i < numsprites; i++) { if (md2_models[i].model) LoadModelInterpolationSettings(md2_models[i].model); @@ -255,7 +253,7 @@ void LoadModelInterpolationSettings(model_t *model) { INT32 i; INT32 numframes = model->meshes[0].numFrames; - char *framename = model->framenames; + char *framename = model->frameNames; if (!framename) return; @@ -295,7 +293,7 @@ void LoadModelSprite2(model_t *model) INT32 i; modelspr2frames_t *spr2frames = NULL; INT32 numframes = model->meshes[0].numFrames; - char *framename = model->framenames; + char *framename = model->frameNames; if (!framename) return; diff --git a/src/hardware/hw_model.h b/src/hardware/hw_model.h index 6b39eb24d..f057271df 100644 --- a/src/hardware/hw_model.h +++ b/src/hardware/hw_model.h @@ -91,17 +91,14 @@ typedef struct model_s { int maxNumFrames; - int numMaterials; - material_t *materials; int numMeshes; mesh_t *meshes; + int numMaterials; + material_t *materials; int numTags; tag_t *tags; - char *mdlFilename; - boolean unloaded; - - char *framenames; + char *frameNames; boolean interpolate[256]; modelspr2frames_t *spr2frames; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 6296cd765..1cae08da6 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -303,6 +303,8 @@ typedef void (APIENTRY * PFNglDisable) (GLenum cap); static PFNglDisable pglDisable; typedef void (APIENTRY * PFNglGetFloatv) (GLenum pname, GLfloat *params); static PFNglGetFloatv pglGetFloatv; +typedef void (APIENTRY * PFNglPolygonMode) (GLenum, GLenum); +static PFNglPolygonMode pglPolygonMode; /* Depth Buffer */ typedef void (APIENTRY * PFNglClearDepth) (GLclampd depth); @@ -477,6 +479,7 @@ boolean SetupGLfunc(void) GETOPENGLFUNC(pglGetFloatv, glGetFloatv) GETOPENGLFUNC(pglGetIntegerv, glGetIntegerv) GETOPENGLFUNC(pglGetString, glGetString) + GETOPENGLFUNC(pglPolygonMode, glPolygonMode) GETOPENGLFUNC(pglClearDepth, glClearDepth) GETOPENGLFUNC(pglDepthFunc, glDepthFunc) @@ -2493,6 +2496,10 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value) Flush(); //??? if we want to change filter mode by texture, remove this break; + case HWD_SET_WIREFRAME: + pglPolygonMode(GL_FRONT_AND_BACK, Value ? GL_LINE : GL_FILL); + break; + default: break; } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 76943940c..c3d7bd397 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -79,6 +79,7 @@ patch_t *nto_font[NT_FONTSIZE]; static player_t *plr; boolean chat_on; // entering a chat message? +boolean chat_on_first_event; // blocker for first chat input event static char w_chat[HU_MAXMSGLEN + 1]; static size_t c_input = 0; // let's try to make the chat input less shitty. static boolean headsupactive = false; @@ -618,7 +619,9 @@ static void Command_CSay_f(void) DoSayCommand(0, 1, HU_CSAY); } -static tic_t stop_spamming[MAXPLAYERS]; + +static tic_t spam_tokens[MAXPLAYERS]; +static tic_t spam_tics[MAXPLAYERS]; /** Receives a message, processing an ::XD_SAY command. * \sa DoSayCommand @@ -670,14 +673,14 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) // before we do anything, let's verify the guy isn't spamming, get this easier on us. //if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY)) - if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & HU_CSAY)) + if (spam_tokens[playernum] <= 0 && cv_chatspamprotection.value && !(flags & HU_CSAY)) { CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]); - stop_spamming[playernum] = 4; + spam_tics[playernum] = 0; spam_eatmsg = 1; } else - stop_spamming[playernum] = 4; // you can hold off for 4 tics, can you? + spam_tokens[playernum] -= 1; // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. @@ -857,6 +860,25 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) // void HU_Ticker(void) { + // do this server-side, too + if (netgame) + { + size_t i = 0; + + // handle spam while we're at it: + for(; (i= (tic_t)cv_chatspamspeed.value) + { + spam_tokens[i]++; + spam_tics[i] = 0; + } + } + } + } + if (dedicated) return; @@ -879,13 +901,6 @@ void HU_Ticker(void) { size_t i = 0; - // handle spam while we're at it: - for(; (i 0) - stop_spamming[i]--; - } - // handle chat timers for (i=0; (itype != ev_keydown) + if (ev->type != ev_keydown && ev->type != ev_text) return false; // only KeyDown events now... @@ -1054,11 +1069,15 @@ boolean HU_Responder(event_t *ev) if (!chat_on) { + if (ev->type == ev_text) + return false; + // enter chat mode if ((ev->key == gamecontrol[GC_TALKKEY][0] || ev->key == gamecontrol[GC_TALKKEY][1]) && netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise. { chat_on = true; + chat_on_first_event = false; w_chat[0] = 0; teamtalk = false; chat_scrollmedown = true; @@ -1069,6 +1088,7 @@ boolean HU_Responder(event_t *ev) && netgame && !OLD_MUTE) { chat_on = true; + chat_on_first_event = false; w_chat[0] = 0; teamtalk = G_GametypeHasTeams(); // Don't teamtalk if we don't have teams. chat_scrollmedown = true; @@ -1078,6 +1098,31 @@ boolean HU_Responder(event_t *ev) } else // if chat_on { + if (!chat_on_first_event) + { + // since the text event is sent immediately after the keydown event, + // we need to make sure that nothing is displayed once the chat + // opens, otherwise a 't' would be outputted. + chat_on_first_event = true; + return true; + } + + if (ev->type == ev_text) + { + if ((c < HU_FONTSTART || c > HU_FONTEND || !hu_font[c-HU_FONTSTART]) + && c != ' ') // Allow spaces, of course + { + return false; + } + + if (CHAT_MUTE || strlen(w_chat) >= HU_MAXMSGLEN) + return true; + + memmove(&w_chat[c_input + 1], &w_chat[c_input], strlen(w_chat) - c_input + 1); + w_chat[c_input] = c; + c_input++; + return true; + } // Ignore modifier keys // Note that we do this here so users can still set @@ -1087,23 +1132,8 @@ boolean HU_Responder(event_t *ev) || ev->key == KEY_LALT || ev->key == KEY_RALT) return true; - c = (INT32)ev->key; - - // I know this looks very messy but this works. If it ain't broke, don't fix it! - // shift LETTERS to uppercase if we have capslock or are holding shift - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) - { - if (shiftdown ^ capslock) - c = shiftxform[c]; - } - else // if we're holding shift we should still shift non letter symbols - { - if (shiftdown) - c = shiftxform[c]; - } - // pasting. pasting is cool. chat is a bit limited, though :( - if ((c == 'v' || c == 'V') && ctrldown) + if (c == 'v' && ctrldown) { const char *paste; size_t chatlen; @@ -1171,16 +1201,6 @@ boolean HU_Responder(event_t *ev) else c_input++; } - else if ((c >= HU_FONTSTART && c <= HU_FONTEND && hu_font[c-HU_FONTSTART]) - || c == ' ') // Allow spaces, of course - { - if (CHAT_MUTE || strlen(w_chat) >= HU_MAXMSGLEN) - return true; - - memmove(&w_chat[c_input + 1], &w_chat[c_input], strlen(w_chat) - c_input + 1); - w_chat[c_input] = c; - c_input++; - } else if (c == KEY_BACKSPACE) { if (CHAT_MUTE || c_input <= 0) @@ -2026,7 +2046,7 @@ void HU_Drawer(void) // IN-LEVEL MULTIPLAYER RANKINGS //====================================================================== -#define supercheckdef (!(players[tab[i].num].charflags & SF_NOSUPERSPRITES) && ((players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS1] || players[tab[i].num].mo->state >= &states[S_PLAY_SUPER_TRANS6])) || (players[tab[i].num].powers[pw_carry] == CR_NIGHTSMODE && skins[players[tab[i].num].skin].flags & SF_SUPER))) +#define supercheckdef (!(players[tab[i].num].charflags & SF_NOSUPERSPRITES) && ((players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS1] || players[tab[i].num].mo->state >= &states[S_PLAY_SUPER_TRANS6])) || (players[tab[i].num].powers[pw_carry] == CR_NIGHTSMODE && skins[players[tab[i].num].skin]->flags & SF_SUPER))) #define greycheckdef (players[tab[i].num].spectator || players[tab[i].num].playerstate == PST_DEAD || (G_IsSpecialStage(gamemap) && players[tab[i].num].exiting)) // diff --git a/src/i_time.c b/src/i_time.c index fae26abed..39854b242 100644 --- a/src/i_time.c +++ b/src/i_time.c @@ -30,12 +30,6 @@ static precise_t enterprecise, oldenterprecise; static fixed_t entertic, oldentertics; static double tictimer; -// A little more than the minimum sleep duration on Windows. -// May be incorrect for other platforms, but we don't currently have a way to -// query the scheduler granularity. SDL will do what's needed to make this as -// low as possible though. -#define MIN_SLEEP_DURATION_MS 2.1 - tic_t I_GetTime(void) { return g_time.time; @@ -88,38 +82,3 @@ void I_UpdateTime(fixed_t timescale) g_time.timefrac = FLOAT_TO_FIXED(fractional); } } - -void I_SleepDuration(precise_t duration) -{ - UINT64 precision = I_GetPrecisePrecision(); - INT32 sleepvalue = cv_sleep.value; - UINT64 delaygranularity; - precise_t cur; - precise_t dest; - - { - double gran = round(((double)(precision / 1000) * sleepvalue * MIN_SLEEP_DURATION_MS)); - delaygranularity = (UINT64)gran; - } - - cur = I_GetPreciseTime(); - dest = cur + duration; - - // the reason this is not dest > cur is because the precise counter may wrap - // two's complement arithmetic is our friend here, though! - // e.g. cur 0xFFFFFFFFFFFFFFFE = -2, dest 0x0000000000000001 = 1 - // 0x0000000000000001 - 0xFFFFFFFFFFFFFFFE = 3 - while ((INT64)(dest - cur) > 0) - { - // If our cv_sleep value exceeds the remaining sleep duration, use the - // hard sleep function. - if (sleepvalue > 0 && (dest - cur) > delaygranularity) - { - I_Sleep(sleepvalue); - } - - // Otherwise, this is a spinloop. - - cur = I_GetPreciseTime(); - } -} diff --git a/src/info.h b/src/info.h index a2d87dbdc..20a0a4b8a 100644 --- a/src/info.h +++ b/src/info.h @@ -5123,7 +5123,7 @@ typedef enum mobj_type MT_POLYANCHOR, MT_POLYSPAWN, - // Skybox objects + // Portal objects MT_SKYBOX, // Debris diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 9c723353b..0c916bcb9 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1346,6 +1346,17 @@ static int lib_pSetObjectMomZ(lua_State *L) return 0; } +static int lib_pIsLocalPlayer(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + //NOHUD + //INLEVEL + if (!player) + return LUA_ErrInvalid(L, "player_t"); + lua_pushboolean(L, P_IsLocalPlayer(player)); + return 1; +} + static int lib_pPlayJingle(lua_State *L) { player_t *player = NULL; @@ -1685,6 +1696,19 @@ static int lib_pHomingAttack(lua_State *L) return 1; } +static int lib_pResetCamera(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + camera_t *cam = *((camera_t **)luaL_checkudata(L, 2, META_CAMERA)); + + if (!player) + return LUA_ErrInvalid(L, "player_t"); + if (!cam) + return LUA_ErrInvalid(L, "camera_t"); + P_ResetCamera(player, cam); + return 0; +} + static int lib_pSuperReady(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -1701,11 +1725,12 @@ static int lib_pDoJump(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); boolean soundandstate = (boolean)lua_opttrueboolean(L, 2); + boolean allowflip = (boolean)lua_opttrueboolean(L, 3); NOHUD INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); - P_DoJump(player, soundandstate); + P_DoJump(player, soundandstate, allowflip); return 0; } @@ -1936,6 +1961,45 @@ static int lib_pMoveOrigin(lua_State *L) return 2; } +static int lib_pLineIsBlocking(lua_State *L) +{ + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + line_t *line = *((line_t **)luaL_checkudata(L, 2, META_LINE)); + NOHUD + INLEVEL + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + if (!line) + return LUA_ErrInvalid(L, "line_t"); + + // P_LineOpening in P_LineIsBlocking sets these variables. + // We want to keep their old values after so that whatever + // map collision code uses them doesn't get messed up. + fixed_t oldopentop = opentop; + fixed_t oldopenbottom = openbottom; + fixed_t oldopenrange = openrange; + fixed_t oldlowfloor = lowfloor; + fixed_t oldhighceiling = highceiling; + pslope_t *oldopentopslope = opentopslope; + pslope_t *oldopenbottomslope = openbottomslope; + ffloor_t *oldopenfloorrover = openfloorrover; + ffloor_t *oldopenceilingrover = openceilingrover; + + lua_pushboolean(L, P_LineIsBlocking(mo, line)); + + opentop = oldopentop; + openbottom = oldopenbottom; + openrange = oldopenrange; + lowfloor = oldlowfloor; + highceiling = oldhighceiling; + opentopslope = oldopentopslope; + openbottomslope = oldopenbottomslope; + openfloorrover = oldopenfloorrover; + openceilingrover = oldopenceilingrover; + + return 1; +} + static int lib_pSlideMove(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -3753,10 +3817,10 @@ static int lib_gAddPlayer(lua_State *L) if (!lua_isnoneornil(L, 2)) newplayer->skincolor = R_GetColorByName(luaL_checkstring(L, 2)); else - newplayer->skincolor = skins[skinnum].prefcolor; + newplayer->skincolor = skins[skinnum]->prefcolor; // Set the bot default name as the skin - strcpy(player_names[newplayernum], skins[skinnum].realname); + strcpy(player_names[newplayernum], skins[skinnum]->realname); // Read the bot name, if given if (!lua_isnoneornil(L, 3)) @@ -4277,6 +4341,7 @@ static luaL_Reg lib[] = { {"P_InQuicksand",lib_pInQuicksand}, {"P_InJumpFlipSector",lib_pInJumpFlipSector}, {"P_SetObjectMomZ",lib_pSetObjectMomZ}, + {"P_IsLocalPlayer",lib_pIsLocalPlayer}, {"P_PlayJingle",lib_pPlayJingle}, {"P_PlayJingleMusic",lib_pPlayJingleMusic}, {"P_RestoreMusic",lib_pRestoreMusic}, @@ -4303,6 +4368,7 @@ static luaL_Reg lib[] = { {"P_NukeEnemies",lib_pNukeEnemies}, {"P_Earthquake",lib_pEarthquake}, {"P_HomingAttack",lib_pHomingAttack}, + {"P_ResetCamera",lib_pResetCamera}, {"P_SuperReady",lib_pSuperReady}, {"P_DoJump",lib_pDoJump}, {"P_DoSpinDashDust",lib_pDoSpinDashDust}, @@ -4323,6 +4389,7 @@ static luaL_Reg lib[] = { {"P_TeleportMove",lib_pTeleportMove}, {"P_SetOrigin",lib_pSetOrigin}, {"P_MoveOrigin",lib_pMoveOrigin}, + {"P_LineIsBlocking",lib_pLineIsBlocking}, {"P_SlideMove",lib_pSlideMove}, {"P_BounceMove",lib_pBounceMove}, {"P_CheckSight", lib_pCheckSight}, diff --git a/src/lua_colorlib.c b/src/lua_colorlib.c index 1e6e82333..1ef21a41c 100644 --- a/src/lua_colorlib.c +++ b/src/lua_colorlib.c @@ -13,10 +13,33 @@ #include "doomdef.h" #include "fastcmp.h" #include "r_data.h" +#include "v_video.h" #include "lua_script.h" #include "lua_libs.h" +#define COLORLIB_USE_LOOKUP + +#ifdef COLORLIB_USE_LOOKUP + static colorlookup_t colormix_lut; + #define GetNearestColor(r, g, b) GetColorLUT(&colormix_lut, r, g, b) +#else + #define GetNearestColor(r, g, b) NearestPaletteColor(r, g, b, pMasterPalette) +#endif + +//////////////// +// Color library +//////////////// + +static int lib_colorPaletteToRgb(lua_State *L) +{ + RGBA_t color = V_GetMasterColor((UINT8)luaL_checkinteger(L, 1)); + lua_pushinteger(L, color.s.red); + lua_pushinteger(L, color.s.green); + lua_pushinteger(L, color.s.blue); + return 3; +} + #define IS_HEX_CHAR(x) ((x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F')) #define ARE_HEX_CHARS(str, i) IS_HEX_CHAR(str[i]) && IS_HEX_CHAR(str[i + 1]) @@ -32,6 +55,13 @@ static UINT32 hex2int(char x) return 0; } +static UINT8 GetHTMLColorLength(const char *str) +{ + if (str[0] == '#') + str++; + return strlen(str) >= 8 ? 4 : 3; +} + static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc) { const char *hex = str; @@ -77,6 +107,302 @@ static UINT8 ParseHTMLColor(const char *str, UINT8 *rgba, size_t numc) return 0; } +static UINT8 GetPackedRGBA(UINT32 colors, UINT8 *rgba) +{ + if (colors > 0xFFFFFF) + { + rgba[0] = (colors >> 24) & 0xFF; + rgba[1] = (colors >> 16) & 0xFF; + rgba[2] = (colors >> 8) & 0xFF; + rgba[3] = colors & 0xFF; + return 4; + } + else + { + rgba[0] = (colors >> 16) & 0xFF; + rgba[1] = (colors >> 8) & 0xFF; + rgba[2] = colors & 0xFF; + rgba[3] = 0xFF; + return 3; + } +} + +static UINT8 GetArgsRGBA(lua_State *L, UINT8 index, INT32 *r, INT32 *g, INT32 *b, INT32 *a) +{ + UINT8 rgba[4] = { 0, 0, 0, 255 }; + UINT8 num = 0; + + if (lua_gettop(L) == 1 && lua_type(L, index) == LUA_TNUMBER) + { + num = GetPackedRGBA(luaL_checkinteger(L, 1), rgba); + + *r = rgba[0]; + *g = rgba[1]; + *b = rgba[2]; + if (a) + *a = rgba[3]; + } + else if (lua_type(L, index) == LUA_TSTRING) + { + const char *str = lua_tostring(L, index); + UINT8 parsed = ParseHTMLColor(str, rgba, GetHTMLColorLength(str)); + if (!parsed) + luaL_error(L, "Malformed HTML color '%s'", str); + + num = parsed == 8 ? 4 : 3; + + *r = rgba[0]; + *g = rgba[1]; + *b = rgba[2]; + if (a) + *a = rgba[3]; + } + else + { + INT32 temp; + +#define CHECKINT(i) luaL_checkinteger(L, i) +#define GETCOLOR(c, i, desc) { \ + temp = CHECKINT(i); \ + if (temp < 0 || temp > 255) \ + luaL_error(L, desc " channel %d out of range (0 - 255)", temp); \ + c = temp; \ + num++; \ + } + + GETCOLOR(*r, index + 0, "red color"); + GETCOLOR(*g, index + 1, "green color"); + GETCOLOR(*b, index + 2, "blue color"); +#undef CHECKINT +#define CHECKINT(i) luaL_optinteger(L, i, 255) + if (a) + GETCOLOR(*a, index + 3, "alpha"); +#undef CHECKINT +#undef GETCOLOR + + num = 3 + (lua_type(L, index + 3) == LUA_TNUMBER); + } + + return num; +} + +static int lib_colorRgbToPalette(lua_State *L) +{ + INT32 r, g, b; + GetArgsRGBA(L, 1, &r, &g, &b, NULL); + +#ifdef COLORLIB_USE_LOOKUP + InitColorLUT(&colormix_lut, pMasterPalette, false); +#endif + + lua_pushinteger(L, GetNearestColor(r, g, b)); + return 1; +} + +#define SCALE_UINT8_TO_FIXED(val) FixedDiv(val * FRACUNIT, 255 * FRACUNIT) +#define SCALE_FIXED_TO_UINT8(val) FixedRound(FixedMul(val, 255 * FRACUNIT)) / FRACUNIT + +static fixed_t hue2rgb(fixed_t p, fixed_t q, fixed_t t) +{ + if (t < 0) + t += FRACUNIT; + if (t > FRACUNIT) + t -= FRACUNIT; + + fixed_t out; + + if (t < FRACUNIT / 6) + out = p + FixedMul(FixedMul(q - p, 6 * FRACUNIT), t); + else if (t < FRACUNIT / 2) + out = q; + else if (t < 2 * FRACUNIT / 3) + out = p + FixedMul(FixedMul(q - p, 2 * FRACUNIT / 3 - t), 6 * FRACUNIT); + else + out = p; + + return out; +} + +static int lib_colorHslToRgb(lua_State *L) +{ + fixed_t h, s, l; + +#define GETHSL(c, i, desc) \ + c = luaL_checkinteger(L, i); \ + if (c < 0 || c > 255) \ + luaL_error(L, desc " %d out of range (0 - 255)", c) + + GETHSL(h, 1, "hue"); + GETHSL(s, 2, "saturation"); + GETHSL(l, 3, "value"); +#undef GETHSL + + if (!s) + { + lua_pushinteger(L, l); + lua_pushinteger(L, l); + lua_pushinteger(L, l); + } + else + { + h = SCALE_UINT8_TO_FIXED(h); + s = SCALE_UINT8_TO_FIXED(s); + l = SCALE_UINT8_TO_FIXED(l); + + fixed_t q, p; + + if (l < FRACUNIT/2) + q = FixedMul(l, FRACUNIT + s); + else + q = l + s - FixedMul(l, s); + + p = l * 2 - q; + + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h + FRACUNIT/3))); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h))); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(hue2rgb(p, q, h - FRACUNIT/3))); + } + + return 3; +} + +static int lib_colorRgbToHsl(lua_State *L) +{ + INT32 ir, ig, ib; + GetArgsRGBA(L, 1, &ir, &ig, &ib, NULL); + + fixed_t r = SCALE_UINT8_TO_FIXED(ir); + fixed_t g = SCALE_UINT8_TO_FIXED(ig); + fixed_t b = SCALE_UINT8_TO_FIXED(ib); + + fixed_t cmin = min(min(r, g), b); + fixed_t cmax = max(max(r, g), b); + + fixed_t h, s, l = (cmax + cmin) / 2; + fixed_t delta = cmax - cmin; + + if (!delta) + h = s = 0; + else + { + if (l > FRACUNIT / 2) + s = FixedDiv(delta, (FRACUNIT * 2) - cmax - cmin); + else + s = FixedDiv(delta, cmax + cmin); + + if (r > g && r > b) + { + h = FixedDiv(g - b, delta); + + if (g < b) + h += FRACUNIT * 6; + } + else + { + h = FixedDiv(r - g, delta); + + if (g > b) + h += FRACUNIT * 2; + else + h += FRACUNIT * 4; + } + + h = FixedDiv(h, FRACUNIT * 6); + } + + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(h)); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(s)); + lua_pushinteger(L, SCALE_FIXED_TO_UINT8(l)); + + return 3; +} + +static int lib_colorHexToRgb(lua_State *L) +{ + UINT8 rgba[4] = { 0, 0, 0, 255 }; + + const char *str = luaL_checkstring(L, 1); + UINT8 parsed = ParseHTMLColor(str, rgba, 4), num = 3; + if (!parsed) + luaL_error(L, "Malformed HTML color '%s'", str); + else if (parsed == 8) + num++; + + lua_pushinteger(L, rgba[0]); + lua_pushinteger(L, rgba[1]); + lua_pushinteger(L, rgba[2]); + if (num == 4) + lua_pushinteger(L, rgba[3]); + + return num; +} + +static int lib_colorRgbToHex(lua_State *L) +{ + INT32 r, g, b, a; + UINT8 num = GetArgsRGBA(L, 1, &r, &g, &b, &a); + + char buffer[10]; + if (num >= 4) + snprintf(buffer, sizeof buffer, "#%02X%02X%02X%02X", r, g, b, a); + else + snprintf(buffer, sizeof buffer, "#%02X%02X%02X", r, g, b); + + lua_pushstring(L, buffer); + return 1; +} + +static int lib_colorPackRgb(lua_State *L) +{ + INT32 r, g, b; + + GetArgsRGBA(L, 1, &r, &g, &b, NULL); + + UINT32 packed = ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF); + + lua_pushinteger(L, packed); + return 1; +} + +static int lib_colorPackRgba(lua_State *L) +{ + INT32 r, g, b, a; + + GetArgsRGBA(L, 1, &r, &g, &b, &a); + + UINT32 packed = ((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF); + + lua_pushinteger(L, packed); + return 1; +} + +static int lib_colorUnpackRgb(lua_State *L) +{ + UINT8 rgba[4]; + + UINT8 num = GetPackedRGBA(lua_tointeger(L, 1), rgba); + + for (UINT8 i = 0; i < num; i++) + { + lua_pushinteger(L, rgba[i]); + } + + return num; +} + +static luaL_Reg color_lib[] = { + {"paletteToRgb", lib_colorPaletteToRgb}, + {"rgbToPalette", lib_colorRgbToPalette}, + {"hslToRgb", lib_colorHslToRgb}, + {"rgbToHsl", lib_colorRgbToHsl}, + {"hexToRgb", lib_colorHexToRgb}, + {"rgbToHex", lib_colorRgbToHex}, + {"packRgb", lib_colorPackRgb}, + {"packRgba", lib_colorPackRgba}, + {"unpackRgb", lib_colorUnpackRgb}, + {NULL, NULL} +}; + ///////////////////////// // extracolormap userdata ///////////////////////// @@ -180,21 +506,7 @@ static void GetExtraColormapRGBA(lua_State *L, UINT8 *rgba, int arg) } else { - UINT32 colors = lua_tointeger(L, arg); - if (colors > 0xFFFFFF) - { - rgba[0] = (colors >> 24) & 0xFF; - rgba[1] = (colors >> 16) & 0xFF; - rgba[2] = (colors >> 8) & 0xFF; - rgba[3] = colors & 0xFF; - } - else - { - rgba[0] = (colors >> 16) & 0xFF; - rgba[1] = (colors >> 8) & 0xFF; - rgba[2] = colors & 0xFF; - rgba[3] = 0xFF; - } + GetPackedRGBA(lua_tointeger(L, arg), rgba); } } @@ -328,5 +640,7 @@ int LUA_ColorLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L, 1); + luaL_register(L, "color", color_lib); + return 0; } diff --git a/src/lua_hook.h b/src/lua_hook.h index cb0e30d87..026e5c4aa 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -126,7 +126,9 @@ int LUA_HookPlayer(player_t *, int hook); int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); int LUA_HookKey(event_t *event, int hook); // Hooks for key events +void LUA_HookPreThinkFrame(void); void LUA_HookThinkFrame(void); +void LUA_HookPostThinkFrame(void); int LUA_HookMobjLineCollide(mobj_t *, line_t *); int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher); int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 0fc25ee6c..529c189ff 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -76,12 +76,12 @@ static boolean mobj_hook_available(int hook_type, mobjtype_t mobj_type) ); } -static int hook_in_list +static unsigned hook_in_list ( const char * const name, const char * const * const list ){ - int type; + unsigned type; for (type = 0; list[type] != NULL; ++type) { @@ -200,7 +200,7 @@ static void add_hook_ref(lua_State *L, int idx) static int lib_addHook(lua_State *L) { const char * name; - int type; + unsigned type; if (!lua_lumploading) return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); @@ -671,10 +671,8 @@ void LUA_HookHUD(int hook_type, huddrawlist_h list) SPECIALIZED HOOKS ========================================================================= */ -void LUA_HookThinkFrame(void) +static void hook_think_frame(int type) { - const int type = HOOK(ThinkFrame); - // variables used by perf stats int hook_index = 0; precise_t time_taken = 0; @@ -692,7 +690,7 @@ void LUA_HookThinkFrame(void) { get_hook(&hook, map->ids, k); - if (cv_perfstats.value == 3) + if (cv_perfstats.value >= 3) { lua_pushvalue(gL, -1);/* need the function again */ time_taken = I_GetPreciseTime(); @@ -700,12 +698,18 @@ void LUA_HookThinkFrame(void) call_single_hook(&hook); - if (cv_perfstats.value == 3) + if (cv_perfstats.value >= 3) { lua_Debug ar; time_taken = I_GetPreciseTime() - time_taken; lua_getinfo(gL, ">S", &ar); - PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + if (type == 4) // sorry for magic numbers + PS_SetPreThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + else if (type == 5) + PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + else if (type == 6) + PS_SetPostThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + hook_index++; } } @@ -714,6 +718,21 @@ void LUA_HookThinkFrame(void) } } +void LUA_HookPreThinkFrame(void) +{ + hook_think_frame(HOOK(PreThinkFrame)); +} + +void LUA_HookThinkFrame(void) +{ + hook_think_frame(HOOK(ThinkFrame)); +} + +void LUA_HookPostThinkFrame(void) +{ + hook_think_frame(HOOK(PostThinkFrame)); +} + int LUA_HookMobjLineCollide(mobj_t *mobj, line_t *line) { Hook_State hook; diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 2e6721a3a..19f8b74c7 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -169,6 +169,7 @@ enum cameraf { camera_x, camera_y, camera_z, + camera_reset, camera_angle, camera_subsector, camera_floorz, @@ -187,6 +188,7 @@ static const char *const camera_opt[] = { "x", "y", "z", + "reset", "angle", "subsector", "floorz", @@ -341,6 +343,9 @@ static int camera_get(lua_State *L) case camera_z: lua_pushinteger(L, cam->z); break; + case camera_reset: + lua_pushboolean(L, cam->reset); + break; case camera_angle: lua_pushinteger(L, cam->angle); break; @@ -387,6 +392,9 @@ static int camera_set(lua_State *L) case camera_x: case camera_y: return luaL_error(L, LUA_QL("camera_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("P_TryCameraMove") " or " LUA_QL("P_TeleportCameraMove") " instead.", camera_opt[field]); + case camera_reset: + cam->reset = luaL_checkboolean(L, 3); + break; case camera_chase: { INT32 chase = luaL_checkboolean(L, 3); if (cam == &camera) @@ -557,7 +565,7 @@ static int libd_getSprite2Patch(lua_State *L) { const char *name = luaL_checkstring(L, 1); for (i = 0; i < numskins; i++) - if (fastcmp(skins[i].name, name)) + if (fastcmp(skins[i]->name, name)) break; if (i >= numskins) return 0; @@ -599,9 +607,9 @@ static int libd_getSprite2Patch(lua_State *L) if (super) j |= FF_SPR2SUPER; - j = P_GetSkinSprite2(&skins[i], j, NULL); // feed skin and current sprite2 through to change sprite2 used if necessary + j = P_GetSkinSprite2(skins[i], j, NULL); // feed skin and current sprite2 through to change sprite2 used if necessary - sprdef = &skins[i].sprites[j]; + sprdef = &skins[i]->sprites[j]; // set frame number frame = luaL_optinteger(L, 2, 0); @@ -629,7 +637,7 @@ static int libd_getSprite2Patch(lua_State *L) INT32 rot = R_GetRollAngle(rollangle); if (rot) { - patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<flip & (1<sprinfo[j], rot); LUA_PushUserdata(L, rotsprite, META_PATCH); lua_pushboolean(L, false); lua_pushboolean(L, true); diff --git a/src/lua_infolib.c b/src/lua_infolib.c index ed78811ce..03888f32e 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -30,9 +30,6 @@ #include "lua_hud.h" // hud_running errors #include "lua_hook.h" // hook_cmd_running errors -extern CV_PossibleValue_t Color_cons_t[]; -extern UINT8 skincolor_modified[]; - boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor); state_t *astate; diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 603fc0d8f..5b80d4d38 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -35,10 +35,14 @@ enum sector_e { sector_floorpic, sector_floorxoffset, sector_flooryoffset, + sector_floorxscale, + sector_flooryscale, sector_floorangle, sector_ceilingpic, sector_ceilingxoffset, sector_ceilingyoffset, + sector_ceilingxscale, + sector_ceilingyscale, sector_ceilingangle, sector_lightlevel, sector_floorlightlevel, @@ -74,10 +78,14 @@ static const char *const sector_opt[] = { "floorpic", "floorxoffset", "flooryoffset", + "floorxscale", + "flooryscale", "floorangle", "ceilingpic", "ceilingxoffset", "ceilingyoffset", + "ceilingxscale", + "ceilingyscale", "ceilingangle", "lightlevel", "floorlightlevel", @@ -188,8 +196,16 @@ enum side_e { side_offsety_top, side_offsetx_mid, side_offsety_mid, + side_offsetx_bottom, side_offsetx_bot, + side_offsety_bottom, side_offsety_bot, + side_scalex_top, + side_scaley_top, + side_scalex_mid, + side_scaley_mid, + side_scalex_bottom, + side_scaley_bottom, side_toptexture, side_bottomtexture, side_midtexture, @@ -208,8 +224,16 @@ static const char *const side_opt[] = { "offsety_top", "offsetx_mid", "offsety_mid", + "offsetx_bottom", "offsetx_bot", + "offsety_bottom", "offsety_bot", + "scalex_top", + "scaley_top", + "scalex_mid", + "scaley_mid", + "scalex_bottom", + "scaley_bottom", "toptexture", "bottomtexture", "midtexture", @@ -249,8 +273,16 @@ enum ffloor_e { ffloor_topheight, ffloor_toppic, ffloor_toplightlevel, + ffloor_topxoffs, + ffloor_topyoffs, + ffloor_topxscale, + ffloor_topyscale, ffloor_bottomheight, ffloor_bottompic, + ffloor_bottomxoffs, + ffloor_bottomyoffs, + ffloor_bottomxscale, + ffloor_bottomyscale, ffloor_tslope, ffloor_bslope, ffloor_sector, @@ -275,8 +307,16 @@ static const char *const ffloor_opt[] = { "topheight", "toppic", "toplightlevel", + "topxoffs", + "topyoffs", + "topxscale", + "topyscale", "bottomheight", "bottompic", + "bottomxoffs", + "bottomyoffs", + "bottomxscale", + "bottomyscale", "t_slope", "b_slope", "sector", // secnum pushed as control sector userdata @@ -656,20 +696,20 @@ static int sector_get(lua_State *L) return 1; } case sector_floorxoffset: - { lua_pushfixed(L, sector->floorxoffset); return 1; - } case sector_flooryoffset: - { lua_pushfixed(L, sector->flooryoffset); return 1; - } + case sector_floorxscale: + lua_pushfixed(L, sector->floorxscale); + return 1; + case sector_flooryscale: + lua_pushfixed(L, sector->flooryscale); + return 1; case sector_floorangle: - { lua_pushangle(L, sector->floorangle); return 1; - } case sector_ceilingpic: // ceilingpic { levelflat_t *levelflat = &levelflats[sector->ceilingpic]; @@ -680,20 +720,20 @@ static int sector_get(lua_State *L) return 1; } case sector_ceilingxoffset: - { lua_pushfixed(L, sector->ceilingxoffset); return 1; - } case sector_ceilingyoffset: - { lua_pushfixed(L, sector->ceilingyoffset); return 1; - } + case sector_ceilingxscale: + lua_pushfixed(L, sector->ceilingxscale); + return 1; + case sector_ceilingyscale: + lua_pushfixed(L, sector->ceilingyscale); + return 1; case sector_ceilingangle: - { lua_pushangle(L, sector->ceilingangle); return 1; - } case sector_lightlevel: lua_pushinteger(L, sector->lightlevel); return 1; @@ -845,6 +885,12 @@ static int sector_set(lua_State *L) case sector_flooryoffset: sector->flooryoffset = luaL_checkfixed(L, 3); break; + case sector_floorxscale: + sector->floorxscale = luaL_checkfixed(L, 3); + break; + case sector_flooryscale: + sector->flooryscale = luaL_checkfixed(L, 3); + break; case sector_floorangle: sector->floorangle = luaL_checkangle(L, 3); break; @@ -857,6 +903,12 @@ static int sector_set(lua_State *L) case sector_ceilingyoffset: sector->ceilingyoffset = luaL_checkfixed(L, 3); break; + case sector_ceilingxscale: + sector->ceilingxscale = luaL_checkfixed(L, 3); + break; + case sector_ceilingyscale: + sector->ceilingyscale = luaL_checkfixed(L, 3); + break; case sector_ceilingangle: sector->ceilingangle = luaL_checkangle(L, 3); break; @@ -1143,7 +1195,7 @@ static int line_num(lua_State *L) static int sidenum_get(lua_State *L) { - UINT16 *sidenum = *((UINT16 **)luaL_checkudata(L, 1, META_SIDENUM)); + UINT32 *sidenum = *((UINT32 **)luaL_checkudata(L, 1, META_SIDENUM)); int i; lua_settop(L, 2); if (!lua_isnumber(L, 2)) @@ -1210,11 +1262,31 @@ static int side_get(lua_State *L) case side_offsety_mid: lua_pushfixed(L, side->offsety_mid); return 1; + case side_offsetx_bottom: case side_offsetx_bot: - lua_pushfixed(L, side->offsetx_bot); + lua_pushfixed(L, side->offsetx_bottom); return 1; + case side_offsety_bottom: case side_offsety_bot: - lua_pushfixed(L, side->offsety_bot); + lua_pushfixed(L, side->offsety_bottom); + return 1; + case side_scalex_top: + lua_pushfixed(L, side->scalex_top); + return 1; + case side_scaley_top: + lua_pushfixed(L, side->scaley_top); + return 1; + case side_scalex_mid: + lua_pushfixed(L, side->scalex_mid); + return 1; + case side_scaley_mid: + lua_pushfixed(L, side->scaley_mid); + return 1; + case side_scalex_bottom: + lua_pushfixed(L, side->scalex_bottom); + return 1; + case side_scaley_bottom: + lua_pushfixed(L, side->scaley_bottom); return 1; case side_toptexture: lua_pushinteger(L, side->toptexture); @@ -1302,10 +1374,30 @@ static int side_set(lua_State *L) side->offsety_mid = luaL_checkfixed(L, 3); break; case side_offsetx_bot: - side->offsetx_bot = luaL_checkfixed(L, 3); + case side_offsetx_bottom: + side->offsetx_bottom = luaL_checkfixed(L, 3); break; case side_offsety_bot: - side->offsety_bot = luaL_checkfixed(L, 3); + case side_offsety_bottom: + side->offsety_bottom = luaL_checkfixed(L, 3); + break; + case side_scalex_top: + side->scalex_top = luaL_checkfixed(L, 3); + break; + case side_scaley_top: + side->scaley_top = luaL_checkfixed(L, 3); + break; + case side_scalex_mid: + side->scalex_mid = luaL_checkfixed(L, 3); + break; + case side_scaley_mid: + side->scaley_mid = luaL_checkfixed(L, 3); + break; + case side_scalex_bottom: + side->scalex_bottom = luaL_checkfixed(L, 3); + break; + case side_scaley_bottom: + side->scaley_bottom = luaL_checkfixed(L, 3); break; case side_toptexture: side->toptexture = luaL_checkinteger(L, 3); @@ -2122,6 +2214,18 @@ static int ffloor_get(lua_State *L) case ffloor_toplightlevel: lua_pushinteger(L, *ffloor->toplightlevel); return 1; + case ffloor_topxoffs: + lua_pushfixed(L, *ffloor->topxoffs); + return 1; + case ffloor_topyoffs: + lua_pushfixed(L, *ffloor->topyoffs); + return 1; + case ffloor_topxscale: + lua_pushfixed(L, *ffloor->topxscale); + return 1; + case ffloor_topyscale: + lua_pushfixed(L, *ffloor->topyscale); + return 1; case ffloor_bottomheight: lua_pushfixed(L, *ffloor->bottomheight); return 1; @@ -2133,6 +2237,18 @@ static int ffloor_get(lua_State *L) lua_pushlstring(L, levelflat->name, i); return 1; } + case ffloor_bottomxoffs: + lua_pushfixed(L, *ffloor->bottomxoffs); + return 1; + case ffloor_bottomyoffs: + lua_pushfixed(L, *ffloor->bottomyoffs); + return 1; + case ffloor_bottomxscale: + lua_pushfixed(L, *ffloor->bottomxscale); + return 1; + case ffloor_bottomyscale: + lua_pushfixed(L, *ffloor->bottomyscale); + return 1; case ffloor_tslope: LUA_PushUserdata(L, *ffloor->t_slope, META_SLOPE); return 1; @@ -2317,6 +2433,18 @@ static int ffloor_set(lua_State *L) case ffloor_toplightlevel: *ffloor->toplightlevel = (INT16)luaL_checkinteger(L, 3); break; + case ffloor_topxoffs: + *ffloor->topxoffs = luaL_checkfixed(L, 3); + break; + case ffloor_topyoffs: + *ffloor->topyoffs = luaL_checkfixed(L, 3); + break; + case ffloor_topxscale: + *ffloor->topxscale = luaL_checkfixed(L, 3); + break; + case ffloor_topyscale: + *ffloor->topyscale = luaL_checkfixed(L, 3); + break; case ffloor_bottomheight: { // bottomheight boolean flag; fixed_t lastpos = *ffloor->bottomheight; @@ -2335,6 +2463,18 @@ static int ffloor_set(lua_State *L) case ffloor_bottompic: *ffloor->bottompic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3)); break; + case ffloor_bottomxoffs: + *ffloor->bottomxoffs = luaL_checkfixed(L, 3); + break; + case ffloor_bottomyoffs: + *ffloor->bottomyoffs = luaL_checkfixed(L, 3); + break; + case ffloor_bottomxscale: + *ffloor->bottomxscale = luaL_checkfixed(L, 3); + break; + case ffloor_bottomyscale: + *ffloor->bottomyscale = luaL_checkfixed(L, 3); + break; case ffloor_fofflags: { ffloortype_e oldflags = ffloor->fofflags; // store FOF's old flags ffloor->fofflags = luaL_checkinteger(L, 3); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 19f30b70e..b5c6c0329 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -641,10 +641,7 @@ static int mobj_set(lua_State *L) mo->tics = luaL_checkinteger(L, 3); break; case mobj_state: // set state by enum - if (mo->player) - P_SetPlayerMobjState(mo, luaL_checkinteger(L, 3)); - else - P_SetMobjState(mo, luaL_checkinteger(L, 3)); + P_SetMobjState(mo, luaL_checkinteger(L, 3)); break; case mobj_flags: // special handling for MF_NOBLOCKMAP and MF_NOSECTOR { @@ -682,10 +679,10 @@ static int mobj_set(lua_State *L) strlcpy(skin, luaL_checkstring(L, 3), sizeof skin); strlwr(skin); // all skin names are lowercase for (i = 0; i < numskins; i++) - if (fastcmp(skins[i].name, skin)) + if (fastcmp(skins[i]->name, skin)) { if (!mo->player || R_SkinUsable(mo->player-players, i)) - mo->skin = &skins[i]; + mo->skin = skins[i]; return 0; } return luaL_error(L, "mobj.skin '%s' not found!", skin); diff --git a/src/lua_script.c b/src/lua_script.c index b0b6eeec7..ecd85f11b 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -38,6 +38,8 @@ #include "doomstat.h" #include "g_state.h" +#include "hu_stuff.h" + lua_State *gL = NULL; // List of internal libraries to load from SRB2 @@ -432,6 +434,9 @@ int LUA_PushGlobals(lua_State *L, const char *word) return 0; LUA_PushUserdata(L, &camera2, META_CAMERA); return 1; + } else if (fastcmp(word, "chatactive")) { + lua_pushboolean(L, chat_on); + return 1; } return 0; } @@ -604,64 +609,114 @@ void LUA_ClearExtVars(void) INT32 lua_lumploading = 0; // Load a script from a MYFILE -static inline void LUA_LoadFile(MYFILE *f, char *name, boolean noresults) +static inline boolean LUA_LoadFile(MYFILE *f, char *name) { int errorhandlerindex; + boolean success; if (!name) name = wadfiles[f->wad]->filename; + CONS_Printf("Loading Lua script from %s\n", name); + if (!gL) // Lua needs to be initialized LUA_ClearState(); + lua_pushinteger(gL, f->wad); lua_setfield(gL, LUA_REGISTRYINDEX, "WAD"); + lua_pushcfunction(gL, LUA_GetErrorMessage); + errorhandlerindex = lua_gettop(gL); + + success = !luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)); + + if (!success) { + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); + lua_pop(gL,1); + } + + lua_gc(gL, LUA_GCCOLLECT, 0); + lua_remove(gL, errorhandlerindex); + + return success; +} + +// Runs a script loaded by LUA_LoadFile. +static inline void LUA_DoFile(boolean noresults) +{ + int errorhandlerindex; + + if (!gL) // LUA_LoadFile should've allocated gL for us! + return; + lua_lumploading++; // turn on loading flag lua_pushcfunction(gL, LUA_GetErrorMessage); - errorhandlerindex = lua_gettop(gL); - if (luaL_loadbuffer(gL, f->data, f->size, va("@%s",name)) || lua_pcall(gL, 0, noresults ? 0 : LUA_MULTRET, lua_gettop(gL) - 1)) { + lua_insert(gL, -2); // move the function we're calling to the top. + errorhandlerindex = lua_gettop(gL) - 1; + + if (lua_pcall(gL, 0, noresults ? 0 : LUA_MULTRET, lua_gettop(gL) - 1)) { CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); lua_pop(gL,1); } + lua_gc(gL, LUA_GCCOLLECT, 0); lua_remove(gL, errorhandlerindex); lua_lumploading--; // turn off again } -// Load a script from a lump -void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults) +static inline MYFILE *LUA_GetFile(UINT16 wad, UINT16 lump, char **name) { - MYFILE f; - char *name; + MYFILE *f = Z_Malloc(sizeof(MYFILE), PU_LUA, NULL); size_t len; - f.wad = wad; - f.size = W_LumpLengthPwad(wad, lump); - f.data = Z_Malloc(f.size, PU_LUA, NULL); - W_ReadLumpPwad(wad, lump, f.data); - f.curpos = f.data; + + f->wad = wad; + f->size = W_LumpLengthPwad(wad, lump); + f->data = Z_Malloc(f->size, PU_LUA, NULL); + W_ReadLumpPwad(wad, lump, f->data); + f->curpos = f->data; len = strlen(wadfiles[wad]->filename); // length of file name if (wadfiles[wad]->type == RET_LUA) { - name = malloc(len+1); - strcpy(name, wadfiles[wad]->filename); + *name = malloc(len+1); + strcpy(*name, wadfiles[wad]->filename); } else // If it's not a .lua file, copy the lump name in too. { lumpinfo_t *lump_p = &wadfiles[wad]->lumpinfo[lump]; len += 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name - name = malloc(len+1); - sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->fullname); - name[len] = '\0'; + *name = malloc(len+1); + sprintf(*name, "%s|%s", wadfiles[wad]->filename, lump_p->fullname); + (*name)[len] = '\0'; // annoying that index takes priority over dereference, but w/e } - LUA_LoadFile(&f, name, noresults); // actually load file! + return f; +} + +// Load a script from a lump +boolean LUA_LoadLump(UINT16 wad, UINT16 lump) +{ + char *name = NULL; + MYFILE *f = LUA_GetFile(wad, lump, &name); + boolean success = LUA_LoadFile(f, name); // actually load file! free(name); - Z_Free(f.data); + + Z_Free(f->data); + Z_Free(f); + + return success; +} + +void LUA_DoLump(UINT16 wad, UINT16 lump, boolean noresults) +{ + boolean success = LUA_LoadLump(wad, lump); + + if (success) + LUA_DoFile(noresults); // run it } #ifdef LUA_ALLOW_BYTECODE @@ -1346,7 +1401,7 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) { skin_t *skin = *((skin_t **)lua_touserdata(gL, myindex)); WRITEUINT8(save_p, ARCH_SKIN); - WRITEUINT8(save_p, skin - skins); // UINT8 because MAXSKINS is only 32 + WRITEUINT8(save_p, skin->skinnum); // UINT8 because MAXSKINS must be <= 256 break; } default: @@ -1596,7 +1651,7 @@ static UINT8 UnArchiveValue(int TABLESINDEX) LUA_PushUserdata(gL, READUINT16(save_p) == 1 ? &mouse : &mouse2, META_MOUSE); break; case ARCH_SKIN: - LUA_PushUserdata(gL, &skins[READUINT8(save_p)], META_SKIN); + LUA_PushUserdata(gL, skins[READUINT8(save_p)], META_SKIN); break; case ARCH_TEND: return 1; diff --git a/src/lua_script.h b/src/lua_script.h index 53d848f8e..45d2a37ff 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -45,7 +45,8 @@ extern INT32 lua_lumploading; // is LUA_LoadLump being called? int LUA_GetErrorMessage(lua_State *L); int LUA_Call(lua_State *L, int nargs, int nresults, int errorhandlerindex); -void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults); +boolean LUA_LoadLump(UINT16 wad, UINT16 lump); +void LUA_DoLump(UINT16 wad, UINT16 lump, boolean noresults); #ifdef LUA_ALLOW_BYTECODE void LUA_DumpFile(const char *filename); #endif diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 3debd3746..e183d8dda 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -54,7 +54,8 @@ enum skin { skin_contspeed, skin_contangle, skin_soundsid, - skin_sprites + skin_sprites, + skin_natkcolor }; static const char *const skin_opt[] = { @@ -94,6 +95,7 @@ static const char *const skin_opt[] = { "contangle", "soundsid", "sprites", + "natkcolor", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("skin_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", skin_opt[field]) @@ -218,6 +220,9 @@ static int skin_get(lua_State *L) case skin_sprites: LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES); break; + case skin_natkcolor: + lua_pushinteger(L, skin->natkcolor); + break; } return 1; } @@ -234,7 +239,7 @@ static int skin_num(lua_State *L) // skins are always valid, only added, never removed I_Assert(skin != NULL); - lua_pushinteger(L, skin-skins); + lua_pushinteger(L, skin->skinnum); return 1; } @@ -253,14 +258,14 @@ static int lib_iterateSkins(lua_State *L) lua_remove(L, 1); // state is unused. if (!lua_isnil(L, 1)) - i = (INT32)(*((skin_t **)luaL_checkudata(L, 1, META_SKIN)) - skins) + 1; + i = (INT32)((*((skin_t **)luaL_checkudata(L, 1, META_SKIN)))->skinnum) + 1; else i = 0; // skins are always valid, only added, never removed if (i < numskins) { - LUA_PushUserdata(L, &skins[i], META_SKIN); + LUA_PushUserdata(L, skins[i], META_SKIN); return 1; } @@ -280,7 +285,7 @@ static int lib_getSkin(lua_State *L) return luaL_error(L, "skins[] index %d out of range (0 - %d)", i, MAXSKINS-1); if (i >= numskins) return 0; - LUA_PushUserdata(L, &skins[i], META_SKIN); + LUA_PushUserdata(L, skins[i], META_SKIN); return 1; } @@ -295,9 +300,9 @@ static int lib_getSkin(lua_State *L) // find skin by name for (i = 0; i < numskins; i++) - if (fastcmp(skins[i].name, field)) + if (fastcmp(skins[i]->name, field)) { - LUA_PushUserdata(L, &skins[i], META_SKIN); + LUA_PushUserdata(L, skins[i], META_SKIN); return 1; } diff --git a/src/lua_taglib.c b/src/lua_taglib.c index 9e73a050c..a040a7efc 100644 --- a/src/lua_taglib.c +++ b/src/lua_taglib.c @@ -228,7 +228,7 @@ static int taglist_get(lua_State *L) } else { - lua_getmetatable(L, 1); + lua_getglobal(L, "taglist"); lua_replace(L, 1); lua_rawget(L, 1); return 1; diff --git a/src/m_cheat.c b/src/m_cheat.c index 2bcf43ad1..e61db2c2e 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -1019,7 +1019,7 @@ static void OP_CycleThings(INT32 amt) if (players[0].mo->eflags & MFE_VERTICALFLIP) // correct z when flipped players[0].mo->z += players[0].mo->height - FixedMul(mobjinfo[op_currentthing].height, players[0].mo->scale); players[0].mo->height = FixedMul(mobjinfo[op_currentthing].height, players[0].mo->scale); - P_SetPlayerMobjState(players[0].mo, S_OBJPLACE_DUMMY); + P_SetMobjState(players[0].mo, S_OBJPLACE_DUMMY); op_currentdoomednum = mobjinfo[op_currentthing].doomednum; } @@ -1528,7 +1528,7 @@ void Command_ObjectPlace_f(void) else OP_CycleThings(0); // sets all necessary height values without cycling op_currentthing - P_SetPlayerMobjState(players[0].mo, S_OBJPLACE_DUMMY); + P_SetMobjState(players[0].mo, S_OBJPLACE_DUMMY); } // Or are we leaving it instead? else @@ -1542,7 +1542,7 @@ void Command_ObjectPlace_f(void) // If still in dummy state, get out of it. if (players[0].mo->state == &states[S_OBJPLACE_DUMMY]) - P_SetPlayerMobjState(players[0].mo, op_oldstate); + P_SetMobjState(players[0].mo, op_oldstate); // Reset everything back to how it was before we entered objectplace. P_UnsetThingPosition(players[0].mo); diff --git a/src/m_cond.c b/src/m_cond.c index 706a1b510..5a5913297 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -475,6 +475,9 @@ UINT8 M_MapLocked(INT32 mapnum, gamedata_t *data) // that's better than making dedicated server's lives hell. return false; } + + if (cv_debug || devparm) + return false; // Unlock every level when in devmode. if (!mapheaderinfo[mapnum-1] || mapheaderinfo[mapnum-1]->unlockrequired < 0) { diff --git a/src/m_fixed.h b/src/m_fixed.h index 94bd6a16b..4e644c9b6 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -34,6 +34,7 @@ */ typedef INT32 fixed_t; +typedef UINT32 ufixed_t; /*! \brief convert fixed_t into floating number @@ -106,7 +107,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedInt(fixed_t a) */ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv(fixed_t a, fixed_t b) { - if ((abs(a) >> (FRACBITS-2)) >= abs(b)) + if (((ufixed_t)abs(a) >> (FRACBITS-2)) >= (ufixed_t)abs(b)) return (a^b) < 0 ? INT32_MIN : INT32_MAX; return FixedDiv2(a, b); diff --git a/src/m_menu.c b/src/m_menu.c index 1b4c48c75..4e1c94860 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -132,7 +132,9 @@ M_waiting_mode_t m_waiting_mode = M_NOT_WAITING; const char *quitmsg[NUM_QUITMESSAGES]; // Stuff for customizing the player select screen Tails 09-22-2003 -description_t description[MAXSKINS]; +description_t *description = NULL; +INT32 numdescriptions = 0; + INT16 char_on = -1, startchar = 0; static char *char_notes = NULL; @@ -259,7 +261,7 @@ static void M_ConfirmTeamScramble(INT32 choice); static void M_ConfirmTeamChange(INT32 choice); static void M_SecretsMenu(INT32 choice); static void M_SetupChoosePlayer(INT32 choice); -static UINT8 M_SetupChoosePlayerDirect(INT32 choice); +static UINT16 M_SetupChoosePlayerDirect(INT32 choice); static void M_QuitSRB2(INT32 choice); menu_t SP_MainDef, OP_MainDef; menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef; @@ -2252,7 +2254,7 @@ void Nextmap_OnChange(void) SP_NightsAttackMenu[naghost].status = IT_DISABLED; // Check if file exists, if not, disable REPLAY option - sprintf(tabase,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s",srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name); + sprintf(tabase,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s",srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1]->name); #ifdef OLDNREPLAYNAME sprintf(tabaseold,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s",srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); @@ -2323,7 +2325,7 @@ void Nextmap_OnChange(void) SP_TimeAttackMenu[taghost].status = IT_DISABLED; // Check if file exists, if not, disable REPLAY option - sprintf(tabase,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s",srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name); + sprintf(tabase,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s",srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1]->name); for (i = 0; i < 5; i++) { SP_ReplayMenu[i].status = IT_DISABLED; SP_GuestReplayMenu[i].status = IT_DISABLED; @@ -2745,7 +2747,7 @@ void M_ChangeMenuMusic(const char *defaultmusname, boolean defaultmuslooping) void M_SetMenuCurBackground(const char *defaultname) { - char name[9]; + char name[9] = ""; strncpy(name, defaultname, 8); name[8] = '\0'; M_IterateMenuTree(MIT_SetCurBackground, &name); @@ -3023,7 +3025,7 @@ static void M_ChangeCvar(INT32 choice) { SINT8 skinno = R_SkinAvailable(cv_chooseskin.string); if (skinno != -1) - CV_SetValue(cv,skins[skinno].prefcolor); + CV_SetValue(cv,skins[skinno]->prefcolor); return; } CV_Set(cv,cv->defaultvalue); @@ -3171,40 +3173,42 @@ boolean M_Responder(event_t *ev) } else if (menuactive) { - if (ev->type == ev_keydown) + if (ev->type == ev_keydown || ev->type == ev_text) { - keydown++; ch = ev->key; - - // added 5-2-98 remap virtual keys (mouse & joystick buttons) - switch (ch) + if (ev->type == ev_keydown) { - case KEY_MOUSE1: - case KEY_JOY1: - ch = KEY_ENTER; - break; - case KEY_JOY1 + 3: - ch = 'n'; - break; - case KEY_MOUSE1 + 1: - case KEY_JOY1 + 1: - ch = KEY_ESCAPE; - break; - case KEY_JOY1 + 2: - ch = KEY_BACKSPACE; - break; - case KEY_HAT1: - ch = KEY_UPARROW; - break; - case KEY_HAT1 + 1: - ch = KEY_DOWNARROW; - break; - case KEY_HAT1 + 2: - ch = KEY_LEFTARROW; - break; - case KEY_HAT1 + 3: - ch = KEY_RIGHTARROW; - break; + keydown++; + // added 5-2-98 remap virtual keys (mouse & joystick buttons) + switch (ch) + { + case KEY_MOUSE1: + case KEY_JOY1: + ch = KEY_ENTER; + break; + case KEY_JOY1 + 3: + ch = 'n'; + break; + case KEY_MOUSE1 + 1: + case KEY_JOY1 + 1: + ch = KEY_ESCAPE; + break; + case KEY_JOY1 + 2: + ch = KEY_BACKSPACE; + break; + case KEY_HAT1: + ch = KEY_UPARROW; + break; + case KEY_HAT1 + 1: + ch = KEY_DOWNARROW; + break; + case KEY_HAT1 + 2: + ch = KEY_LEFTARROW; + break; + case KEY_HAT1 + 3: + ch = KEY_RIGHTARROW; + break; + } } } else if (ev->type == ev_joystick && ev->key == 0 && joywait < I_GetTime()) @@ -3366,8 +3370,11 @@ boolean M_Responder(event_t *ev) // Handle menuitems which need a specific key handling if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER) { - if (shiftdown && ch >= 32 && ch <= 127) - ch = shiftxform[ch]; + // ignore ev_keydown events if the key maps to a character, since + // the ev_text event will follow immediately after in that case. + if (ev->type == ev_keydown && ch >= 32 && ch <= 127) + return true; + routine(ch); return true; } @@ -3409,6 +3416,11 @@ boolean M_Responder(event_t *ev) { if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING) { + // ignore ev_keydown events if the key maps to a character, since + // the ev_text event will follow immediately after in that case. + if (ev->type == ev_keydown && ch >= 32 && ch <= 127) + return false; + if (M_ChangeStringCvar(ch)) return true; else @@ -3634,9 +3646,12 @@ void M_StartControlPanel(void) } else if (!(netgame || multiplayer)) // Single Player { + // Devmode unlocks Pandora's Box in the pause menu + boolean pandora = ((M_SecretUnlocked(SECRET_PANDORA, serverGamedata) || cv_debug || devparm) && !marathonmode); + if (gamestate != GS_LEVEL || ultimatemode) // intermission, so gray out stuff. { - SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata)) ? (IT_GRAYEDOUT) : (IT_DISABLED); + SPauseMenu[spause_pandora].status = (pandora) ? (IT_GRAYEDOUT) : (IT_DISABLED); SPauseMenu[spause_retry].status = IT_GRAYEDOUT; } else @@ -3645,7 +3660,7 @@ void M_StartControlPanel(void) if (players[consoleplayer].playerstate != PST_LIVE) ++numlives; - SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA, serverGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED); + SPauseMenu[spause_pandora].status = (pandora) ? (IT_STRING | IT_CALL) : (IT_DISABLED); // The list of things that can disable retrying is (was?) a little too complex // for me to want to use the short if statement syntax @@ -3656,7 +3671,11 @@ void M_StartControlPanel(void) } // We can always use level select though. :33 - SPauseMenu[spause_levelselect].status = (maplistoption != 0) ? (IT_STRING | IT_CALL) : (IT_DISABLED); + // Guarantee it if we have either it unlocked or devmode is enabled + if ((maplistoption != 0 || M_SecretUnlocked(SECRET_LEVELSELECT, serverGamedata) || cv_debug || devparm) && !marathonmode) + SPauseMenu[spause_levelselect].status = (IT_STRING | IT_CALL); + else + SPauseMenu[spause_levelselect].status = (IT_DISABLED); // And emblem hints. SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS, clientGamedata) && !marathonmode) ? (IT_STRING | IT_CALL) : (IT_DISABLED); @@ -3920,24 +3939,32 @@ void M_Init(void) CV_RegisterVar(&cv_serversort); } -void M_InitCharacterTables(void) +static void M_InitCharacterDescription(INT32 i) { - UINT8 i; - // Setup description table - for (i = 0; i < MAXSKINS; i++) - { - description[i].used = false; - strcpy(description[i].notes, "???"); - strcpy(description[i].picname, ""); - strcpy(description[i].nametag, ""); - strcpy(description[i].skinname, ""); - strcpy(description[i].displayname, ""); - description[i].prev = description[i].next = 0; - description[i].charpic = NULL; - description[i].namepic = NULL; - description[i].oppositecolor = description[i].tagtextcolor = description[i].tagoutlinecolor = 0; - } + description_t *desc = &description[i]; + desc->picname[0] = '\0'; + desc->nametag[0] = '\0'; + desc->skinname[0] = '\0'; + desc->displayname[0] = '\0'; + desc->prev = desc->next = 0; + desc->charpic = NULL; + desc->namepic = NULL; + desc->oppositecolor = SKINCOLOR_NONE; + desc->tagtextcolor = SKINCOLOR_NONE; + desc->tagoutlinecolor = SKINCOLOR_NONE; + strcpy(desc->notes, "???"); +} + +void M_InitCharacterTables(INT32 num) +{ + INT32 i = numdescriptions; + + description = Z_Realloc(description, sizeof(description_t) * num, PU_STATIC, NULL); + numdescriptions = num; + + for (; i < numdescriptions; i++) + M_InitCharacterDescription(i); } // ========================================================================== @@ -4067,31 +4094,97 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines) V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 159); } -static fixed_t staticalong = 0; - +// +// Draw the TV static effect on unavailable map icons +// static void M_DrawStaticBox(fixed_t x, fixed_t y, INT32 flags, fixed_t w, fixed_t h) { - patch_t *patch; - fixed_t sw, pw; + patch_t *patch = W_CachePatchName("LSSTATIC", PU_PATCH); + static fixed_t staticx = 0, staticy = 0; // Keep track of where we are across function calls - patch = W_CachePatchName("LSSTATIC", PU_PATCH); - pw = patch->width - (sw = w*2); //FixedDiv(w, scale); -- for scale FRACUNIT/2 - /*if (pw > 0) -- model code for modders providing weird LSSTATIC + if (!patch->width || !patch->height) // Shouldn't be necessary, but I don't want to get in trouble! { - if (staticalong > pw) - staticalong -= pw; + W_UnlockCachedPatch(patch); + return; } - else - staticalong = 0;*/ + if (patch->width == 1) // Nothing to randomise or tile + { + // Just stretch the patch over the whole box - no need to draw it 160 times over + // Technically, we should crop and maybe tile and randomise the Y axis, but... it's not worth it here + V_DrawStretchyFixedPatch(x*FRACUNIT, y*FRACUNIT, (w*FRACUNIT) / patch->width, (h*FRACUNIT) / patch->height, flags, patch, NULL); + W_UnlockCachedPatch(patch); + return; + } + if (patch->width == 160) // Something to randomise or tile - but don't! + { + // If it's 160 pixels wide, the modder probably wants the patch fixed in place + // But instead of "just drawing" it, why not allow sequential frames of animation on the Y axis? + // For example, this could be used to make a vignette effect, whether animated or not + // This function is primarily called with a patch region of 160x100, so the frames must be 160x100 too + fixed_t temp = patch->height / 100; // Amount of 160x100 frames in the patch + if (temp) // Don't modulo by zero + temp = (gametic % temp) * h*2*FRACUNIT; // Which frame to draw - if (staticalong > pw) // simplified for base LSSTATIC - staticalong -= pw; + V_DrawCroppedPatch(x*FRACUNIT, y*FRACUNIT, (w*FRACUNIT) / 160, (h*FRACUNIT) / 100, flags, patch, NULL, 0, temp, w*2*FRACUNIT, h*2*FRACUNIT); + + W_UnlockCachedPatch(patch); + return; + } - V_DrawCroppedPatch(x<width*2); + + if (patch->height == h*2) // Is the patch 100 pixels tall? If so, reset "staticy"... + staticy = 0; // ...in case that one add-on would randomise it and a later add-on wouldn't + else // Otherwise, as we already make "staticx" near-sequential, I think that making "staticy"... + staticy = M_RandomRange(0, (patch->height*2) - 1); // ...fully random instead of sequential increases the... randomness + + // The drawing function calls can get a bit lengthy, so let's make a little shortcut +#define DRAWSTATIC(_x,_y,_sx,_sy,_w,_h) V_DrawCroppedPatch((x*FRACUNIT)+((_x)*FRACUNIT/4), (y*FRACUNIT)+((_y)*FRACUNIT/4),\ +FRACUNIT/2, FRACUNIT/2, flags, patch, NULL, (_sx)*FRACUNIT/2, (_sy)*FRACUNIT/2, (w*2*FRACUNIT)+((_w)*FRACUNIT/2), (h*2*FRACUNIT)+((_h)*FRACUNIT/2)) + + // And finally, let's draw it! Don't worry about "staticx" plus "w*2" potentially going off-patch + DRAWSTATIC(0, 0, staticx, staticy, 0, 0); // This gets drawn in all cases + + if ((patch->width*2) - staticx >= w*4) // No horizontal tiling + { + if ((patch->height*2) - staticy >= h*4) // Simplest-case scenario, no tiling at all + {} + else // Vertical tiling only + { + for (INT16 j = 2; ((patch->height*j) - staticy) < h*4; j += 2) + DRAWSTATIC(0, (patch->height*j) - staticy, staticx, 0, 0, staticy - (patch->height*j)); + } + } + else // Horizontal tiling + { + if ((patch->height*2) - staticy >= h*4) // Horizontal tiling only + { + for (INT16 i = 2; ((patch->width*i) - staticx) < w*4; i += 2) + DRAWSTATIC((patch->width*i) - staticx, 0, 0, staticy, staticx - (patch->width*i), 0); + } + else // Horizontal and vertical tiling + { + for (INT16 j = 2; ((patch->height*j) - staticy) < h*4; j += 2) + DRAWSTATIC(0, (patch->height*j) - staticy, staticx, 0, 0, staticy - (patch->height*j)); + + for (INT16 i = 2; ((patch->width*i) - staticx) < w*4; i += 2) + { + DRAWSTATIC((patch->width*i) - staticx, 0, 0, staticy, staticx - (patch->width*i), 0); + for (INT16 j = 2; ((patch->height*j) - staticy) < h*4; j += 2) + DRAWSTATIC((patch->width*i) - staticx, (patch->height*j) - staticy, 0, 0, staticx - (patch->width*i), staticy - (patch->height*j)); + } + } + } +#undef DRAWSTATIC + + // Now that we're done with the patch, it's time to say our goodbyes. Until next time, patch! W_UnlockCachedPatch(patch); } @@ -4885,9 +4978,9 @@ static void M_PatchSkinNameTable(void) for (j = 0; j < MAXSKINS; j++) { - if (skins[j].name[0] != '\0' && R_SkinUsable(-1, j)) + if (j < numskins && skins[j]->name[0] != '\0' && R_SkinUsable(-1, j)) { - skins_cons_t[j].strvalue = skins[j].realname; + skins_cons_t[j].strvalue = skins[j]->realname; skins_cons_t[j].value = j+1; } else @@ -5003,7 +5096,8 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt) return false; case LLM_LEVELSELECT: - if (!(mapheaderinfo[mapnum]->levelselect & maplistoption)) + if (!(mapheaderinfo[mapnum]->levelselect & maplistoption) + && !(cv_debug || devparm)) //Allow ALL levels in devmode! return false; return true; @@ -5832,16 +5926,6 @@ static void M_DrawNightsAttackBackground(void) if (ntsatkdrawtimer < 0) ntsatkdrawtimer = 0; } -// NiGHTS Attack floating Super Sonic. -static patch_t *ntssupersonic[2]; -static void M_DrawNightsAttackSuperSonic(void) -{ - const UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_YELLOW, GTC_CACHE); - INT32 timer = FixedInt(ntsatkdrawtimer/4) % 2; - angle_t fa = (FixedAngle((FixedInt(ntsatkdrawtimer * 4) % 360)<>ANGLETOFINESHIFT) & FINEMASK; - V_DrawFixedPatch(235<sprites[SPR2_SIGN]; if (!sprdef->numframes) goto skipbot; @@ -9076,9 +9163,9 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) { if (!(description[i].picname[0])) { - if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_CHARSEL) + if (skins[skinnum]->sprites[SPR2_XTRA].numframes > XTRA_CHARSEL) { - spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + spritedef_t *sprdef = &skins[skinnum]->sprites[SPR2_XTRA]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL]; description[i].charpic = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); } @@ -9092,71 +9179,74 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) description[i].namepic = W_CachePatchName(description[i].nametag, PU_PATCH); } -static UINT8 M_SetupChoosePlayerDirect(INT32 choice) +static UINT16 M_SetupChoosePlayerDirect(INT32 choice) { INT32 skinnum, botskinnum; - UINT8 i; - UINT8 firstvalid = 255, lastvalid = 255; + UINT16 i; + INT32 firstvalid = INT32_MAX, lastvalid = INT32_MAX; boolean allowed = false; - char *and; (void)choice; if (!mapheaderinfo[startmap-1] || mapheaderinfo[startmap-1]->forcecharacter[0] == '\0') { - for (i = 0; i < MAXSKINS; i++) // Handle charsels, availability, and unlocks. + for (i = 0; i < numdescriptions; i++) // Handle charsels, availability, and unlocks. { - if (description[i].used) // If the character's disabled through SOC, there's nothing we can do for it. + char *and; + + // If the character's disabled through SOC, there's nothing we can do for it. + if (!description[i].used) + continue; + + and = strchr(description[i].skinname, '&'); + + if (and) { - and = strchr(description[i].skinname, '&'); - if (and) + char firstskin[SKINNAMESIZE+1]; + if (mapheaderinfo[startmap-1] && mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS) // skip tagteam characters for NiGHTS levels + continue; + strncpy(firstskin, description[i].skinname, (and - description[i].skinname)); + firstskin[(and - description[i].skinname)] = '\0'; + description[i].skinnum[0] = R_SkinAvailable(firstskin); + description[i].skinnum[1] = R_SkinAvailable(and+1); + } + else + { + description[i].skinnum[0] = R_SkinAvailable(description[i].skinname); + description[i].skinnum[1] = -1; + } + + skinnum = description[i].skinnum[0]; + + if ((skinnum != -1) && (R_SkinUsable(-1, skinnum))) + { + botskinnum = description[i].skinnum[1]; + if ((botskinnum != -1) && (!R_SkinUsable(-1, botskinnum))) { - char firstskin[SKINNAMESIZE+1]; - if (mapheaderinfo[startmap-1] && mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS) // skip tagteam characters for NiGHTS levels - continue; - strncpy(firstskin, description[i].skinname, (and - description[i].skinname)); - firstskin[(and - description[i].skinname)] = '\0'; - description[i].skinnum[0] = R_SkinAvailable(firstskin); - description[i].skinnum[1] = R_SkinAvailable(and+1); + // Bot skin isn't unlocked + continue; } + + // Handling order. + if (firstvalid == INT32_MAX) + firstvalid = i; else { - description[i].skinnum[0] = R_SkinAvailable(description[i].skinname); - description[i].skinnum[1] = -1; + description[i].prev = lastvalid; + description[lastvalid].next = i; } - skinnum = description[i].skinnum[0]; - if ((skinnum != -1) && (R_SkinUsable(-1, skinnum))) - { - botskinnum = description[i].skinnum[1]; - if ((botskinnum != -1) && (!R_SkinUsable(-1, botskinnum))) - { - // Bot skin isn't unlocked - continue; - } + lastvalid = i; - // Handling order. - if (firstvalid == 255) - firstvalid = i; - else - { - description[i].prev = lastvalid; - description[lastvalid].next = i; - } - lastvalid = i; + if (i == char_on) + allowed = true; - if (i == char_on) - allowed = true; - - M_CacheCharacterSelectEntry(i, skinnum); - } - // else -- Technically, character select icons without corresponding skins get bundled away behind this too. Sucks to be them. + M_CacheCharacterSelectEntry(i, skinnum); } + // else -- Technically, character select icons without corresponding skins get bundled away behind this too. Sucks to be them. } } if (firstvalid == lastvalid) // We're being forced into a specific character, so might as well just skip it. - { return firstvalid; - } // One last bit of order we can't do in the iteration above. description[firstvalid].prev = lastvalid; @@ -9165,7 +9255,7 @@ static UINT8 M_SetupChoosePlayerDirect(INT32 choice) if (!allowed) { char_on = firstvalid; - if (startchar > 0 && startchar < MAXSKINS) + if (startchar > 0 && startchar < numdescriptions) { INT16 workchar = startchar; while (workchar--) @@ -9173,13 +9263,13 @@ static UINT8 M_SetupChoosePlayerDirect(INT32 choice) } } - return MAXSKINS; + return MAXCHARACTERSLOTS; } static void M_SetupChoosePlayer(INT32 choice) { - UINT8 skinset = M_SetupChoosePlayerDirect(choice); - if (skinset != MAXSKINS) + UINT16 skinset = M_SetupChoosePlayerDirect(choice); + if (skinset != MAXCHARACTERSLOTS) { M_ChoosePlayer(skinset); return; @@ -9290,7 +9380,7 @@ static void M_DrawSetupChoosePlayerMenu(void) { const INT32 my = 16; - skin_t *charskin = &skins[0]; + skin_t *charskin = skins[0]; INT32 skinnum = 0; UINT16 col; UINT8 *colormap = NULL; @@ -9322,7 +9412,7 @@ static void M_DrawSetupChoosePlayerMenu(void) // Find skin number from description[] skinnum = description[char_on].skinnum[0]; - charskin = &skins[skinnum]; + charskin = skins[skinnum]; // Use the opposite of the character's skincolor col = description[char_on].oppositecolor; @@ -9425,7 +9515,7 @@ static void M_DrawSetupChoosePlayerMenu(void) prevoutlinecolor = description[prev].tagoutlinecolor; if (prevtext[0] == '\0') prevpatch = description[prev].namepic; - charskin = &skins[description[prev].skinnum[0]]; + charskin = skins[description[prev].skinnum[0]]; if (!prevtextcolor) prevtextcolor = charskin->prefcolor; if (!prevoutlinecolor) @@ -9455,7 +9545,7 @@ static void M_DrawSetupChoosePlayerMenu(void) nextoutlinecolor = description[next].tagoutlinecolor; if (nexttext[0] == '\0') nextpatch = description[next].namepic; - charskin = &skins[description[next].skinnum[0]]; + charskin = skins[description[next].skinnum[0]]; if (!nexttextcolor) nexttextcolor = charskin->prefcolor; if (!nextoutlinecolor) @@ -9500,7 +9590,7 @@ static void M_ChoosePlayer(INT32 choice) UINT8 skinnum; // skip this if forcecharacter or no characters available - if (choice == 255) + if (choice == INT32_MAX) { skinnum = botskin = 0; botingame = false; @@ -9513,7 +9603,7 @@ static void M_ChoosePlayer(INT32 choice) if ((botingame = (description[choice].skinnum[1] != -1))) { // this character has a second skin botskin = (UINT8)(description[choice].skinnum[1]+1); - botcolor = skins[description[choice].skinnum[1]].prefcolor; + botcolor = skins[description[choice].skinnum[1]]->prefcolor; } else botskin = botcolor = 0; @@ -9801,7 +9891,7 @@ void M_DrawTimeAttackMenu(void) gamedata_t *data = clientGamedata; INT32 i, x, y, empatx, empaty, cursory = 0; UINT16 dispstatus; - patch_t *PictureOfUrFace; // my WHAT + patch_t *PictureOfUrFace; patch_t *empatch; M_SetMenuCurBackground("RECATKBG"); @@ -9870,9 +9960,9 @@ void M_DrawTimeAttackMenu(void) // Character face! { - if (skins[cv_chooseskin.value-1].sprites[SPR2_XTRA].numframes > XTRA_CHARSEL) + if (skins[cv_chooseskin.value-1]->sprites[SPR2_XTRA].numframes > XTRA_CHARSEL) { - spritedef_t *sprdef = &skins[cv_chooseskin.value-1].sprites[SPR2_XTRA]; + spritedef_t *sprdef = &skins[cv_chooseskin.value-1]->sprites[SPR2_XTRA]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL]; PictureOfUrFace = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); } @@ -10192,8 +10282,38 @@ void M_DrawNightsAttackMenu(void) V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit")); } - // Super Sonic - M_DrawNightsAttackSuperSonic(); + // Draw selected character's NiGHTS sprite + patch_t *natksprite; //The patch for the sprite itself + INT32 spritetimer; //Timer for animating NiGHTS sprite + INT32 skinnumber; //Number for skin + UINT16 color; //natkcolor + + if (skins[cv_chooseskin.value-1]->sprites[SPR2_NFLY].numframes == 0) //If we don't have NiGHTS sprites + skinnumber = 0; //Default to Sonic + else + skinnumber = (cv_chooseskin.value-1); + + spritedef_t *sprdef = &skins[skinnumber]->sprites[SPR2_NFLY]; //Make our patch the selected character's NFLY sprite + spritetimer = FixedInt(ntsatkdrawtimer/2) % skins[skinnumber]->sprites[SPR2_NFLY].numframes; //Make the sprite timer cycle though all the frames at 2 tics per frame + spriteframe_t *sprframe = &sprdef->spriteframes[spritetimer]; //Our animation frame is equal to the number on the timer + + natksprite = W_CachePatchNum(sprframe->lumppat[6], PU_PATCH); //Draw the right facing angle + + if (skins[skinnumber]->natkcolor) //If you set natkcolor use it + color = skins[skinnumber]->natkcolor; + else if ((skins[skinnumber]->flags & SF_SUPER) && !(skins[skinnumber]->flags & SF_NONIGHTSSUPER)) //If you go super in NiGHTS, use supercolor + color = skins[skinnumber]->supercolor+4; + else //If you don't go super in NiGHTS or at all, use prefcolor + color = skins[skinnumber]->prefcolor; + + angle_t fa = (FixedAngle(((FixedInt(ntsatkdrawtimer * 4)) % 360)<>ANGLETOFINESHIFT) & FINEMASK; + + V_DrawFixedPatch(270<highresscale, skins[skinnumber]->shieldscale), + (sprframe->flip & 1<<6) ? V_FLIP : 0, + natksprite, + R_GetTranslationColormap(TC_BLINK, color, GTC_CACHE)); + //if (P_HasGrades(cv_nextmap.value, 0)) // V_DrawScaledPatch(235 - (((ngradeletters[bestoverall])->width)*3)/2, 135, 0, ngradeletters[bestoverall]); @@ -10285,9 +10405,6 @@ static void M_NightsAttack(INT32 choice) // This is really just to make sure Sonic is the played character, just in case M_PatchSkinNameTable(); - ntssupersonic[0] = W_CachePatchName("NTSSONC1", PU_PATCH); - ntssupersonic[1] = W_CachePatchName("NTSSONC2", PU_PATCH); - G_SetGamestate(GS_TIMEATTACK); // do this before M_SetupNextMenu so that menu meta state knows that we're switching titlemapinaction = TITLEMAP_OFF; // Nope don't give us HOMs please M_SetupNextMenu(&SP_NightsAttackDef); @@ -10318,7 +10435,7 @@ static void M_ChooseNightsAttack(INT32 choice) I_Error("Out of memory for replay filepath\n"); sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(cv_nextmap.value)); - snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1].name); + snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name); if (!cv_autorecord.value) remove(va("%s"PATHSEP"%s.lmp", srb2home, nameofdemo)); @@ -10347,7 +10464,7 @@ static void M_ChooseTimeAttack(INT32 choice) I_Error("Out of memory for replay filepath\n"); sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(cv_nextmap.value)); - snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1].name); + snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name); if (!cv_autorecord.value) remove(va("%s"PATHSEP"%s.lmp", srb2home, nameofdemo)); @@ -10400,7 +10517,7 @@ static void M_ReplayTimeAttack(INT32 choice) if (choice != 4) { // srb2/replay/main/map01-sonic-time-best.lmp - snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name, which); + snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1]->name, which); } } else if (currentMenu == &SP_NightsReplayDef) @@ -10423,7 +10540,7 @@ static void M_ReplayTimeAttack(INT32 choice) if (choice != 3) { - snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name, which); + snprintf(ra_demoname, 1024, "%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1]->name, which); #ifdef OLDNREPLAYNAME // Check for old style named NiGHTS replay if a new style replay doesn't exist. if (!FIL_FileExists(ra_demoname)) @@ -10510,7 +10627,7 @@ static void M_OverwriteGuest(const char *which) char *rguest = Z_StrDup(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value))); UINT8 *buf; size_t len; - len = FIL_ReadFile(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1].name, which), &buf); + len = FIL_ReadFile(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), skins[cv_chooseskin.value-1]->name, which), &buf); if (!len) { return; @@ -10652,7 +10769,7 @@ static void M_MarathonLiveEventBackup(INT32 choice) // Going to Marathon menu... static void M_Marathon(INT32 choice) { - UINT8 skinset; + UINT16 skinset; INT32 mapnum = 0; if (choice != -1 && FIL_FileExists(liveeventbackup)) @@ -10676,7 +10793,7 @@ static void M_Marathon(INT32 choice) skinset = M_SetupChoosePlayerDirect(-1); - SP_MarathonMenu[marathonplayer].status = (skinset == MAXSKINS) ? IT_KEYHANDLER|IT_STRING : IT_NOTHING|IT_DISABLED; + SP_MarathonMenu[marathonplayer].status = (skinset == MAXCHARACTERSLOTS) ? IT_KEYHANDLER|IT_STRING : IT_NOTHING|IT_DISABLED; while (mapnum < NUMMAPS) { @@ -11202,6 +11319,8 @@ static void M_DrawConnectMenu(void) V_DrawSmallString(currentMenu->x+202, S_LINEY(i)+8, globalflags, "\x85" "Mod"); if (serverlist[slindex].info.cheatsenabled) V_DrawSmallString(currentMenu->x+222, S_LINEY(i)+8, globalflags, "\x83" "Cheats"); + if (Net_IsNodeIPv6(serverlist[slindex].node)) + V_DrawSmallString(currentMenu->x+252, S_LINEY(i)+8, globalflags, "\x84" "IPv6"); V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags, va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time))); @@ -11895,7 +12014,7 @@ static void M_HandleConnectIP(INT32 choice) // Rudimentary number and period enforcing - also allows letters so hostnames can be used instead // and square brackets for RFC 2732 IPv6 addresses if ((choice >= '-' && choice <= ':') || - (choice == '[' || choice == ']') || + (choice == '[' || choice == ']' || choice == '%') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z')) { @@ -12117,11 +12236,11 @@ static void M_DrawSetupMultiPlayerMenu(void) // draw skin string V_DrawRightAlignedString(BASEVIDWIDTH - x, y, ((MP_PlayerSetupMenu[1].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|(itemOn == 1 ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE, - skins[setupm_fakeskin].realname); + skins[setupm_fakeskin]->realname); if (itemOn == 1 && (MP_PlayerSetupMenu[1].status & IT_TYPE) != IT_SPACE) { - V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(skins[setupm_fakeskin].realname, V_ALLOWLOWERCASE) - (skullAnimCounter/5), y, + V_DrawCharacter(BASEVIDWIDTH - x - 10 - V_StringWidth(skins[setupm_fakeskin]->realname, V_ALLOWLOWERCASE) - (skullAnimCounter/5), y, '\x1C' | V_YELLOWMAP, false); V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y, '\x1D' | V_YELLOWMAP, false); @@ -12144,7 +12263,7 @@ static void M_DrawSetupMultiPlayerMenu(void) V_DrawFill(x-(charw/2), y, charw, 84, multi_invcolor ?skincolors[skincolors[setupm_fakecolor->color].invcolor].ramp[skincolors[setupm_fakecolor->color].invshade] : 159); - sprdef = &skins[setupm_fakeskin].sprites[multi_spr2]; + sprdef = &skins[setupm_fakeskin]->sprites[multi_spr2]; if (!setupm_fakecolor->color || !sprdef->numframes) // should never happen but hey, who knows goto faildraw; @@ -12165,7 +12284,7 @@ static void M_DrawSetupMultiPlayerMenu(void) V_DrawFixedPatch( x<highresscale, skins[setupm_fakeskin]->shieldscale), flags, patch, colormap); goto colordraw; @@ -12397,7 +12516,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) setupm_fakeskin = numskins-1; } while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUsable(-1, setupm_fakeskin))); - multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_WALK, NULL); + multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); } else if (itemOn == 2) // player color { @@ -12413,7 +12532,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) { S_StartSound(NULL,sfx_strpst); // you know what? always putting these in the buffer won't hurt anything. - COM_BufAddText (va("%s \"%s\"\n",setupm_cvdefaultskin->name,skins[setupm_fakeskin].name)); + COM_BufAddText (va("%s \"%s\"\n",setupm_cvdefaultskin->name,skins[setupm_fakeskin]->name)); COM_BufAddText (va("%s %d\n",setupm_cvdefaultcolor->name,setupm_fakecolor->color)); break; } @@ -12437,7 +12556,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) setupm_fakeskin = 0; } while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUsable(-1, setupm_fakeskin))); - multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_WALK, NULL); + multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); } else if (itemOn == 2) // player color { @@ -12493,7 +12612,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) } else if (itemOn == 2) { - UINT16 col = skins[setupm_fakeskin].prefcolor; + UINT16 col = skins[setupm_fakeskin]->prefcolor; if ((setupm_fakecolor->color != col) && skincolors[col].accessible) { S_StartSound(NULL,sfx_menu1); // Tails @@ -12587,7 +12706,7 @@ static void M_SetupMultiPlayer(INT32 choice) MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING); - multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_WALK, NULL); + multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); @@ -12628,7 +12747,7 @@ static void M_SetupMultiPlayer2(INT32 choice) MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING); - multi_spr2 = P_GetSkinSprite2(&skins[setupm_fakeskin], SPR2_WALK, NULL); + multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); @@ -12646,7 +12765,7 @@ static boolean M_QuitMultiPlayerMenu(void) setupm_name[l] =0; COM_BufAddText (va("%s \"%s\"\n",setupm_cvname->name,setupm_name)); } - COM_BufAddText (va("%s \"%s\"\n",setupm_cvskin->name,skins[setupm_fakeskin].name)); + COM_BufAddText (va("%s \"%s\"\n",setupm_cvskin->name,skins[setupm_fakeskin]->name)); // send color if changed if (setupm_fakecolor->color != setupm_cvcolor->value) COM_BufAddText (va("%s %d\n",setupm_cvcolor->name,setupm_fakecolor->color)); diff --git a/src/m_menu.h b/src/m_menu.h index 37e94dd9b..6ccdd88a3 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -200,8 +200,8 @@ void M_Drawer(void); // Called by D_SRB2Main, loads the config file. void M_Init(void); -// Called by D_SRB2Main also, sets up the playermenu and description tables. -void M_InitCharacterTables(void); +// Called by deh_soc.c, sets up the playermenu and description tables. +void M_InitCharacterTables(INT32 num); // Called by intro code to force menu up upon a keypress, // does nothing if menu is already up. @@ -375,10 +375,8 @@ typedef struct patch_t *charpic; UINT8 prev; UINT8 next; - - // new character select char displayname[SKINNAMESIZE+1]; - SINT8 skinnum[2]; + INT16 skinnum[2]; UINT16 oppositecolor; char nametag[8]; patch_t *namepic; @@ -430,7 +428,8 @@ typedef struct INT32 gamemap; } saveinfo_t; -extern description_t description[MAXSKINS]; +extern description_t *description; +extern INT32 numdescriptions; extern consvar_t cv_showfocuslost; extern consvar_t cv_newgametype, cv_nextmap, cv_chooseskin, cv_serversort; diff --git a/src/m_misc.c b/src/m_misc.c index 923a188c6..ce332910d 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -2207,430 +2207,11 @@ char *sizeu5(size_t num) return sizeu5_buf; } -#if defined (__GNUC__) && defined (__i386__) // from libkwave, under GPL -// Alam: note libkwave memcpy code comes from mplayer's libvo/aclib_template.c, r699 - -/* for small memory blocks (<256 bytes) this version is faster */ -#define small_memcpy(dest,src,n)\ -{\ -register unsigned long int dummy;\ -__asm__ __volatile__(\ - "cld\n\t"\ - "rep; movsb"\ - :"=&D"(dest), "=&S"(src), "=&c"(dummy)\ - :"0" (dest), "1" (src),"2" (n)\ - : "memory", "cc");\ -} -/* linux kernel __memcpy (from: /include/asm/string.h) */ -ATTRINLINE static FUNCINLINE void *__memcpy (void *dest, const void * src, size_t n) +void *M_Memcpy(void *dest, const void *src, size_t n) { - int d0, d1, d2; - - if ( n < 4 ) - { - small_memcpy(dest, src, n); - } - else - { - __asm__ __volatile__ ( - "rep ; movsl;" - "testb $2,%b4;" - "je 1f;" - "movsw;" - "1:\ttestb $1,%b4;" - "je 2f;" - "movsb;" - "2:" - : "=&c" (d0), "=&D" (d1), "=&S" (d2) - :"0" (n/4), "q" (n),"1" ((long) dest),"2" ((long) src) - : "memory"); - } - - return dest; -} - -#define SSE_MMREG_SIZE 16 -#define MMX_MMREG_SIZE 8 - -#define MMX1_MIN_LEN 0x800 /* 2K blocks */ -#define MIN_LEN 0x40 /* 64-byte blocks */ - -/* SSE note: i tried to move 128 bytes a time instead of 64 but it -didn't make any measureable difference. i'm using 64 for the sake of -simplicity. [MF] */ -static /*FUNCTARGET("sse2")*/ void *sse_cpy(void * dest, const void * src, size_t n) -{ - void *retval = dest; - size_t i; - - /* PREFETCH has effect even for MOVSB instruction ;) */ - __asm__ __volatile__ ( - "prefetchnta (%0);" - "prefetchnta 32(%0);" - "prefetchnta 64(%0);" - "prefetchnta 96(%0);" - "prefetchnta 128(%0);" - "prefetchnta 160(%0);" - "prefetchnta 192(%0);" - "prefetchnta 224(%0);" - "prefetchnta 256(%0);" - "prefetchnta 288(%0);" - : : "r" (src) ); - - if (n >= MIN_LEN) - { - register unsigned long int delta; - /* Align destinition to MMREG_SIZE -boundary */ - delta = ((unsigned long int)dest)&(SSE_MMREG_SIZE-1); - if (delta) - { - delta=SSE_MMREG_SIZE-delta; - n -= delta; - small_memcpy(dest, src, delta); - } - i = n >> 6; /* n/64 */ - n&=63; - if (((unsigned long)src) & 15) - /* if SRC is misaligned */ - for (; i>0; i--) - { - __asm__ __volatile__ ( - "prefetchnta 320(%0);" - "prefetchnta 352(%0);" - "movups (%0), %%xmm0;" - "movups 16(%0), %%xmm1;" - "movups 32(%0), %%xmm2;" - "movups 48(%0), %%xmm3;" - "movntps %%xmm0, (%1);" - "movntps %%xmm1, 16(%1);" - "movntps %%xmm2, 32(%1);" - "movntps %%xmm3, 48(%1);" - :: "r" (src), "r" (dest) : "memory"); - src = (const unsigned char *)src + 64; - dest = (unsigned char *)dest + 64; - } - else - /* - Only if SRC is aligned on 16-byte boundary. - It allows to use movaps instead of movups, which required data - to be aligned or a general-protection exception (#GP) is generated. - */ - for (; i>0; i--) - { - __asm__ __volatile__ ( - "prefetchnta 320(%0);" - "prefetchnta 352(%0);" - "movaps (%0), %%xmm0;" - "movaps 16(%0), %%xmm1;" - "movaps 32(%0), %%xmm2;" - "movaps 48(%0), %%xmm3;" - "movntps %%xmm0, (%1);" - "movntps %%xmm1, 16(%1);" - "movntps %%xmm2, 32(%1);" - "movntps %%xmm3, 48(%1);" - :: "r" (src), "r" (dest) : "memory"); - src = ((const unsigned char *)src) + 64; - dest = ((unsigned char *)dest) + 64; - } - /* since movntq is weakly-ordered, a "sfence" - * is needed to become ordered again. */ - __asm__ __volatile__ ("sfence":::"memory"); - /* enables to use FPU */ - __asm__ __volatile__ ("emms":::"memory"); - } - /* - * Now do the tail of the block - */ - if (n) __memcpy(dest, src, n); - return retval; -} - -static FUNCTARGET("mmx") void *mmx2_cpy(void *dest, const void *src, size_t n) -{ - void *retval = dest; - size_t i; - - /* PREFETCH has effect even for MOVSB instruction ;) */ - __asm__ __volatile__ ( - "prefetchnta (%0);" - "prefetchnta 32(%0);" - "prefetchnta 64(%0);" - "prefetchnta 96(%0);" - "prefetchnta 128(%0);" - "prefetchnta 160(%0);" - "prefetchnta 192(%0);" - "prefetchnta 224(%0);" - "prefetchnta 256(%0);" - "prefetchnta 288(%0);" - : : "r" (src)); - - if (n >= MIN_LEN) - { - register unsigned long int delta; - /* Align destinition to MMREG_SIZE -boundary */ - delta = ((unsigned long int)dest)&(MMX_MMREG_SIZE-1); - if (delta) - { - delta=MMX_MMREG_SIZE-delta; - n -= delta; - small_memcpy(dest, src, delta); - } - i = n >> 6; /* n/64 */ - n&=63; - for (; i>0; i--) - { - __asm__ __volatile__ ( - "prefetchnta 320(%0);" - "prefetchnta 352(%0);" - "movq (%0), %%mm0;" - "movq 8(%0), %%mm1;" - "movq 16(%0), %%mm2;" - "movq 24(%0), %%mm3;" - "movq 32(%0), %%mm4;" - "movq 40(%0), %%mm5;" - "movq 48(%0), %%mm6;" - "movq 56(%0), %%mm7;" - "movntq %%mm0, (%1);" - "movntq %%mm1, 8(%1);" - "movntq %%mm2, 16(%1);" - "movntq %%mm3, 24(%1);" - "movntq %%mm4, 32(%1);" - "movntq %%mm5, 40(%1);" - "movntq %%mm6, 48(%1);" - "movntq %%mm7, 56(%1);" - :: "r" (src), "r" (dest) : "memory"); - src = ((const unsigned char *)src) + 64; - dest = ((unsigned char *)dest) + 64; - } - /* since movntq is weakly-ordered, a "sfence" - * is needed to become ordered again. */ - __asm__ __volatile__ ("sfence":::"memory"); - __asm__ __volatile__ ("emms":::"memory"); - } - /* - * Now do the tail of the block - */ - if (n) __memcpy(dest, src, n); - return retval; -} - -static FUNCTARGET("mmx") void *mmx1_cpy(void *dest, const void *src, size_t n) //3DNOW -{ - void *retval = dest; - size_t i; - - /* PREFETCH has effect even for MOVSB instruction ;) */ - __asm__ __volatile__ ( - "prefetch (%0);" - "prefetch 32(%0);" - "prefetch 64(%0);" - "prefetch 96(%0);" - "prefetch 128(%0);" - "prefetch 160(%0);" - "prefetch 192(%0);" - "prefetch 224(%0);" - "prefetch 256(%0);" - "prefetch 288(%0);" - : : "r" (src)); - - if (n >= MMX1_MIN_LEN) - { - register unsigned long int delta; - /* Align destinition to MMREG_SIZE -boundary */ - delta = ((unsigned long int)dest)&(MMX_MMREG_SIZE-1); - if (delta) - { - delta=MMX_MMREG_SIZE-delta; - n -= delta; - small_memcpy(dest, src, delta); - } - i = n >> 6; /* n/64 */ - n&=63; - for (; i>0; i--) - { - __asm__ __volatile__ ( - "prefetch 320(%0);" - "prefetch 352(%0);" - "movq (%0), %%mm0;" - "movq 8(%0), %%mm1;" - "movq 16(%0), %%mm2;" - "movq 24(%0), %%mm3;" - "movq 32(%0), %%mm4;" - "movq 40(%0), %%mm5;" - "movq 48(%0), %%mm6;" - "movq 56(%0), %%mm7;" - "movq %%mm0, (%1);" - "movq %%mm1, 8(%1);" - "movq %%mm2, 16(%1);" - "movq %%mm3, 24(%1);" - "movq %%mm4, 32(%1);" - "movq %%mm5, 40(%1);" - "movq %%mm6, 48(%1);" - "movq %%mm7, 56(%1);" - :: "r" (src), "r" (dest) : "memory"); - src = ((const unsigned char *)src) + 64; - dest = ((unsigned char *)dest) + 64; - } - __asm__ __volatile__ ("femms":::"memory"); // same as mmx_cpy() but with a femms - } - /* - * Now do the tail of the block - */ - if (n) __memcpy(dest, src, n); - return retval; -} -#endif - -// Alam: why? memcpy may be __cdecl/_System and our code may be not the same type -static void *cpu_cpy(void *dest, const void *src, size_t n) -{ - if (src == NULL) - { - CONS_Debug(DBG_MEMORY, "Memcpy from 0x0?!: %p %p %s\n", dest, src, sizeu1(n)); - return dest; - } - - if(dest == NULL) - { - CONS_Debug(DBG_MEMORY, "Memcpy to 0x0?!: %p %p %s\n", dest, src, sizeu1(n)); - return dest; - } - return memcpy(dest, src, n); } -static /*FUNCTARGET("mmx")*/ void *mmx_cpy(void *dest, const void *src, size_t n) -{ -#if defined (_MSC_VER) && defined (_X86_) - _asm - { - mov ecx, [n] - mov esi, [src] - mov edi, [dest] - shr ecx, 6 // mit mmx: 64bytes per iteration - jz lower_64 // if lower than 64 bytes - loop_64: // MMX transfers multiples of 64bytes - movq mm0, 0[ESI] // read sources - movq mm1, 8[ESI] - movq mm2, 16[ESI] - movq mm3, 24[ESI] - movq mm4, 32[ESI] - movq mm5, 40[ESI] - movq mm6, 48[ESI] - movq mm7, 56[ESI] - - movq 0[EDI], mm0 // write destination - movq 8[EDI], mm1 - movq 16[EDI], mm2 - movq 24[EDI], mm3 - movq 32[EDI], mm4 - movq 40[EDI], mm5 - movq 48[EDI], mm6 - movq 56[EDI], mm7 - - add esi, 64 - add edi, 64 - dec ecx - jnz loop_64 - emms // close mmx operation - lower_64:// transfer rest of buffer - mov ebx,esi - sub ebx,src - mov ecx,[n] - sub ecx,ebx - shr ecx, 3 // multiples of 8 bytes - jz lower_8 - loop_8: - movq mm0, [esi] // read source - movq [edi], mm0 // write destination - add esi, 8 - add edi, 8 - dec ecx - jnz loop_8 - emms // close mmx operation - lower_8: - mov ebx,esi - sub ebx,src - mov ecx,[n] - sub ecx,ebx - rep movsb - mov eax, [dest] // return dest - } -#elif defined (__GNUC__) && defined (__i386__) - void *retval = dest; - size_t i; - - if (n >= MMX1_MIN_LEN) - { - register unsigned long int delta; - /* Align destinition to MMREG_SIZE -boundary */ - delta = ((unsigned long int)dest)&(MMX_MMREG_SIZE-1); - if (delta) - { - delta=MMX_MMREG_SIZE-delta; - n -= delta; - small_memcpy(dest, src, delta); - } - i = n >> 6; /* n/64 */ - n&=63; - for (; i>0; i--) - { - __asm__ __volatile__ ( - "movq (%0), %%mm0;" - "movq 8(%0), %%mm1;" - "movq 16(%0), %%mm2;" - "movq 24(%0), %%mm3;" - "movq 32(%0), %%mm4;" - "movq 40(%0), %%mm5;" - "movq 48(%0), %%mm6;" - "movq 56(%0), %%mm7;" - "movq %%mm0, (%1);" - "movq %%mm1, 8(%1);" - "movq %%mm2, 16(%1);" - "movq %%mm3, 24(%1);" - "movq %%mm4, 32(%1);" - "movq %%mm5, 40(%1);" - "movq %%mm6, 48(%1);" - "movq %%mm7, 56(%1);" - :: "r" (src), "r" (dest) : "memory"); - src = ((const unsigned char *)src) + 64; - dest = ((unsigned char *)dest) + 64; - } - __asm__ __volatile__ ("emms":::"memory"); - } - /* - * Now do the tail of the block - */ - if (n) __memcpy(dest, src, n); - return retval; -#else - return cpu_cpy(dest, src, n); -#endif -} - -void *(*M_Memcpy)(void* dest, const void* src, size_t n) = cpu_cpy; - -/** Memcpy that uses MMX, 3DNow, MMXExt or even SSE - * Do not use on overlapped memory, use memmove for that - */ -void M_SetupMemcpy(void) -{ -#if defined (__GNUC__) && defined (__i386__) - if (R_SSE2) - M_Memcpy = sse_cpy; - else if (R_MMXExt) - M_Memcpy = mmx2_cpy; - else if (R_3DNow) - M_Memcpy = mmx1_cpy; - else -#endif - if (R_MMX) - M_Memcpy = mmx_cpy; -#if 0 - M_Memcpy = cpu_cpy; -#endif -} - /** Return the appropriate message for a file error or end of file. */ const char *M_FileError(FILE *fp) diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 151185932..b9948bdc0 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -65,7 +65,10 @@ static ps_metric_t ps_removecount = {0}; ps_metric_t ps_checkposition_calls = {0}; +ps_metric_t ps_lua_prethinkframe_time = {0}; ps_metric_t ps_lua_thinkframe_time = {0}; +ps_metric_t ps_lua_postthinkframe_time = {0}; + ps_metric_t ps_lua_mobjhooks = {0}; ps_metric_t ps_otherlogictime = {0}; @@ -157,7 +160,9 @@ perfstatrow_t gamelogic_rows[] = { {" mobjs ", " Mobjs: ", &ps_thlist_times[THINK_MOBJ], PS_TIME|PS_LEVEL}, {" dynslop", " Dynamic slopes: ", &ps_thlist_times[THINK_DYNSLOPE], PS_TIME|PS_LEVEL}, {" precip ", " Precipitation: ", &ps_thlist_times[THINK_PRECIP], PS_TIME|PS_LEVEL}, + {" lprethinkf", " LUAh_PreThinkFrame:", &ps_lua_prethinkframe_time, PS_TIME|PS_LEVEL}, {" lthinkf", " LUAh_ThinkFrame:", &ps_lua_thinkframe_time, PS_TIME|PS_LEVEL}, + {" lpostthinkf", " LUAh_PostThinkFrame:", &ps_lua_postthinkframe_time, PS_TIME|PS_LEVEL}, {" other ", " Other: ", &ps_otherlogictime, PS_TIME|PS_LEVEL}, {0} }; @@ -192,10 +197,43 @@ int ps_frame_index = 0; int ps_tick_index = 0; // dynamically allocated resizeable array for thinkframe hook stats +ps_hookinfo_t *prethinkframe_hooks = NULL; +int prethinkframe_hooks_length = 0; +int prethinkframe_hooks_capacity = 16; + ps_hookinfo_t *thinkframe_hooks = NULL; int thinkframe_hooks_length = 0; int thinkframe_hooks_capacity = 16; +ps_hookinfo_t *postthinkframe_hooks = NULL; +int postthinkframe_hooks_length = 0; +int postthinkframe_hooks_capacity = 16; + +void PS_SetPreThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) +{ + if (!prethinkframe_hooks) + { + // array needs to be initialized + prethinkframe_hooks = Z_Calloc(sizeof(ps_hookinfo_t) * prethinkframe_hooks_capacity, PU_STATIC, NULL); + } + if (index >= prethinkframe_hooks_capacity) + { + // array needs more space, realloc with double size + int new_capacity = prethinkframe_hooks_capacity * 2; + prethinkframe_hooks = Z_Realloc(prethinkframe_hooks, + sizeof(ps_hookinfo_t) * new_capacity, PU_STATIC, NULL); + // initialize new memory with zeros so the pointers in the structs are null + memset(&prethinkframe_hooks[prethinkframe_hooks_capacity], 0, + sizeof(ps_hookinfo_t) * prethinkframe_hooks_capacity); + prethinkframe_hooks_capacity = new_capacity; + } + prethinkframe_hooks[index].time_taken.value.p = time_taken; + memcpy(prethinkframe_hooks[index].short_src, short_src, LUA_IDSIZE * sizeof(char)); + // since the values are set sequentially from begin to end, the last call should leave + // the correct value to this variable + prethinkframe_hooks_length = index + 1; +} + void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) { if (!thinkframe_hooks) @@ -221,6 +259,31 @@ void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) thinkframe_hooks_length = index + 1; } +void PS_SetPostThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) +{ + if (!postthinkframe_hooks) + { + // array needs to be initialized + postthinkframe_hooks = Z_Calloc(sizeof(ps_hookinfo_t) * postthinkframe_hooks_capacity, PU_STATIC, NULL); + } + if (index >= postthinkframe_hooks_capacity) + { + // array needs more space, realloc with double size + int new_capacity = postthinkframe_hooks_capacity * 2; + postthinkframe_hooks = Z_Realloc(postthinkframe_hooks, + sizeof(ps_hookinfo_t) * new_capacity, PU_STATIC, NULL); + // initialize new memory with zeros so the pointers in the structs are null + memset(&postthinkframe_hooks[postthinkframe_hooks_capacity], 0, + sizeof(ps_hookinfo_t) * postthinkframe_hooks_capacity); + postthinkframe_hooks_capacity = new_capacity; + } + postthinkframe_hooks[index].time_taken.value.p = time_taken; + memcpy(postthinkframe_hooks[index].short_src, short_src, LUA_IDSIZE * sizeof(char)); + // since the values are set sequentially from begin to end, the last call should leave + // the correct value to this variable + postthinkframe_hooks_length = index + 1; +} + static boolean PS_HighResolution(void) { return (vid.width >= 640 && vid.height >= 400); @@ -564,7 +627,9 @@ void PS_UpdateTickStats(void) ps_tictime.value.p - ps_playerthink_time.value.p - ps_thinkertime.value.p - - ps_lua_thinkframe_time.value.p; + ps_lua_prethinkframe_time.value.p - + ps_lua_thinkframe_time.value.p - + ps_lua_postthinkframe_time.value.p; PS_CountThinkers(); } @@ -576,21 +641,35 @@ void PS_UpdateTickStats(void) PS_UpdateRowHistories(misc_calls_rows, false); } } - if (cv_perfstats.value == 3 && cv_ps_samplesize.value > 1 && PS_IsLevelActive()) + if (cv_ps_samplesize.value > 1) { - int i; - for (i = 0; i < thinkframe_hooks_length; i++) + if(cv_perfstats.value >= 3 && PS_IsLevelActive()) { - PS_UpdateMetricHistory(&thinkframe_hooks[i].time_taken, true, false, false); + int i; + if (cv_perfstats.value == 3) + { + for (i = 0; i < thinkframe_hooks_length; i++) + PS_UpdateMetricHistory(&thinkframe_hooks[i].time_taken, true, false, false); + } + else if (cv_perfstats.value == 4) + { + for (i = 0; i < prethinkframe_hooks_length; i++) + PS_UpdateMetricHistory(&prethinkframe_hooks[i].time_taken, true, false, false); + } + else if (cv_perfstats.value == 5) + { + for (i = 0; i < postthinkframe_hooks_length; i++) + PS_UpdateMetricHistory(&postthinkframe_hooks[i].time_taken, true, false, false); + } + } + if (cv_perfstats.value) + { + ps_tick_index++; + if (ps_tick_index >= cv_ps_samplesize.value) + ps_tick_index = 0; + if (ps_tick_samples_left) + ps_tick_samples_left--; } - } - if (cv_perfstats.value && cv_ps_samplesize.value > 1) - { - ps_tick_index++; - if (ps_tick_index >= cv_ps_samplesize.value) - ps_tick_index = 0; - if (ps_tick_samples_left) - ps_tick_samples_left--; } } @@ -610,7 +689,7 @@ static void PS_DrawDescriptorHeader(void) int samples_left = max(ps_frame_samples_left, ps_tick_samples_left); int x, y; - if (cv_perfstats.value == 3) + if (cv_perfstats.value >= 3) { x = 2; y = 0; @@ -697,7 +776,7 @@ static void PS_DrawGameLogicStats(void) PS_DrawPerfRows(x, y, V_PURPLEMAP, misc_calls_rows); } -static void PS_DrawThinkFrameStats(void) +static void draw_think_frame_stats(int hook_length, ps_hookinfo_t *hook) { char s[100]; int i; @@ -711,7 +790,7 @@ static void PS_DrawThinkFrameStats(void) PS_DrawDescriptorHeader(); - for (i = 0; i < thinkframe_hooks_length; i++) + for (i = 0; i < hook_length; i++) { #define NEXT_ROW() \ @@ -724,7 +803,7 @@ if (y > 192) \ break; \ } - char* str = thinkframe_hooks[i].short_src; + char* str = hook[i].short_src; char* tempstr = tempbuffer; int len = (int)strlen(str); char* str_ptr; @@ -771,7 +850,7 @@ if (y > 192) \ if (len > 20) str += len - 20; snprintf(s, sizeof s - 1, "%20s: %d", str, - PS_GetMetricScreenValue(&thinkframe_hooks[i].time_taken, true)); + PS_GetMetricScreenValue(&hook[i].time_taken, true)); V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | text_color, s); NEXT_ROW() @@ -780,6 +859,21 @@ if (y > 192) \ } } +static void PS_DrawPreThinkFrameStats(void) +{ + draw_think_frame_stats(prethinkframe_hooks_length, prethinkframe_hooks); +} + +static void PS_DrawThinkFrameStats(void) +{ + draw_think_frame_stats(thinkframe_hooks_length, thinkframe_hooks); +} + +static void PS_DrawPostThinkFrameStats(void) +{ + draw_think_frame_stats(postthinkframe_hooks_length, postthinkframe_hooks); +} + void M_DrawPerfStats(void) { if (cv_perfstats.value == 1) // rendering @@ -793,7 +887,7 @@ void M_DrawPerfStats(void) // tics when frame skips happen PS_DrawGameLogicStats(); } - else if (cv_perfstats.value == 3) // lua thinkframe + else if (cv_perfstats.value >= 3) // lua thinkframe { if (!PS_IsLevelActive()) return; @@ -802,13 +896,22 @@ void M_DrawPerfStats(void) // Low resolutions can't really use V_DrawSmallString that is used by thinkframe stats. // A low-res version using V_DrawThinString could be implemented, // but it would have much less space for information. - V_DrawThinString(80, 92, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Perfstats 3 is not available"); + V_DrawThinString(80, 92, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Lua Perfstats is not available"); V_DrawThinString(80, 100, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "for resolutions below 640x400."); + return; } - else + if (cv_perfstats.value == 3) { PS_DrawThinkFrameStats(); } + else if (cv_perfstats.value == 4) + { + PS_DrawPreThinkFrameStats(); + } + else if (cv_perfstats.value == 5) + { + PS_DrawPostThinkFrameStats(); + } } } diff --git a/src/m_perfstats.h b/src/m_perfstats.h index db250be2a..592ab31d2 100644 --- a/src/m_perfstats.h +++ b/src/m_perfstats.h @@ -43,12 +43,16 @@ extern ps_metric_t ps_thlist_times[]; extern ps_metric_t ps_checkposition_calls; +extern ps_metric_t ps_lua_prethinkframe_time; extern ps_metric_t ps_lua_thinkframe_time; +extern ps_metric_t ps_lua_postthinkframe_time; extern ps_metric_t ps_lua_mobjhooks; extern ps_metric_t ps_otherlogictime; +void PS_SetPreThinkFrameHookInfo(int index, precise_t time_taken, char* short_src); void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src); +void PS_SetPostThinkFrameHookInfo(int index, precise_t time_taken, char* short_src); void PS_UpdateTickStats(void); diff --git a/src/netcode/commands.c b/src/netcode/commands.c index 4b67198ba..e7d51437e 100644 --- a/src/netcode/commands.c +++ b/src/netcode/commands.c @@ -79,7 +79,7 @@ static void Ban_Clear(void) void Ban_Load_File(boolean warning) { FILE *f; - const char *address, *mask; + char *address, *mask; char buffer[MAX_WADPATH]; if (!I_ClearBans) @@ -100,6 +100,14 @@ void Ban_Load_File(boolean warning) { address = strtok(buffer, " \t\r\n"); mask = strtok(NULL, " \t\r\n"); + if (address[0] == '[') + { + size_t len; + address++; + len = strlen(address); + if (address[len-1] == ']') + address[len-1] = '\0'; + } I_SetBanAddress(address, mask); diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 7804b068f..f4251ef08 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -90,8 +90,8 @@ INT16 consistancy[BACKUPTICS]; // true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks boolean hu_stopped = false; -UINT8 adminpassmd5[16]; -boolean adminpasswordset = false; +UINT8 (*adminpassmd5)[16]; +UINT32 adminpasscount = 0; tic_t neededtic; SINT8 servernode = 0; // the number of the server node @@ -862,26 +862,31 @@ static void PT_Login(SINT8 node, INT32 netconsole) #ifndef NOMD5 UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ + UINT32 i; if (doomcom->datalength < 16)/* ignore partial sends */ return; - if (!adminpasswordset) + if (adminpasscount == 0) { CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); return; } - // Do the final pass to compare with the sent md5 - D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); - - if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) + for (i = 0; i < adminpasscount; i++) { - CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); - COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately + // Do the final pass to compare with the sent md5 + D_MD5PasswordPass(adminpassmd5[i], 16, va("PNUM%02d", netconsole), &finalmd5); + + if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) + { + CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); + COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately + return; + } } - else - CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); + + CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); #else (void)netconsole; #endif @@ -1275,6 +1280,7 @@ static void UpdatePingTable(void) } } +// Handle idle and disconnected player timers static void IdleUpdate(void) { INT32 i; @@ -1297,7 +1303,26 @@ static void IdleUpdate(void) } } else + { players[i].lastinputtime = 0; + + if (players[i].quittime && playeringame[i]) + { + players[i].quittime++; + + if (players[i].quittime == 30 * TICRATE && G_TagGametype()) + P_CheckSurvivors(); + + if (server && players[i].quittime >= (tic_t)FixedMul(cv_rejointimeout.value, 60 * TICRATE) + && !(players[i].quittime % TICRATE)) + { + if (D_NumNodes(true) > 0) + SendKick(i, KICK_MSG_PLAYER_QUIT); + else // If the server is empty, don't send a NetXCmd - that would wake an idling dedicated server + CL_RemovePlayer(i, KICK_MSG_PLAYER_QUIT); + } + } + } } } @@ -1512,7 +1537,7 @@ void NetUpdate(void) nowtime /= NEWTICRATERATIO; - if (nowtime > resptime) + if (nowtime != resptime) { resptime = nowtime; #ifdef HAVE_THREADS @@ -1618,6 +1643,20 @@ INT32 D_NumPlayers(void) return num; } +/** Returns the number of currently-connected nodes in a netgame. + * Not necessarily equivalent to D_NumPlayers() minus D_NumBots(). + * + * \param skiphost Skip the server's own node. + */ +INT32 D_NumNodes(boolean skiphost) +{ + INT32 num = 0; + for (INT32 ix = skiphost ? 1 : 0; ix < MAXNETNODES; ix++) + if (netnodes[ix].ingame) + num++; + return num; +} + /** Similar to the above, but counts only bots. * Purpose is to remove bots from both the player count and the * max player count on the server view diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index db9a780cd..61823e65d 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -121,14 +121,15 @@ extern char motd[254], server_context[8]; extern UINT8 playernode[MAXPLAYERS]; INT32 D_NumPlayers(void); +INT32 D_NumNodes(boolean skiphost); INT32 D_NumBots(void); tic_t GetLag(INT32 node); void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest); -extern UINT8 adminpassmd5[16]; -extern boolean adminpasswordset; +extern UINT8 (*adminpassmd5)[16]; +extern UINT32 adminpasscount; extern boolean hu_stopped; diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c index cfb1963b9..5a2e229d3 100644 --- a/src/netcode/d_net.c +++ b/src/netcode/d_net.c @@ -313,9 +313,9 @@ static void RemoveAck(INT32 i) } // We have got a packet, proceed the ack request and ack return -static boolean Processackpak(void) +static int Processackpak(void) { - boolean goodpacket = true; + int goodpacket = 0; node_t *node = &nodes[doomcom->remotenode]; // Received an ack return, so remove the ack in the list @@ -340,7 +340,7 @@ static boolean Processackpak(void) { DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack)); duppacket++; - goodpacket = false; // Discard packet (duplicate) + goodpacket = 1; // Discard packet (duplicate) } else { @@ -350,10 +350,10 @@ static boolean Processackpak(void) { DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack)); duppacket++; - goodpacket = false; // Discard packet (duplicate) + goodpacket = 1; // Discard packet (duplicate) break; } - if (goodpacket) + if (goodpacket == 0) { // Is a good packet so increment the acknowledge number, // Then search for a "hole" in the queue @@ -414,12 +414,13 @@ static boolean Processackpak(void) else // Buffer full discard packet, sender will resend it { // We can admit the packet but we will not detect the duplication after :( DEBFILE("no more freeackret\n"); - goodpacket = false; + goodpacket = 2; } } } } } + // return values: 0 = ok, 1 = duplicate, 2 = out of order return goodpacket; } @@ -1069,6 +1070,7 @@ boolean HGetPacket(void) while(true) { //nodejustjoined = I_NetGet(); + int goodpacket; I_NetGet(); if (doomcom->remotenode == -1) // No packet received @@ -1114,8 +1116,15 @@ boolean HGetPacket(void) }*/ // Proceed the ack and ackreturn field - if (!Processackpak()) + goodpacket = Processackpak(); + if (goodpacket != 0) + { + // resend the ACK in case the previous ACK didn't reach the client. + // prevents the client's netbuffer from locking up. + if (goodpacket == 1) + Net_SendAcks(doomcom->remotenode); continue; // discarded (duplicated) + } // A packet with just ackreturn if (netbuffer->packettype == PT_NOTHING) diff --git a/src/netcode/d_net.h b/src/netcode/d_net.h index 549f2b93c..2f83939f8 100644 --- a/src/netcode/d_net.h +++ b/src/netcode/d_net.h @@ -70,6 +70,7 @@ boolean HGetPacket(void); void D_SetDoomcom(void); boolean D_CheckNetGame(void); void D_CloseConnection(void); +boolean Net_IsNodeIPv6(INT32 node); void Net_UnAcknowledgePacket(INT32 node); void Net_CloseConnection(INT32 node); void Net_ConnectionTimeout(INT32 node); diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c index 9b87e8cfe..ec07c31f9 100644 --- a/src/netcode/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -150,6 +150,7 @@ static void Command_Clearscores_f(void); // Remote Administration static void Command_Changepassword_f(void); +static void Command_Clearpassword_f(void); static void Command_Login_f(void); static void Got_Verification(UINT8 **cp, INT32 playernum); static void Got_Removal(UINT8 **cp, INT32 playernum); @@ -379,7 +380,7 @@ consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_ consvar_t cv_sleep = CVAR_INIT ("cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL); static CV_PossibleValue_t perfstats_cons_t[] = { - {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {0, NULL}}; + {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {4, "PreThinkFrame"}, {5, "PostThinkFrame"}, {0, NULL}}; consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", CV_CALL, perfstats_cons_t, PS_PerfStats_OnChange); static CV_PossibleValue_t ps_samplesize_cons_t[] = { {1, "MIN"}, {1000, "MAX"}, {0, NULL}}; @@ -468,6 +469,7 @@ void D_RegisterServerCommands(void) // Remote Administration COM_AddCommand("password", Command_Changepassword_f, COM_LUA); + COM_AddCommand("clearpassword", Command_Clearpassword_f, COM_LUA); COM_AddCommand("login", Command_Login_f, COM_LUA); // useful in dedicated to kick off remote admin COM_AddCommand("promote", Command_Verify_f, COM_LUA); RegisterNetXCmd(XD_VERIFIED, Got_Verification); @@ -620,6 +622,10 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_addons_folder); CV_RegisterVar(&cv_dummyconsvar); + + CV_RegisterVar(&cv_chatspamprotection); + CV_RegisterVar(&cv_chatspamspeed); + CV_RegisterVar(&cv_chatspamburst); } // ========================================================================= @@ -763,7 +769,6 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_chatheight); CV_RegisterVar(&cv_chatwidth); CV_RegisterVar(&cv_chattime); - CV_RegisterVar(&cv_chatspamprotection); CV_RegisterVar(&cv_chatbacktint); CV_RegisterVar(&cv_consolechat); CV_RegisterVar(&cv_chatnotifications); @@ -1254,8 +1259,8 @@ static void SendNameAndColor(void) CV_StealthSetValue(&cv_playercolor, players[consoleplayer].skincolor); else if (skincolors[atoi(cv_playercolor.defaultvalue)].accessible) CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue); - else if (skins[players[consoleplayer].skin].prefcolor && skincolors[skins[players[consoleplayer].skin].prefcolor].accessible) - CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor); + else if (skins[players[consoleplayer].skin]->prefcolor && skincolors[skins[players[consoleplayer].skin]->prefcolor].accessible) + CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin]->prefcolor); else { UINT16 i = 0; while (iname)) return; players[consoleplayer].availabilities = R_GetSkinAvailabilities(); @@ -1308,7 +1313,7 @@ static void SendNameAndColor(void) if ((cv_skin.value < 0) || !R_SkinUsable(consoleplayer, cv_skin.value)) { INT32 defaultSkinNum = GetPlayerDefaultSkin(consoleplayer); - CV_StealthSet(&cv_skin, skins[defaultSkinNum].name); + CV_StealthSet(&cv_skin, skins[defaultSkinNum]->name); cv_skin.value = defaultSkinNum; } @@ -1339,8 +1344,8 @@ static void SendNameAndColor2(void) CV_StealthSetValue(&cv_playercolor2, players[secondplaya].skincolor); else if (skincolors[atoi(cv_playercolor2.defaultvalue)].accessible) CV_StealthSet(&cv_playercolor, cv_playercolor2.defaultvalue); - else if (skins[players[secondplaya].skin].prefcolor && skincolors[skins[players[secondplaya].skin].prefcolor].accessible) - CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor); + else if (skins[players[secondplaya].skin]->prefcolor && skincolors[skins[players[secondplaya].skin]->prefcolor].accessible) + CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin]->prefcolor); else { UINT16 i = 0; while (iprefcolor; } mapnumber = M_MapNumber(mapname[3], mapname[4]); @@ -2841,8 +2846,15 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) void D_SetPassword(const char *pw) { - D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &adminpassmd5); - adminpasswordset = true; + adminpassmd5 = Z_Realloc(adminpassmd5, sizeof(*adminpassmd5) * ++adminpasscount, PU_STATIC, NULL); + D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &adminpassmd5[adminpasscount-1]); +} + +void D_ClearPassword(void) +{ + Z_Free(adminpassmd5); + adminpassmd5 = NULL; + adminpasscount = 0; } // Remote Administration @@ -2860,12 +2872,30 @@ static void Command_Changepassword_f(void) if (COM_Argc() != 2) { - CONS_Printf(M_GetText("password : change remote admin password\n")); + CONS_Printf(M_GetText("password : add remote admin password\n")); return; } D_SetPassword(COM_Argv(1)); - CONS_Printf(M_GetText("Password set.\n")); + CONS_Printf(M_GetText("Password added.\n")); +#endif +} + +// Remote Administration +static void Command_Clearpassword_f(void) +{ +#ifdef NOMD5 + // If we have no MD5 support then completely disable XD_LOGIN responses for security. + CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n"); +#else + if (client) // cannot change remotely + { + CONS_Printf(M_GetText("Only the server can use this.\n")); + return; + } + + D_ClearPassword(); + CONS_Printf(M_GetText("Passwords cleared.\n")); #endif } @@ -3641,11 +3671,11 @@ static void Got_RequestAddfoldercmd(UINT8 **cp, INT32 playernum) static void Got_Addfilecmd(UINT8 **cp, INT32 playernum) { - char filename[241]; + char filename[MAX_WADPATH+1]; filestatus_t ncs = FS_NOTCHECKED; UINT8 md5sum[16]; - READSTRINGN(*cp, filename, 240); + READSTRINGN(*cp, filename, MAX_WADPATH); READMEM(*cp, md5sum, 16); if (playernum != serverplayer) @@ -4465,37 +4495,62 @@ static void Command_Mapmd5_f(void) CONS_Printf(M_GetText("You must be in a level to use this.\n")); } -void D_SendExitLevel(boolean cheat) -{ - UINT8 buf[8]; - UINT8 *buf_p = buf; - - WRITEUINT8(buf_p, cheat); - - SendNetXCmd(XD_EXITLEVEL, &buf, buf_p - buf); -} - static void Command_ExitLevel_f(void) { if (!(server || (IsPlayerAdmin(consoleplayer)))) CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demoplayback) CONS_Printf(M_GetText("You must be in a level to use this.\n")); + else if (usedCheats) + SendNetXCmd(XD_EXITLEVEL, NULL, 0); // Does it matter if it's a cheat if we've used one already? + else if (!(splitscreen || multiplayer || cv_debug)) + CONS_Printf(M_GetText("Cheats must be enabled to force a level exit in single player.\n")); else - D_SendExitLevel(true); + { + if (G_IsSpecialStage(gamemap) // If you wanna give up that emerald then go right ahead! + || gamestate == GS_CREDITS) // Somebody hasn't heard of the credits skip button... + { + SendNetXCmd(XD_EXITLEVEL, NULL, 0); + return; + } + + // Allow exiting without cheating if at least one player beat the level + // Consistent with just setting playersforexit to one + if (splitscreen || multiplayer) + { + INT32 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || players[i].bot) + continue; + if (players[i].quittime > 30 * TICRATE) + continue; + if (players[i].lives <= 0) + continue; + + if ((players[i].pflags & PF_FINISHED) || players[i].exiting) + { + SendNetXCmd(XD_EXITLEVEL, NULL, 0); + return; + } + } + } + + // Only consider it a cheat if we're not allowed to go to the next map + if (M_CampaignWarpIsCheat(gametype, G_GetNextMap(true, true) + 1, serverGamedata)) + CONS_Alert(CONS_NOTICE, M_GetText("Cheats must be enabled to force exit to a locked level!\n")); + else + SendNetXCmd(XD_EXITLEVEL, NULL, 0); + } } static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) { - boolean cheat = false; - - cheat = (boolean)READUINT8(*cp); + (void)cp; // Ignore duplicate XD_EXITLEVEL commands. if (gameaction == ga_completed) - { return; - } if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { @@ -4505,11 +4560,6 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) return; } - if (G_CoopGametype() && cheat) - { - G_SetUsedCheats(false); - } - G_ExitLevel(); } @@ -4713,7 +4763,7 @@ static void ForceSkin_OnChange(void) } else { - CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].name); + CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value]->name); if (Playing()) ForceAllSkins(cv_forceskin.value); } @@ -4759,7 +4809,7 @@ static void Skin_OnChange(void) if (!(cv_debug || devparm) && (gamestate != GS_WAITINGPLAYERS)) // allows command line -warp x +skin y { - CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name); + CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin]->name); return; } diff --git a/src/netcode/d_netcmd.h b/src/netcode/d_netcmd.h index 4849079d0..e30fa4a02 100644 --- a/src/netcode/d_netcmd.h +++ b/src/netcode/d_netcmd.h @@ -201,7 +201,6 @@ void D_SendPlayerConfig(void); void Command_ExitGame_f(void); void Command_Retry_f(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore -void D_SendExitLevel(boolean cheat); void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect); boolean IsPlayerAdmin(INT32 playernum); void SetAdminPlayer(INT32 playernum); @@ -209,6 +208,7 @@ void ClearAdminPlayers(void); void RemoveAdminPlayer(INT32 playernum); void ItemFinder_OnChange(void); void D_SetPassword(const char *pw); +void D_ClearPassword(void); // used for the player setup menu UINT8 CanChangeSkin(INT32 playernum); diff --git a/src/netcode/http-mserv.c b/src/netcode/http-mserv.c index f8bd5da21..10137e67b 100644 --- a/src/netcode/http-mserv.c +++ b/src/netcode/http-mserv.c @@ -35,6 +35,10 @@ Documentation available here. #define Blame( ... ) \ CONS_Printf("\x85" __VA_ARGS__) +#define PROTO_ANY 0 +#define PROTO_V4 1 +#define PROTO_V6 2 + static void MasterServer_Debug_OnChange (void); consvar_t cv_masterserver_timeout = CVAR_INIT @@ -59,12 +63,17 @@ consvar_t cv_masterserver_token = CVAR_INIT static int hms_started; +static boolean hms_allow_ipv6; + static char *hms_api; #ifdef HAVE_THREADS static I_mutex hms_api_mutex; #endif static char *hms_server_token; +#ifndef NO_IPV6 +static char *hms_server_token_ipv6; +#endif static char hms_useragent[512]; @@ -126,7 +135,7 @@ HMS_on_read (char *s, size_t _1, size_t n, void *userdata) } static struct HMS_buffer * -HMS_connect (const char *format, ...) +HMS_connect (int proto, const char *format, ...) { va_list ap; CURL *curl; @@ -136,8 +145,14 @@ HMS_connect (const char *format, ...) size_t token_length; struct HMS_buffer *buffer; +#ifdef NO_IPV6 + if (proto == PROTO_V6) + return NULL; +#endif + if (! hms_started) { + hms_allow_ipv6 = !M_CheckParm("-noipv6"); if (curl_global_init(CURL_GLOBAL_ALL) != 0) { Contact_error(); @@ -219,7 +234,9 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); #ifndef NO_IPV6 - if (M_CheckParm("-noipv6")) + if (proto == PROTO_V6) + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + if (proto == PROTO_V4) #endif curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); @@ -309,7 +326,7 @@ HMS_fetch_rooms (int joining, int query_id) (void)query_id; - hms = HMS_connect("rooms"); + hms = HMS_connect(PROTO_ANY, "rooms"); if (! hms) return 0; @@ -409,7 +426,7 @@ HMS_register (void) char *title; - hms = HMS_connect("rooms/%d/register", ms_RoomId); + hms = HMS_connect(PROTO_V4, "rooms/%d/register", ms_RoomId); if (! hms) return 0; @@ -441,6 +458,27 @@ HMS_register (void) HMS_end(hms); +#ifndef NO_IPV6 + if (!hms_allow_ipv6) + return ok; + + hms = HMS_connect(PROTO_V6, "rooms/%d/register", ms_RoomId); + + if (! hms) + return 0; + + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); + + ok = HMS_do(hms); + + if (ok) + { + hms_server_token_ipv6 = strdup(strtok(hms->buffer, "\n")); + } + + HMS_end(hms); +#endif + return ok; } @@ -450,7 +488,7 @@ HMS_unlist (void) struct HMS_buffer *hms; int ok; - hms = HMS_connect("servers/%s/unlist", hms_server_token); + hms = HMS_connect(PROTO_V4, "servers/%s/unlist", hms_server_token); if (! hms) return 0; @@ -462,6 +500,23 @@ HMS_unlist (void) free(hms_server_token); +#ifndef NO_IPV6 + if (hms_server_token_ipv6 && hms_allow_ipv6) + { + hms = HMS_connect(PROTO_V6, "servers/%s/unlist", hms_server_token_ipv6); + + if (! hms) + return 0; + + curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); + + ok = HMS_do(hms); + HMS_end(hms); + + free(hms_server_token_ipv6); + } +#endif + return ok; } @@ -475,7 +530,7 @@ HMS_update (void) char *title; - hms = HMS_connect("servers/%s/update", hms_server_token); + hms = HMS_connect(PROTO_V4, "servers/%s/update", hms_server_token); if (! hms) return 0; @@ -494,6 +549,21 @@ HMS_update (void) ok = HMS_do(hms); HMS_end(hms); +#ifndef NO_IPV6 + if (hms_server_token_ipv6 && hms_allow_ipv6) + { + hms = HMS_connect(PROTO_V6, "servers/%s/update", hms_server_token_ipv6); + + if (! hms) + return ok; + + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); + + ok = HMS_do(hms); + HMS_end(hms); + } +#endif + return ok; } @@ -505,7 +575,7 @@ HMS_list_servers (void) char *list; char *p; - hms = HMS_connect("servers"); + hms = HMS_connect(PROTO_ANY, "servers"); if (! hms) return; @@ -554,10 +624,10 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id) if (room_number > 0) { - hms = HMS_connect("rooms/%d/servers", room_number); + hms = HMS_connect(PROTO_ANY, "rooms/%d/servers", room_number); } else - hms = HMS_connect("servers"); + hms = HMS_connect(PROTO_ANY, "servers"); if (! hms) return NULL; @@ -661,7 +731,7 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size) char *version; char *version_name; - hms = HMS_connect("versions/%d", MODID); + hms = HMS_connect(PROTO_ANY, "versions/%d", MODID); if (! hms) return 0; diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c index 0b650de49..3fa230647 100644 --- a/src/netcode/i_tcp.c +++ b/src/netcode/i_tcp.c @@ -83,6 +83,10 @@ #undef ETIMEDOUT #endif #define ETIMEDOUT WSAETIMEDOUT + #ifdef EHOSTUNREACH + #undef EHOSTUNREACH + #endif + #define EHOSTUNREACH WSAEHOSTUNREACH #ifndef IOC_VENDOR #define IOC_VENDOR 0x18000000 #endif @@ -109,16 +113,18 @@ typedef union #endif } mysockaddr_t; -#ifdef HAVE_MINIUPNPC - #ifdef STATIC_MINIUPNPC - #define STATICLIB + #ifdef HAVE_MINIUPNPC + #ifdef MINIUPNP_STATICLIB + #include "miniwget.h" + #include "miniupnpc.h" + #include "upnpcommands.h" + #else + #include "miniupnpc/miniwget.h" + #include "miniupnpc/miniupnpc.h" + #include "miniupnpc/upnpcommands.h" #endif - #include "miniupnpc/miniwget.h" - #include "miniupnpc/miniupnpc.h" - #include "miniupnpc/upnpcommands.h" - #undef STATICLIB - static UINT8 UPNP_support = TRUE; -#endif // HAVE_MINIUPNC + static boolean UPNP_support = true; + #endif // HAVE_MINIUPNC #define MAXBANS 100 @@ -256,21 +262,24 @@ static const char* inet_ntopA(short af, const void *cp, char *buf, socklen_t len #endif #ifdef HAVE_MINIUPNPC // based on old XChat patch +static void I_ShutdownUPnP(void); static struct UPNPUrls urls; static struct IGDdatas data; static char lanaddr[64]; -static void I_ShutdownUPnP(void) -{ - FreeUPNPUrls(&urls); -} - static inline void I_InitUPnP(void) { + const char * const deviceTypes[] = { + "urn:schemas-upnp-org:device:InternetGatewayDevice:2", + "urn:schemas-upnp-org:device:InternetGatewayDevice:1", + 0 + }; struct UPNPDev * devlist = NULL; int upnp_error = -2; + int scope_id = 0; + int status_code = 0; CONS_Printf(M_GetText("Looking for UPnP Internet Gateway Device\n")); - devlist = upnpDiscover(2000, NULL, NULL, 0, false, &upnp_error); + devlist = upnpDiscoverDevices(deviceTypes, 500, NULL, NULL, 0, false, 2, &upnp_error, 0); if (devlist) { struct UPNPDev *dev = devlist; @@ -290,7 +299,7 @@ static inline void I_InitUPnP(void) UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); CONS_Printf(M_GetText("Local LAN IP address: %s\n"), lanaddr); - descXML = miniwget(dev->descURL, &descXMLsize); + descXML = miniwget(dev->descURL, &descXMLsize, scope_id, &status_code); if (descXML) { parserootdesc(descXML, descXMLsize, &data); @@ -298,7 +307,7 @@ static inline void I_InitUPnP(void) descXML = NULL; memset(&urls, 0, sizeof(struct UPNPUrls)); memset(&data, 0, sizeof(struct IGDdatas)); - GetUPNPUrls(&urls, &data, dev->descURL); + GetUPNPUrls(&urls, &data, dev->descURL, status_code); I_AddExitFunc(I_ShutdownUPnP); } freeUPNPDevlist(devlist); @@ -326,6 +335,12 @@ static inline void I_UPnP_rem(const char *port, const char * servicetype) UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port, servicetype, NULL); } + +static void I_ShutdownUPnP(void) +{ + I_UPnP_rem(serverport_name, "UDP"); + FreeUPNPUrls(&urls); +} #endif static const char *SOCK_AddrToStr(mysockaddr_t *sk) @@ -404,6 +419,20 @@ static const char *SOCK_GetBanMask(size_t ban) return NULL; } +#ifdef HAVE_IPV6 +static boolean SOCK_cmpipv6(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) +{ + UINT8 bitmask; + I_Assert(mask <= 128); + if (memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, mask / 8) != 0) + return false; + if (mask % 8 == 0) + return true; + bitmask = 255 << (mask % 8); + return (a->ip6.sin6_addr.s6_addr[mask / 8] & bitmask) == (b->ip6.sin6_addr.s6_addr[mask / 8] & bitmask); +} +#endif + static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { UINT32 bitmask = INADDR_NONE; @@ -416,7 +445,7 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) && (b->ip4.sin_port == 0 || (a->ip4.sin_port == b->ip4.sin_port)); #ifdef HAVE_IPV6 else if (b->any.sa_family == AF_INET6) - return !memcmp(&a->ip6.sin6_addr, &b->ip6.sin6_addr, sizeof(b->ip6.sin6_addr)) + return SOCK_cmpipv6(a, b, mask) && (b->ip6.sin6_port == 0 || (a->ip6.sin6_port == b->ip6.sin6_port)); #endif else @@ -678,7 +707,7 @@ static void SOCK_Send(void) if (c == ERRSOCKET) { int e = errno; // save error code so it can't be modified later - if (e != ECONNREFUSED && e != EWOULDBLOCK) + if (e != ECONNREFUSED && e != EWOULDBLOCK && e != EHOSTUNREACH) I_Error("SOCK_Send, error sending to node %d (%s) #%u: %s", doomcom->remotenode, SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e)); } @@ -1070,10 +1099,10 @@ boolean I_InitTcpDriver(void) { I_AddExitFunc(I_ShutdownTcpDriver); #ifdef HAVE_MINIUPNPC - if (M_CheckParm("-useUPnP")) - I_InitUPnP(); - else + if (M_CheckParm("-noUPnP")) UPNP_support = false; + else + I_InitUPnP(); #endif } return init_tcp_driver; @@ -1362,4 +1391,13 @@ boolean I_InitTcpNetwork(void) return ret; } +boolean Net_IsNodeIPv6(INT32 node) +{ +#ifdef NO_IPV6 + return false; +#else + return clientaddress[node].any.sa_family == AF_INET6; +#endif +} + #include "i_addrinfo.c" diff --git a/src/netcode/mserv.c b/src/netcode/mserv.c index 1c7f3e08d..3acacd24c 100644 --- a/src/netcode/mserv.c +++ b/src/netcode/mserv.c @@ -60,7 +60,7 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { {0,NULL} }; -consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange); +consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://ds.ms.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange); consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Update_parameters); consvar_t cv_masterserver_update_rate = CVAR_INIT ("masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters); @@ -501,7 +501,7 @@ static void Update_parameters (void) { #ifdef MASTERSERVER - int registered; + int registered = 0; int delayed; if (Online()) @@ -539,6 +539,13 @@ static void MasterServer_OnChange(void) CV_StealthSet(&cv_masterserver, cv_masterserver.defaultvalue); } + if ( + ! cv_masterserver.changed && + strcmp(cv_masterserver.string, "https://mb.srb2.org/MS/0") == 0 + ){ + CV_StealthSet(&cv_masterserver, cv_masterserver.defaultvalue); + } + Set_api(cv_masterserver.string); if (Online()) diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index 2164f411a..376700f0d 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -109,7 +109,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); // Exclude bots from both counts - netbuffer->u.serverinfo.numberofplayer = (UINT8)(D_NumPlayers() - D_NumBots()); + netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumNodes(dedicated); netbuffer->u.serverinfo.maxplayer = (UINT8)(cv_maxplayers.value - D_NumBots()); netbuffer->u.serverinfo.refusereason = GetRefuseReason(node); @@ -164,7 +164,7 @@ static void SV_SendPlayerInfo(INT32 node) for (UINT8 i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i]) + if (playernode[i] == UINT8_MAX || !netnodes[playernode[i]].ingame) { netbuffer->u.playerinfo[i].num = 255; // This slot is empty. continue; diff --git a/src/p_enemy.c b/src/p_enemy.c index 93c828fbe..1073fd491 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -1201,7 +1201,8 @@ static void P_SharpDust(mobj_t *actor, mobjtype_t type, angle_t ang) -P_ReturnThrustX(actor, ang, 16<angle, actor->radius), actor->height/3, MT_PARTICLE); + if (P_MobjWasRemoved(flume)) + return; + flume->destscale = actor->scale*3; P_SetScale(flume, flume->destscale); P_SetTarget(&flume->target, actor); @@ -1329,12 +1333,15 @@ void A_FaceStabHurl(mobj_t *actor) { if (!hwork->hnext) P_SetTarget(&hwork->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_FACESTABBERSPEAR)); - hwork = hwork->hnext; - hwork->angle = actor->angle + ANGLE_90; - hwork->destscale = FixedSqrt(step*basesize); - P_SetScale(hwork, hwork->destscale); - hwork->fuse = 2; - P_MoveOrigin(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<hnext)) + { + hwork = hwork->hnext; + hwork->angle = actor->angle + ANGLE_90; + hwork->destscale = FixedSqrt(step*basesize); + P_SetScale(hwork, hwork->destscale); + hwork->fuse = 2; + P_MoveOrigin(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<info->raisestate); + if (P_MobjWasRemoved(newchain)) + continue; P_SetTarget(&prevchain->target, newchain); prevchain = newchain; } @@ -2335,10 +2346,13 @@ static void P_VultureHoverParticle(mobj_t *actor) fixed_t pz = P_FloorzAtPos(px, py, actor->z, actor->height); dust = P_SpawnMobj(px, py, pz, MT_ARIDDUST); - P_SetMobjState(dust, (statenum_t)(dust->state - states + P_RandomRange(0, 2))); - P_Thrust(dust, angle, FixedDiv(12*FRACUNIT, max(FRACUNIT, fdist/2))); - dust->momx += actor->momx; - dust->momy += actor->momy; + if (!P_MobjWasRemoved(dust)) + { + P_SetMobjState(dust, (statenum_t)(dust->state - states + P_RandomRange(0, 2))); + P_Thrust(dust, angle, FixedDiv(12*FRACUNIT, max(FRACUNIT, fdist/2))); + dust->momx += actor->momx; + dust->momy += actor->momy; + } angle += ANGLE_45; } } @@ -2436,6 +2450,8 @@ void A_VultureBlast(mobj_t *actor) { angle_t fa = ((i*(angle_t)ANGLE_45) >> ANGLETOFINESHIFT) & FINEMASK; dust = P_SpawnMobj(actor->x + 48*FixedMul(FINECOSINE(fa), -faasin), actor->y + 48*FixedMul(FINECOSINE(fa), faacos), actor->z + actor->height/2 + 48*FINESINE(fa), MT_PARTICLE); + if (P_MobjWasRemoved(dust)) + continue; P_SetScale(dust, 4*FRACUNIT); dust->destscale = FRACUNIT; @@ -2505,10 +2521,13 @@ void A_VultureFly(mobj_t *actor) P_VultureHoverParticle(actor); dust = P_SpawnMobj(actor->x + P_RandomFixed() - FRACUNIT/2, actor->y + P_RandomFixed() - FRACUNIT/2, actor->z + actor->height/2 + P_RandomFixed() - FRACUNIT/2, MT_PARTICLE); - P_SetScale(dust, 2*FRACUNIT); - dust->destscale = FRACUNIT/3; - dust->scalespeed = FRACUNIT/40; - dust->fuse = TICRATE*2; + if (!P_MobjWasRemoved(dust)) + { + P_SetScale(dust, 2*FRACUNIT); + dust->destscale = FRACUNIT/3; + dust->scalespeed = FRACUNIT/40; + dust->fuse = TICRATE*2; + } actor->momx += FixedDiv(dx, dm)*2; actor->momy += FixedDiv(dy, dm)*2; @@ -2709,6 +2728,8 @@ void A_LobShot(mobj_t *actor) z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale); shot = P_SpawnMobj(actor->x, actor->y, z, locvar1); + if (P_MobjWasRemoved(shot)) + return; if (actor->type == MT_BLACKEGGMAN) { @@ -3098,15 +3119,21 @@ void A_Boss1Laser(mobj_t *actor) S_StartSound(actor, mobjinfo[locvar1].seesound); point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET); - point->angle = actor->angle; - point->fuse = dur+1; - P_SetTarget(&point->target, actor->target); - P_SetTarget(&actor->target, point); + if (!P_MobjWasRemoved(point)) + { + point->angle = actor->angle; + point->fuse = dur+1; + P_SetTarget(&point->target, actor->target); + P_SetTarget(&actor->target, point); + } } angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y)); point = P_SpawnMobj(x, y, z, locvar1); + if (P_MobjWasRemoved(point)) + return; + P_SetTarget(&point->target, actor); point->angle = actor->angle; speed = point->radius; @@ -3117,6 +3144,9 @@ void A_Boss1Laser(mobj_t *actor) for (i = 0; i < 256; i++) { mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type); + if (P_MobjWasRemoved(mo)) + continue; + mo->angle = point->angle; mo->color = LASERCOLORS[((UINT8)(i + 3*dur) >> 2) % sizeof(LASERCOLORS)]; // codeing P_UnsetThingPosition(mo); @@ -3129,9 +3159,12 @@ void A_Boss1Laser(mobj_t *actor) if (mo->info->meleestate) { mobj_t *mo2 = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_PARTICLE); - mo2->flags2 |= MF2_LINKDRAW; - P_SetTarget(&mo2->tracer, actor); - P_SetMobjState(mo2, mo->info->meleestate); + if (!P_MobjWasRemoved(mo2)) + { + mo2->flags2 |= MF2_LINKDRAW; + P_SetTarget(&mo2->tracer, actor); + P_SetMobjState(mo2, mo->info->meleestate); + } } } @@ -3149,37 +3182,42 @@ void A_Boss1Laser(mobj_t *actor) if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1 && dur & 1) { point = P_SpawnMobj(x, y, floorz, MT_EGGMOBILE_FIRE); - point->angle = actor->angle; - point->destscale = actor->scale; - P_SetScale(point, point->destscale); - P_SetTarget(&point->target, actor); - P_MobjCheckWater(point); - if (point->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) + if (!P_MobjWasRemoved(point)) { - for (i = 0; i < 2; i++) + point->angle = actor->angle; + point->destscale = actor->scale; + P_SetScale(point, point->destscale); + P_SetTarget(&point->target, actor); + P_MobjCheckWater(point); + if (point->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) { - UINT8 size = 3; - mobj_t *steam = P_SpawnMobj(x, y, point->watertop - size*mobjinfo[MT_DUST].height, MT_DUST); - P_SetScale(steam, size*actor->scale); - P_SetObjectMomZ(steam, FRACUNIT + 2*P_RandomFixed(), true); - P_InstaThrust(steam, FixedAngle(P_RandomKey(360)*FRACUNIT), 2*P_RandomFixed()); - if (point->info->painsound) - S_StartSound(steam, point->info->painsound); - } - } - else - { - fixed_t distx = P_ReturnThrustX(point, point->angle, point->radius); - fixed_t disty = P_ReturnThrustY(point, point->angle, point->radius); - if (P_TryMove(point, point->x + distx, point->y + disty, false) // prevents the sprite from clipping into the wall or dangling off ledges - && P_TryMove(point, point->x - 2*distx, point->y - 2*disty, false) - && P_TryMove(point, point->x + distx, point->y + disty, false)) - { - if (point->info->seesound) - S_StartSound(point, point->info->seesound); + for (i = 0; i < 2; i++) + { + UINT8 size = 3; + mobj_t *steam = P_SpawnMobj(x, y, point->watertop - size*mobjinfo[MT_DUST].height, MT_DUST); + if (P_MobjWasRemoved(steam)) + continue; + P_SetScale(steam, size*actor->scale); + P_SetObjectMomZ(steam, FRACUNIT + 2*P_RandomFixed(), true); + P_InstaThrust(steam, FixedAngle(P_RandomKey(360)*FRACUNIT), 2*P_RandomFixed()); + if (point->info->painsound) + S_StartSound(steam, point->info->painsound); + } } else - P_RemoveMobj(point); + { + fixed_t distx = P_ReturnThrustX(point, point->angle, point->radius); + fixed_t disty = P_ReturnThrustY(point, point->angle, point->radius); + if (P_TryMove(point, point->x + distx, point->y + disty, false) // prevents the sprite from clipping into the wall or dangling off ledges + && P_TryMove(point, point->x - 2*distx, point->y - 2*disty, false) + && P_TryMove(point, point->x + distx, point->y + disty, false)) + { + if (point->info->seesound) + S_StartSound(point, point->info->seesound); + } + else + P_RemoveMobj(point); + } } } @@ -3528,6 +3566,8 @@ void A_BossScream(mobj_t *actor) z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale); mo = P_SpawnMobj(x, y, z, explodetype); + if (P_MobjWasRemoved(mo)) + return; if (actor->eflags & MFE_VERTICALFLIP) mo->flags2 |= MF2_OBJECTFLIP; mo->destscale = actor->scale; @@ -3637,7 +3677,7 @@ void A_1upThinker(mobj_t *actor) } } - if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0) + if (closestplayer == -1 || skins[players[closestplayer].skin]->sprites[SPR2_LIFE].numframes == 0) { // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite. if (actor->tracer) { @@ -3654,8 +3694,11 @@ void A_1upThinker(mobj_t *actor) if (!actor->tracer) { P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY)); + if (P_MobjWasRemoved(actor->tracer)) + return; + P_SetTarget(&actor->tracer->target, actor); - actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame + actor->tracer->skin = skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame P_SetMobjState(actor->tracer, actor->info->seestate); // The overlay is going to be one tic early turning off and on @@ -3665,7 +3708,7 @@ void A_1upThinker(mobj_t *actor) } actor->tracer->color = players[closestplayer].mo->color; - actor->tracer->skin = &skins[players[closestplayer].skin]; + actor->tracer->skin = skins[players[closestplayer].skin]; } // Function: A_MonitorPop @@ -3715,31 +3758,41 @@ void A_MonitorPop(mobj_t *actor) return; } - newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item); - P_SetTarget(&newmobj->target, actor->target); // Transfer target - if (item == MT_1UP_ICON) { if (actor->tracer) // Remove the old lives icon. P_RemoveMobj(actor->tracer); + } - if (!newmobj->target - || !newmobj->target->player - || !newmobj->target->skin - || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) - {} // No lives icon for this player, use the default. - else - { // Spawn the lives icon. - mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&livesico->target, newmobj); - P_SetTarget(&newmobj->tracer, livesico); + newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item); + if (!P_MobjWasRemoved(newmobj)) + { + P_SetTarget(&newmobj->target, actor->target); // Transfer target - livesico->color = newmobj->target->player->mo->color; - livesico->skin = &skins[newmobj->target->player->skin]; - P_SetMobjState(livesico, newmobj->info->seestate); - // We're using the overlay, so use the overlay 1up sprite (no text) - newmobj->sprite = SPR_TV1P; + if (item == MT_1UP_ICON) + { + if (!newmobj->target + || !newmobj->target->player + || !newmobj->target->skin + || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) + {} // No lives icon for this player, use the default. + else + { // Spawn the lives icon. + mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); + if (!P_MobjWasRemoved(livesico)) + { + P_SetTarget(&livesico->target, newmobj); + P_SetTarget(&newmobj->tracer, livesico); + + livesico->color = newmobj->target->player->mo->color; + livesico->skin = skins[newmobj->target->player->skin]; + P_SetMobjState(livesico, newmobj->info->seestate); + } + + // We're using the overlay, so use the overlay 1up sprite (no text) + newmobj->sprite = SPR_TV1P; + } } } @@ -3802,30 +3855,35 @@ void A_GoldMonitorPop(mobj_t *actor) // Note: the icon spawns 1 fracunit higher newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item); - P_SetTarget(&newmobj->target, actor->target); // Transfer target - - if (item == MT_1UP_ICON) + if (!P_MobjWasRemoved(newmobj)) { - if (actor->tracer) // Remove the old lives icon. - P_RemoveMobj(actor->tracer); + P_SetTarget(&newmobj->target, actor->target); // Transfer target + if (item == MT_1UP_ICON) + { + if (actor->tracer) // Remove the old lives icon. + P_RemoveMobj(actor->tracer); - if (!newmobj->target - || !newmobj->target->player - || !newmobj->target->skin - || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) - {} // No lives icon for this player, use the default. - else - { // Spawn the lives icon. - mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&livesico->target, newmobj); - P_SetTarget(&newmobj->tracer, livesico); + if (!newmobj->target + || !newmobj->target->player + || !newmobj->target->skin + || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0) + {} // No lives icon for this player, use the default. + else + { // Spawn the lives icon. + mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY); + if (!P_MobjWasRemoved(livesico)) + { + P_SetTarget(&livesico->target, newmobj); + P_SetTarget(&newmobj->tracer, livesico); - livesico->color = newmobj->target->player->mo->color; - livesico->skin = &skins[newmobj->target->player->skin]; - P_SetMobjState(livesico, newmobj->info->seestate); + livesico->color = newmobj->target->player->mo->color; + livesico->skin = skins[newmobj->target->player->skin]; + P_SetMobjState(livesico, newmobj->info->seestate); + } - // We're using the overlay, so use the overlay 1up sprite (no text) - newmobj->sprite = SPR_TV1P; + // We're using the overlay, so use the overlay 1up sprite (no text) + newmobj->sprite = SPR_TV1P; + } } } @@ -3870,7 +3928,13 @@ void A_GoldMonitorSparkle(mobj_t *actor) yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS); for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2) - P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false); + { + mobj_t *sparkle = P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE); + if (P_MobjWasRemoved(sparkle)) + continue; + + P_SetObjectMomZ(sparkle, i, false); + } } // Function: A_Explode @@ -3992,56 +4056,73 @@ static void P_DoBossVictory(mobj_t *mo) static void P_SpawnBoss1Junk(mobj_t *mo) { mobj_t *mo2 = P_SpawnMobjFromMobj(mo, - P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; - P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - P_SetMobjState(mo2, S_BOSSEGLZ1); + P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; + P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSEGLZ1); + } mo2 = P_SpawnMobjFromMobj(mo, P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<angle + ANGLE_90, 32<angle = mo->angle; - P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - P_SetMobjState(mo2, S_BOSSEGLZ2); + if (!P_MobjWasRemoved(mo2)) + { + mo2->angle = mo->angle; + P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSEGLZ2); + } } static void P_SpawnBoss2Junk(mobj_t *mo) { mobj_t *mo2 = P_SpawnMobjFromMobj(mo, - P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; - P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - P_SetMobjState(mo2, S_BOSSTANK1); + P_ReturnThrustX(mo, mo->angle - ANGLE_90, 32<angle - ANGLE_90, 32<angle = mo->angle; + P_InstaThrust(mo2, mo2->angle - ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSTANK1); + } mo2 = P_SpawnMobjFromMobj(mo, P_ReturnThrustX(mo, mo->angle + ANGLE_90, 32<angle + ANGLE_90, 32<angle = mo->angle; - P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - P_SetMobjState(mo2, S_BOSSTANK2); + if (!P_MobjWasRemoved(mo2)) + { + mo2->angle = mo->angle; + P_InstaThrust(mo2, mo2->angle + ANGLE_90, 4*mo2->scale); + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + P_SetMobjState(mo2, S_BOSSTANK2); + } mo2 = P_SpawnMobjFromMobj(mo, 0, 0, mobjinfo[MT_EGGMOBILE2].height + (32<angle = mo->angle; - P_SetObjectMomZ(mo2, 4*FRACUNIT, false); - mo2->momz += mo->momz; - P_SetMobjState(mo2, S_BOSSSPIGOT); + if (!P_MobjWasRemoved(mo2)) + { + mo2->angle = mo->angle; + P_SetObjectMomZ(mo2, 4*FRACUNIT, false); + mo2->momz += mo->momz; + P_SetMobjState(mo2, S_BOSSSPIGOT); + } } static void P_SpawnBoss3Junk(mobj_t *mo) { mobj_t *mo2 = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_BOSSJUNK); + if (P_MobjWasRemoved(mo2)) + return; mo2->angle = mo->angle; P_SetMobjState(mo2, S_BOSSSEBH1); } @@ -4081,26 +4162,36 @@ static void P_DoBoss5Death(mobj_t *mo) mo->tracer->y - P_ReturnThrustY(mo->tracer, mo->tracer->angle, speed*time), mo->tracer->floorz + (256+1)*FRACUNIT, MT_FSGNB); - P_SetTarget(&pole->tracer, P_SpawnMobj( - pole->x, pole->y, - pole->z - 256*FRACUNIT, - MT_FSGNB)); - P_SetTarget(&pole->tracer->tracer, P_SpawnMobj( - pole->x + P_ReturnThrustX(pole, mo->tracer->angle, FRACUNIT), - pole->y + P_ReturnThrustY(pole, mo->tracer->angle, FRACUNIT), - pole->z + 256*FRACUNIT, - MT_FSGNA)); - pole->tracer->flags |= MF_NOCLIPTHING; - P_SetScale(pole, (pole->destscale = 2*FRACUNIT)); - P_SetScale(pole->tracer, (pole->tracer->destscale = 2*FRACUNIT)); - pole->angle = pole->tracer->angle = mo->tracer->angle; - pole->tracer->tracer->angle = pole->angle - ANGLE_90; - pole->momx = P_ReturnThrustX(pole, pole->angle, speed); - pole->momy = P_ReturnThrustY(pole, pole->angle, speed); - pole->tracer->momx = pole->momx; - pole->tracer->momy = pole->momy; - pole->tracer->tracer->momx = pole->momx; - pole->tracer->tracer->momy = pole->momy; + if (!P_MobjWasRemoved(pole)) + { + P_SetScale(pole, (pole->destscale = 2*FRACUNIT)); + pole->momx = P_ReturnThrustX(pole, pole->angle, speed); + pole->momy = P_ReturnThrustY(pole, pole->angle, speed); + P_SetTarget(&pole->tracer, P_SpawnMobj( + pole->x, pole->y, + pole->z - 256*FRACUNIT, + MT_FSGNB)); + if (!P_MobjWasRemoved(pole->tracer)) + { + pole->tracer->flags |= MF_NOCLIPTHING; + P_SetScale(pole->tracer, (pole->tracer->destscale = 2*FRACUNIT)); + pole->angle = pole->tracer->angle = mo->tracer->angle; + pole->tracer->momx = pole->momx; + pole->tracer->momy = pole->momy; + + P_SetTarget(&pole->tracer->tracer, P_SpawnMobj( + pole->x + P_ReturnThrustX(pole, mo->tracer->angle, FRACUNIT), + pole->y + P_ReturnThrustY(pole, mo->tracer->angle, FRACUNIT), + pole->z + 256*FRACUNIT, + MT_FSGNA)); + if (!P_MobjWasRemoved(pole->tracer->tracer)) + { + pole->tracer->tracer->angle = pole->angle - ANGLE_90; + pole->tracer->tracer->momx = pole->momx; + pole->tracer->tracer->momy = pole->momy; + } + } + } } } else @@ -4760,9 +4851,12 @@ void A_AttractChase(mobj_t *actor) { mobj_t *newring; newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime); - newring->momx = actor->momx; - newring->momy = actor->momy; - newring->momz = actor->momz; + if (!P_MobjWasRemoved(newring)) + { + newring->momx = actor->momx; + newring->momy = actor->momy; + newring->momz = actor->momz; + } P_RemoveMobj(actor); return; } @@ -4842,9 +4936,12 @@ void A_DropMine(mobj_t *actor) // Use raisestate instead of MT_MINE mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate); - if (actor->eflags & MFE_VERTICALFLIP) - mine->eflags |= MFE_VERTICALFLIP; - mine->momz = actor->momz + actor->pmomz; + if (!P_MobjWasRemoved(mine)) + { + if (actor->eflags & MFE_VERTICALFLIP) + mine->eflags |= MFE_VERTICALFLIP; + mine->momz = actor->momz + actor->pmomz; + } S_StartSound(actor, actor->info->attacksound); } @@ -5169,11 +5266,13 @@ void A_SignSpin(mobj_t *actor) if (actor->tracer == NULL || P_MobjWasRemoved(actor->tracer)) return; for (i = -1; i < 2; i += 2) { - P_SpawnMobjFromMobj(actor, + mobj_t *spinmobj = P_SpawnMobjFromMobj(actor, P_ReturnThrustX(actor, actor->tracer->angle, i * actor->radius), P_ReturnThrustY(actor, actor->tracer->angle, i * actor->radius), (actor->eflags & MFE_VERTICALFLIP) ? 0 : actor->height, - actor->info->painchance)->destscale >>= 1; + actor->info->painchance); + if (!P_MobjWasRemoved(spinmobj)) + spinmobj->destscale >>= 1; } } @@ -5233,8 +5332,11 @@ void A_SignPlayer(mobj_t *actor) if (actor->tracer->tracer == NULL || P_MobjWasRemoved(actor->tracer->tracer)) { ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); - P_SetTarget(&ov->target, actor->tracer); - P_SetTarget(&actor->tracer->tracer, ov); + if (!P_MobjWasRemoved(ov)) + { + P_SetTarget(&ov->target, actor->tracer); + P_SetTarget(&actor->tracer->tracer, ov); + } } else ov = actor->tracer->tracer; @@ -5247,7 +5349,7 @@ void A_SignPlayer(mobj_t *actor) if (!actor->target->player) return; - skin = &skins[actor->target->player->skin]; + skin = skins[actor->target->player->skin]; facecolor = P_GetPlayerColor(actor->target->player); if (signcolor) @@ -5278,10 +5380,10 @@ void A_SignPlayer(mobj_t *actor) if (!SignSkinCheck(player, skincount)) skinnum++; } - skin = &skins[skinnum]; + skin = skins[skinnum]; } else // specific skin - skin = &skins[locvar1]; + skin = skins[locvar1]; facecolor = skin->prefcolor; if (signcolor) @@ -5294,33 +5396,36 @@ void A_SignPlayer(mobj_t *actor) signcolor = skincolors[facecolor].invcolor; } - if (skin) - { - if (skin->sprites[SPR2_SIGN].numframes) // player face + if (!P_MobjWasRemoved(ov)) + { + if (skin) { - ov->color = facecolor; - ov->skin = skin; - if ((statenum_t)(ov->state-states) != actor->info->seestate) - P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN + if (skin->sprites[SPR2_SIGN].numframes) // player face + { + ov->color = facecolor; + ov->skin = skin; + if ((statenum_t)(ov->state-states) != actor->info->seestate) + P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN + } + else // CLEAR! sign + { + ov->color = SKINCOLOR_NONE; + ov->skin = NULL; // needs to be NULL in the case of SF_HIRES characters + if ((statenum_t)(ov->state-states) != actor->info->missilestate) + P_SetMobjState(ov, actor->info->missilestate); // S_CLEARSIGN + } } - else // CLEAR! sign + else // Eggman face { ov->color = SKINCOLOR_NONE; - ov->skin = NULL; // needs to be NULL in the case of SF_HIRES characters - if ((statenum_t)(ov->state-states) != actor->info->missilestate) - P_SetMobjState(ov, actor->info->missilestate); // S_CLEARSIGN + ov->skin = NULL; + if ((statenum_t)(ov->state-states) != actor->info->meleestate) + P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN + if (!signcolor) + signcolor = SKINCOLOR_CARBON; + facecolor = signcolor; } - } - else // Eggman face - { - ov->color = SKINCOLOR_NONE; - ov->skin = NULL; - if ((statenum_t)(ov->state-states) != actor->info->meleestate) - P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN - if (!signcolor) - signcolor = SKINCOLOR_CARBON; - facecolor = signcolor; - } + } actor->tracer->color = signcolor; if (signcolor && signcolor < numskincolors) @@ -5511,12 +5616,14 @@ void A_JetbThink(mobj_t *actor) // use raisestate instead of MT_MINE bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<scale), (mobjtype_t)actor->info->raisestate); - - P_SetTarget(&bomb->target, actor); - bomb->destscale = actor->scale; - P_SetScale(bomb, actor->scale); - actor->reactiontime = TICRATE; // one second - S_StartSound(actor, actor->info->attacksound); + if (!P_MobjWasRemoved(bomb)) + { + P_SetTarget(&bomb->target, actor); + bomb->destscale = actor->scale; + P_SetScale(bomb, actor->scale); + actor->reactiontime = TICRATE; // one second + S_StartSound(actor, actor->info->attacksound); + } } } else if (((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul((32<scale) + actor->height) > actor->ceilingz)) @@ -5668,9 +5775,12 @@ void A_MinusDigging(mobj_t *actor) } par = P_SpawnMobj(actor->x, actor->y, mz, MT_MINUSDIRT); - if (actor->eflags & MFE_VERTICALFLIP) - par->eflags |= MFE_VERTICALFLIP; - P_TryMove(par, x, y, false); + if (!P_MobjWasRemoved(par)) + { + if (actor->eflags & MFE_VERTICALFLIP) + par->eflags |= MFE_VERTICALFLIP; + P_TryMove(par, x, y, false); + } // If close enough, prepare to attack if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < actor->radius*2) @@ -5681,6 +5791,8 @@ void A_MinusDigging(mobj_t *actor) // Spawn growing dirt pile. par = P_SpawnMobj(actor->x, actor->y, mz, MT_MINUSDIRT); + if (P_MobjWasRemoved(par)) + return; P_SetMobjState(par, actor->info->raisestate); P_SetScale(par, actor->scale*2); if (actor->eflags & MFE_VERTICALFLIP) @@ -5751,6 +5863,8 @@ void A_MinusPopup(mobj_t *actor) for (i = 1; i <= num; i++) { mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1); + if (P_MobjWasRemoved(rock)) + continue; P_Thrust(rock, ani*i, FRACUNIT); P_SetObjectMomZ(rock, 3*FRACUNIT, false); P_SetScale(rock, rock->scale/3); @@ -5788,6 +5902,8 @@ void A_MinusCheck(mobj_t *actor) for (i = 1; i <= num; i++) { mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1); + if (P_MobjWasRemoved(rock)) + continue; P_Thrust(rock, ani*i, FRACUNIT); P_SetObjectMomZ(rock, 3*FRACUNIT, false); P_SetScale(rock, rock->scale/3); @@ -6299,6 +6415,8 @@ void A_RockSpawn(mobj_t *actor) dist += actor->spawnpoint->args[2] ? P_RandomByte() * (FRACUNIT/32) : 0; // random oomph mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK); + if (P_MobjWasRemoved(mo)) + return; P_SetMobjState(mo, mobjinfo[type].spawnstate); mo->angle = FixedAngle(actor->spawnpoint->angle << FRACBITS); @@ -6335,6 +6453,8 @@ void A_SlingAppear(mobj_t *actor) actor->friction = 128; hprev = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLGRABCHAIN); + if (P_MobjWasRemoved(hprev)) + return; P_SetTarget(&hprev->tracer, actor); P_SetTarget(&hprev->hprev, actor); P_SetTarget(&actor->hnext, hprev); @@ -6346,13 +6466,16 @@ void A_SlingAppear(mobj_t *actor) while (mlength > 0) { spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN); - P_SetTarget(&spawnee->tracer, actor); - P_SetTarget(&spawnee->hprev, hprev); - P_SetTarget(&hprev->hnext, spawnee); - hprev = spawnee; + if (!P_MobjWasRemoved(spawnee)) + { + P_SetTarget(&spawnee->tracer, actor); + P_SetTarget(&spawnee->hprev, hprev); + P_SetTarget(&hprev->hnext, spawnee); + hprev = spawnee; - spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; - spawnee->movecount = mlength; + spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT; + spawnee->movecount = mlength; + } mlength--; } @@ -6612,6 +6735,8 @@ void A_OldRingExplode(mobj_t *actor) { const angle_t fa = (i*FINEANGLES/16) & FINEMASK; mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1); + if (P_MobjWasRemoved(mo)) + continue; P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points mo->momx = FixedMul(FINECOSINE(fa),ns); @@ -6638,33 +6763,37 @@ void A_OldRingExplode(mobj_t *actor) { } mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1); - - P_SetTarget(&mo->target, actor->target); - mo->momz = ns; - mo->flags2 |= MF2_DEBRIS; - mo->fuse = TICRATE/5; - - if (changecolor) + if (!P_MobjWasRemoved(mo)) { - if (!(gametyperules & GTR_TEAMS)) - mo->color = actor->target->color; //copy color - else if (actor->target->player->ctfteam == 2) - mo->color = skincolor_bluering; + P_SetTarget(&mo->target, actor->target); + mo->momz = ns; + mo->flags2 |= MF2_DEBRIS; + mo->fuse = TICRATE/5; + + if (changecolor) + { + if (!(gametyperules & GTR_TEAMS)) + mo->color = actor->target->color; //copy color + else if (actor->target->player->ctfteam == 2) + mo->color = skincolor_bluering; + } } mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1); - - P_SetTarget(&mo->target, actor->target); - mo->momz = -ns; - mo->flags2 |= MF2_DEBRIS; - mo->fuse = TICRATE/5; - - if (changecolor) + if (!P_MobjWasRemoved(mo)) { - if (!(gametyperules & GTR_TEAMS)) - mo->color = actor->target->color; //copy color - else if (actor->target->player->ctfteam == 2) - mo->color = skincolor_bluering; + P_SetTarget(&mo->target, actor->target); + mo->momz = -ns; + mo->flags2 |= MF2_DEBRIS; + mo->fuse = TICRATE/5; + + if (changecolor) + { + if (!(gametyperules & GTR_TEAMS)) + mo->color = actor->target->color; //copy color + else if (actor->target->player->ctfteam == 2) + mo->color = skincolor_bluering; + } } } @@ -7300,23 +7429,26 @@ void A_Boss2Chase(mobj_t *actor) fa = (actor->movedir*FINEANGLES/8) & FINEMASK; goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance); - goop->momx = FixedMul(FINECOSINE(fa),ns); - goop->momy = FixedMul(FINESINE(fa),ns); - goop->momz = FixedMul(4*FRACUNIT, actor->scale); - goop->fuse = 10*TICRATE; - - if (actor->info->attacksound) - S_StartAttackSound(actor, actor->info->attacksound); - - if (P_RandomChance(FRACUNIT/2)) + if (!P_MobjWasRemoved(goop)) { - goop->momx *= 2; - goop->momy *= 2; - } - else if (P_RandomChance(129*FRACUNIT/256)) - { - goop->momx *= 3; - goop->momy *= 3; + goop->momx = FixedMul(FINECOSINE(fa),ns); + goop->momy = FixedMul(FINESINE(fa),ns); + goop->momz = FixedMul(4*FRACUNIT, actor->scale); + goop->fuse = 10*TICRATE; + + if (actor->info->attacksound) + S_StartAttackSound(actor, actor->info->attacksound); + + if (P_RandomChance(FRACUNIT/2)) + { + goop->momx *= 2; + goop->momy *= 2; + } + else if (P_RandomChance(129*FRACUNIT/256)) + { + goop->momx *= 3; + goop->momy *= 3; + } } actor->flags2 |= MF2_JUSTATTACKED; @@ -7357,6 +7489,8 @@ void A_Boss2Pogo(mobj_t *actor) fa = (actor->movedir*FINEANGLES/8) & FINEMASK; goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance); + if (P_MobjWasRemoved(goop)) + continue; goop->momx = FixedMul(FINECOSINE(fa),ns); goop->momy = FixedMul(FINESINE(fa),ns); goop->momz = FixedMul(4*FRACUNIT, actor->scale); @@ -7685,7 +7819,8 @@ void A_Boss2PogoTarget(mobj_t *actor) if (actor->info->missilestate) // spawn the pogo stick collision box { mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate); - P_SetTarget(&pogo->target, actor); + if (!P_MobjWasRemoved(pogo)) + P_SetTarget(&pogo->target, actor); } actor->reactiontime = 1; @@ -8176,6 +8311,8 @@ void A_Boss1Spikeballs(mobj_t *actor) return; ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL); + if (P_MobjWasRemoved(ball)) + return; P_SetTarget(&ball->target, actor); ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<threshold = ball->radius + actor->radius + ball->info->painchance; @@ -8363,19 +8500,22 @@ void A_Boss3ShockThink(mobj_t *actor) snew = P_SpawnMobj((x0 >> 1) + (x1 >> 1), (y0 >> 1) + (y1 >> 1), (actor->z >> 1) + (snext->z >> 1), actor->type); - snew->momx = (actor->momx + snext->momx) >> 1; - snew->momy = (actor->momy + snext->momy) >> 1; - snew->momz = (actor->momz + snext->momz) >> 1; // is this really needed? - snew->angle = (actor->angle + snext->angle) >> 1; - P_SetTarget(&snew->target, actor->target); - snew->fuse = actor->fuse; + if (!P_MobjWasRemoved(snew)) + { + snew->momx = (actor->momx + snext->momx) >> 1; + snew->momy = (actor->momy + snext->momy) >> 1; + snew->momz = (actor->momz + snext->momz) >> 1; // is this really needed? + snew->angle = (actor->angle + snext->angle) >> 1; + P_SetTarget(&snew->target, actor->target); + snew->fuse = actor->fuse; - P_SetScale(snew, actor->scale); - snew->destscale = actor->destscale; - snew->scalespeed = actor->scalespeed; + P_SetScale(snew, actor->scale); + snew->destscale = actor->destscale; + snew->scalespeed = actor->scalespeed; - P_SetTarget(&actor->hnext, snew); - P_SetTarget(&snew->hnext, snext); + P_SetTarget(&actor->hnext, snew); + P_SetTarget(&snew->hnext, snext); + } } } } @@ -8560,10 +8700,13 @@ void A_SmokeTrailer(mobj_t *actor) if (actor->eflags & MFE_VERTICALFLIP) { th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1); - th->flags2 |= MF2_OBJECTFLIP; + if (!P_MobjWasRemoved(th)) + th->flags2 |= MF2_OBJECTFLIP; } else th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1); + if (P_MobjWasRemoved(th)) + return; P_SetObjectMomZ(th, FRACUNIT, false); th->destscale = actor->scale; P_SetScale(th, actor->scale); @@ -8600,6 +8743,8 @@ void A_SpawnObjectAbsolute(mobj_t *actor) type = (mobjtype_t)(locvar2&65535); mo = P_SpawnMobj(x<angle = actor->angle; @@ -8642,6 +8787,8 @@ void A_SpawnObjectRelative(mobj_t *actor) mo = P_SpawnMobj(actor->x + FixedMul(x<scale), actor->y + FixedMul(y<scale), (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), type); + if (P_MobjWasRemoved(mo)) + return; // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn mo->angle = actor->angle; @@ -9303,12 +9450,15 @@ void A_BossJetFume(mobj_t *actor) jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale); filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->fuse = 56; + if (!P_MobjWasRemoved(filler)) + { + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->fuse = 56; + } if (actor->eflags & MFE_VERTICALFLIP) jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale); @@ -9318,22 +9468,28 @@ void A_BossJetFume(mobj_t *actor) filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), jetz, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->fuse = 57; + if (!P_MobjWasRemoved(filler)) + { + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->fuse = 57; + } filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)), jetz, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->fuse = 58; + if (!P_MobjWasRemoved(filler)) + { + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->fuse = 58; + } P_SetTarget(&actor->tracer, filler); } @@ -9361,14 +9517,17 @@ void A_BossJetFume(mobj_t *actor) else if (locvar1 == 2) // Metal Sonic jet fumes { filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1); - P_SetTarget(&filler->target, actor); - filler->fuse = 59; - P_SetTarget(&actor->tracer, filler); - P_SetScale(filler, (filler->destscale = actor->scale/3)); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; - filler->color = SKINCOLOR_ICY; - filler->colorized = true; + if (!P_MobjWasRemoved(filler)) + { + P_SetTarget(&filler->target, actor); + filler->fuse = 59; + P_SetTarget(&actor->tracer, filler); + P_SetScale(filler, (filler->destscale = actor->scale/3)); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + filler->color = SKINCOLOR_ICY; + filler->colorized = true; + } } else if (locvar1 == 3) // Boss 4 jet flame { @@ -9378,12 +9537,15 @@ void A_BossJetFume(mobj_t *actor) else jetz = actor->z - 50*actor->scale; filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME); - P_SetTarget(&filler->target, actor); - // Boss 4 already uses its tracer for other things - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; + if (!P_MobjWasRemoved(filler)) + { + P_SetTarget(&filler->target, actor); + // Boss 4 already uses its tracer for other things + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + } } else if (locvar1 == 4) // Boss 4 Spectator Eggrobo jet flame { @@ -9398,12 +9560,15 @@ void A_BossJetFume(mobj_t *actor) jetx = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, movefactor*actor->scale) - P_ReturnThrustX(actor, actor->angle, 19*actor->scale); jety = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, movefactor*actor->scale) - P_ReturnThrustY(actor, actor->angle, 19*actor->scale); filler = P_SpawnMobj(jetx, jety, jetz, MT_EGGROBO1JET); - filler->movefactor = movefactor; - P_SetTarget(&filler->target, actor); - filler->destscale = actor->scale; - P_SetScale(filler, filler->destscale); - if (actor->eflags & MFE_VERTICALFLIP) - filler->flags2 |= MF2_OBJECTFLIP; + if (!P_MobjWasRemoved(filler)) + { + filler->movefactor = movefactor; + P_SetTarget(&filler->target, actor); + filler->destscale = actor->scale; + P_SetScale(filler, filler->destscale); + if (actor->eflags & MFE_VERTICALFLIP) + filler->flags2 |= MF2_OBJECTFLIP; + } if (movefactor <= 0) break; movefactor = -movefactor; @@ -9780,7 +9945,7 @@ void A_SetObjectState(mobj_t *actor) if (!target->player) P_SetMobjState(target, locvar1); else - P_SetPlayerMobjState(target, locvar1); + P_SetMobjState(target, locvar1); } } @@ -11110,6 +11275,8 @@ void A_TrapShot(mobj_t *actor) type, frontoff, (INT16)(locvar2 & 65535), vertoff); missile = P_SpawnMobj(x, y, z, type); + if (P_MobjWasRemoved(missile)) + return; if (actor->eflags & MFE_VERTICALFLIP) missile->flags2 |= MF2_OBJECTFLIP; @@ -11182,18 +11349,21 @@ void A_VileTarget(mobj_t *actor) actor->target->y, actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0), fogtype); - if (actor->target->eflags & MFE_VERTICALFLIP) + if (!P_MobjWasRemoved(fog)) { - fog->eflags |= MFE_VERTICALFLIP; - fog->flags2 |= MF2_OBJECTFLIP; - } - fog->destscale = actor->target->scale; - P_SetScale(fog, fog->destscale); + if (actor->target->eflags & MFE_VERTICALFLIP) + { + fog->eflags |= MFE_VERTICALFLIP; + fog->flags2 |= MF2_OBJECTFLIP; + } + fog->destscale = actor->target->scale; + P_SetScale(fog, fog->destscale); - P_SetTarget(&actor->tracer, fog); - P_SetTarget(&fog->target, actor); - P_SetTarget(&fog->tracer, actor->target); - A_VileFire(fog); + P_SetTarget(&actor->tracer, fog); + P_SetTarget(&fog->target, actor); + P_SetTarget(&fog->tracer, actor->target); + A_VileFire(fog); + } } else { @@ -11213,19 +11383,22 @@ void A_VileTarget(mobj_t *actor) players[i].mo->y, players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0), fogtype); - if (players[i].mo->eflags & MFE_VERTICALFLIP) + if (!P_MobjWasRemoved(fog)) { - fog->eflags |= MFE_VERTICALFLIP; - fog->flags2 |= MF2_OBJECTFLIP; - } - fog->destscale = players[i].mo->scale; - P_SetScale(fog, fog->destscale); + if (players[i].mo->eflags & MFE_VERTICALFLIP) + { + fog->eflags |= MFE_VERTICALFLIP; + fog->flags2 |= MF2_OBJECTFLIP; + } + fog->destscale = players[i].mo->scale; + P_SetScale(fog, fog->destscale); - if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now - P_SetTarget(&actor->tracer, fog); - P_SetTarget(&fog->target, actor); - P_SetTarget(&fog->tracer, players[i].mo); - A_VileFire(fog); + if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now + P_SetTarget(&actor->tracer, fog); + P_SetTarget(&fog->target, actor); + P_SetTarget(&fog->tracer, players[i].mo); + A_VileFire(fog); + } } } } @@ -11702,6 +11875,8 @@ void A_BrakLobShot(mobj_t *actor) typeOfShot = MT_CANNONBALL; else typeOfShot = (mobjtype_t)locvar1; shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot); + if (P_MobjWasRemoved(shot)) + return; if (shot->info->seesound) S_StartSound(shot, shot->info->seesound); P_SetTarget(&shot->target, actor); // where it came from @@ -11768,6 +11943,8 @@ void A_NapalmScatter(mobj_t *actor) const angle_t fa = (i*FINEANGLES/numToShoot) & FINEMASK; mo = P_SpawnMobj(actor->x, actor->y, actor->z, typeOfShot); + if (P_MobjWasRemoved(mo)) + continue; P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot mo->angle = fa << ANGLETOFINESHIFT; @@ -11793,6 +11970,8 @@ void A_SpawnFreshCopy(mobj_t *actor) return; newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type); + if (P_MobjWasRemoved(newObject)) + return; newObject->flags2 = actor->flags2 & MF2_AMBUSH; newObject->angle = actor->angle; newObject->color = actor->color; @@ -11828,6 +12007,8 @@ mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz } flicky = P_SpawnMobjFromMobj(actor, offsx, offsy, 0, flickytype); + if (P_MobjWasRemoved(flicky)) + return NULL; flicky->angle = actor->angle; if (flickytype == MT_SEED) @@ -11939,8 +12120,11 @@ void A_FlickyCenter(mobj_t *actor) if (!actor->tracer) { mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false, 0); - P_SetTarget(&flicky->target, actor); - P_SetTarget(&actor->tracer, flicky); + if (!P_MobjWasRemoved(flicky)) + { + P_SetTarget(&flicky->target, actor); + P_SetTarget(&actor->tracer, flicky); + } if (actor->spawnpoint) { @@ -11975,18 +12159,21 @@ void A_FlickyCenter(mobj_t *actor) locvar1 = flickytype; } - if (actor->flags & MF_GRENADEBOUNCE) // in-place - actor->tracer->fuse = 0; - else if (actor->flags & MF_SLIDEME) // aimless + if (!P_MobjWasRemoved(flicky)) { - actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly. - actor->tracer->angle = P_RandomKey(180)*ANG2; - } - else //orbit - actor->tracer->fuse = FRACUNIT; + if (actor->flags & MF_GRENADEBOUNCE) // in-place + actor->tracer->fuse = 0; + else if (actor->flags & MF_SLIDEME) // aimless + { + actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly. + actor->tracer->angle = P_RandomKey(180)*ANG2; + } + else //orbit + actor->tracer->fuse = FRACUNIT; - if (locvar1 == MT_FLICKY_08) - P_InternalFlickySetColor(actor->tracer, actor->extravalue2); + if (locvar1 == MT_FLICKY_08) + P_InternalFlickySetColor(actor->tracer, actor->extravalue2); + } actor->extravalue2 = 0; } @@ -11997,7 +12184,8 @@ void A_FlickyCenter(mobj_t *actor) fixed_t originy = actor->movefactor; fixed_t originz = actor->watertop; - actor->tracer->fuse = FRACUNIT; + if (!P_MobjWasRemoved(actor->tracer)) + actor->tracer->fuse = FRACUNIT; // Impose default home radius if flicky orbits around player if (!actor->extravalue1) @@ -12029,6 +12217,8 @@ void P_InternalFlickyBubble(mobj_t *actor) return; overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY); + if (P_MobjWasRemoved(overlay)) + return; P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate); P_SetTarget(&actor->tracer, overlay); P_SetTarget(&overlay->target, actor); @@ -12403,7 +12593,8 @@ void A_FlameParticle(mobj_t *actor) P_RandomRange(rad, -rad)<frame = actor->frame; if (!(locvar1 & 1)) @@ -12589,6 +12782,8 @@ void A_MineExplode(mobj_t *actor) actor->y+P_RandomRange(-dist, dist)*FRACUNIT, actor->z+P_RandomRange(((actor->eflags & MFE_UNDERWATER) ? -dist : 0), dist)*FRACUNIT, type); + if (P_MobjWasRemoved(b)) + continue; fixed_t dx = b->x - actor->x, dy = b->y - actor->y, dz = b->z - actor->z; fixed_t dm = P_AproxDistance(dz, P_AproxDistance(dy, dx)); b->momx = FixedDiv(dx, dm)*3; @@ -12720,6 +12915,8 @@ void A_SpawnParticleRelative(mobj_t *actor) mo = P_SpawnMobj(actor->x + FixedMul(x<scale), actor->y + FixedMul(y<scale), (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), MT_PARTICLE); + if (P_MobjWasRemoved(mo)) + return; // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn mo->angle = actor->angle; @@ -13114,11 +13311,14 @@ void A_DoNPCSkid(mobj_t *actor) if (!(leveltime % 3)) { mobj_t *particle = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_SPINDUST); - particle->tics = 10; + if (!P_MobjWasRemoved(particle)) + { + particle->tics = 10; - P_SetScale(particle, 2*actor->scale/3); - particle->destscale = actor->scale; - P_SetObjectMomZ(particle, FRACUNIT, false); + P_SetScale(particle, 2*actor->scale/3); + particle->destscale = actor->scale; + P_SetObjectMomZ(particle, FRACUNIT, false); + } } } @@ -13421,11 +13621,18 @@ void A_Boss5MakeJunk(mobj_t *actor) if (actor->extravalue2 > 10) { mobj_t *front = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_VWREF); - broked = P_SpawnMobjFromMobj(front, 0, 0, 0, MT_VWREB); - front->z = broked->z = front->z - broked->height; - P_SetObjectMomZ(front, (4<momz = front->momz; - broked->fuse = front->fuse = (actor->height+(2*front->height))/front->momz; + if (!P_MobjWasRemoved(front)) + { + P_SetObjectMomZ(front, (4<z -= broked->height; + broked->z = front->z; + broked->momz = front->momz; + broked->fuse = front->fuse = (actor->height+(2*front->height))/front->momz; + } + } } if (!(actor->colorized = !actor->colorized)) actor->frame |= FF_FULLBRIGHT; @@ -13439,6 +13646,8 @@ void A_Boss5MakeJunk(mobj_t *actor) while (i--) { broked = P_SpawnMobjFromMobj(actor, 0, 0, FRACUNIT, MT_BROKENROBOT); + if (P_MobjWasRemoved(broked)) + continue; if (locvar2 & 2) broked->fuse = TICRATE; else @@ -13456,13 +13665,17 @@ void A_Boss5MakeJunk(mobj_t *actor) if (locvar2 & 2) { broked = P_SpawnMobjFromMobj(actor, 0, 0, 64<fuse = states[S_FANG_INTRO12].tics+10; - P_SetMobjState(broked, S_ALART1); + if (!P_MobjWasRemoved(broked)) + { + S_StartSound(broked, sfx_alart); + broked->fuse = states[S_FANG_INTRO12].tics+10; + P_SetMobjState(broked, S_ALART1); + } } else if (locvar2 & 1) { - broked->z += broked->momz; + if (!P_MobjWasRemoved(broked)) + broked->z += broked->momz; S_StartSound(actor, sfx_s3kccs); actor->flags &= ~MF_NOCLIPTHING; } @@ -13521,6 +13734,8 @@ static void P_DustRing(mobjtype_t mobjtype, UINT32 div, fixed_t x, fixed_t y, fi z, mobjtype ); + if (P_MobjWasRemoved(dust)) + continue; dust->angle = ang*i + ANGLE_90; P_SetScale(dust, FixedMul(initscale, scale)); @@ -13609,7 +13824,7 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing) player->powers[pw_carry] = CR_DUSTDEVIL; player->powers[pw_nocontrol] = 2; P_SetTarget(&thing->tracer, dustdevil); - P_SetPlayerMobjState(thing, S_PLAY_PAIN); + P_SetMobjState(thing, S_PLAY_PAIN); if (dist > dragamount) { @@ -13632,7 +13847,7 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing) player->powers[pw_nocontrol] = 0; P_SetTarget(&thing->tracer, NULL); S_StartSound(thing, sfx_wdjump); - P_SetPlayerMobjState(thing, S_PLAY_FALL); + P_SetMobjState(thing, S_PLAY_FALL); } thing->momz = thrust; @@ -13673,9 +13888,12 @@ void A_DustDevilThink(mobj_t *actor) if (P_IsObjectOnGround(actor)) { angle_t dustang = ((P_RandomRange(0, 7)*ANGLE_45)>>ANGLETOFINESHIFT) & FINEMASK; mobj_t *dust = P_SpawnMobj(actor->x + 96 * FixedMul(scale, FINECOSINE(dustang)), actor->y + 96 * FixedMul(scale, FINESINE(dustang)), actor->z, MT_ARIDDUST); - P_SetMobjState(dust, dust->info->spawnstate + P_RandomRange(0, 2)); - dust->destscale = scale * 3; - P_SetScale(dust, dust->destscale); + if (!P_MobjWasRemoved(dust)) + { + P_SetMobjState(dust, dust->info->spawnstate + P_RandomRange(0, 2)); + dust->destscale = scale * 3; + P_SetScale(dust, dust->destscale); + } } actor->extravalue1++; @@ -13691,6 +13909,8 @@ void A_DustDevilThink(mobj_t *actor) fixed_t pz = actor->z; layer = P_SpawnMobj(px, py, pz, MT_DUSTLAYER); + if (P_MobjWasRemoved(layer)) + continue; layer->momz = 5 * scale; layer->angle = ANGLE_90 + ANGLE_90*i; layer->extravalue1 = TICRATE * 3; @@ -13876,6 +14096,8 @@ void A_DebrisRandom(mobj_t *actor) static mobj_t *P_TrainSeg(mobj_t *src, fixed_t x, fixed_t y, fixed_t z, angle_t ang, spritenum_t spr, UINT32 frame) { mobj_t *s = P_SpawnMobj(x, y, z, MT_TRAINSEG); + if (P_MobjWasRemoved(s)) + return NULL; s->fuse = 16*TICRATE; s->sprite = spr; s->frame = frame|FF_PAPERSPRITE; @@ -14028,7 +14250,7 @@ static void P_SnapperLegPlace(mobj_t *mo) // Move as many legs as available. seg = seg->tracer; - do + while (seg) { o1 = seg->extravalue1; o2 = seg->extravalue2; @@ -14053,7 +14275,7 @@ static void P_SnapperLegPlace(mobj_t *mo) seg->angle = R_PointToAngle2(mo->x, mo->y, seg->x, seg->y); seg = seg->tracer; - } while (seg); + } } // @@ -14077,6 +14299,12 @@ void A_SnapperSpawn(mobj_t *actor) // It spawns 1 head. seg = P_SpawnMobjFromMobj(actor, 0, 0, 0, headtype); + if (P_MobjWasRemoved(seg)) + { + // if we can't spawn the head, don't spawn the snapper at all + P_RemoveMobj(actor); + return; + } P_SetTarget(&ptr->tracer, seg); ptr = seg; @@ -14084,6 +14312,8 @@ void A_SnapperSpawn(mobj_t *actor) for (i = 1; i <= 4; i++) { seg = P_SpawnMobjFromMobj(actor, 0, 0, 0, legtype); + if (P_MobjWasRemoved(seg)) + continue; P_SetTarget(&ptr->tracer, seg); ptr = seg; @@ -14185,7 +14415,8 @@ void A_SnapperThinker(mobj_t *actor) if (actor->reactiontime < 4) { mobj_t *dust = P_SpawnMobj(x0, y0, actor->z, MT_SPINDUST); - P_Thrust(dust, ang + ANGLE_180 + FixedAngle(P_RandomRange(-20, 20)*FRACUNIT), speed*FRACUNIT); + if (!P_MobjWasRemoved(dust)) + P_Thrust(dust, ang + ANGLE_180 + FixedAngle(P_RandomRange(-20, 20)*FRACUNIT), speed*FRACUNIT); } if (actor->extravalue2 == 0) @@ -14295,6 +14526,8 @@ void A_MinecartSparkThink(mobj_t *actor) for (i = 1; i <= 8; i++) { mobj_t *trail = P_SpawnMobj(actor->x - dx*i, actor->y - dy*i, actor->z - dz*i, MT_PARTICLE); + if (P_MobjWasRemoved(trail)) + continue; trail->tics = 2; trail->sprite = actor->sprite; P_SetScale(trail, trail->scale/4); @@ -14380,7 +14613,8 @@ void A_LavafallLava(mobj_t *actor) return; lavafall = P_SpawnMobjFromMobj(actor, 0, 0, -8*FRACUNIT, MT_LAVAFALL_LAVA); - lavafall->momz = -P_MobjFlip(actor)*25*FRACUNIT; + if (!P_MobjWasRemoved(lavafall)) + lavafall->momz = -P_MobjFlip(actor)*25*FRACUNIT; } // Function: A_FallingLavaCheck @@ -14455,9 +14689,18 @@ void A_SpawnPterabytes(mobj_t *actor) c = FINECOSINE(fa); s = FINESINE(fa); waypoint = P_SpawnMobjFromMobj(actor, FixedMul(c, rad), FixedMul(s, rad), 0, MT_PTERABYTEWAYPOINT); + if (P_MobjWasRemoved(waypoint)) + continue; + waypoint->angle = ang + ANGLE_90; P_SetTarget(&waypoint->tracer, actor); ptera = P_SpawnMobjFromMobj(waypoint, 0, 0, 0, MT_PTERABYTE); + if (P_MobjWasRemoved(ptera)) + { + P_RemoveMobj(waypoint); + continue; + } + ptera->angle = waypoint->angle; P_SetTarget(&ptera->tracer, waypoint); ptera->extravalue1 = 0; @@ -14505,14 +14748,17 @@ void A_RolloutSpawn(mobj_t *actor) || P_MobjWasRemoved(actor->target) || P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) > locvar1) { - actor->target = P_SpawnMobj(actor->x, actor->y, actor->z, locvar2); - actor->target->flags2 |= (actor->flags2 & (MF2_AMBUSH | MF2_OBJECTFLIP)) | MF2_SLIDEPUSH; - actor->target->eflags |= (actor->eflags & MFE_VERTICALFLIP); - - if (actor->target->flags2 & MF2_AMBUSH) + P_SetTarget(&actor->target, P_SpawnMobj(actor->x, actor->y, actor->z, locvar2)); + if (!P_MobjWasRemoved(actor->target)) { - actor->target->color = SKINCOLOR_SUPERRUST3; - actor->target->colorized = true; + actor->target->flags2 |= (actor->flags2 & (MF2_AMBUSH | MF2_OBJECTFLIP)) | MF2_SLIDEPUSH; + actor->target->eflags |= (actor->eflags & MFE_VERTICALFLIP); + + if (actor->target->flags2 & MF2_AMBUSH) + { + actor->target->color = SKINCOLOR_SUPERRUST3; + actor->target->colorized = true; + } } } } @@ -14633,6 +14879,8 @@ void A_DragonbomberSpawn(mobj_t *actor) x = P_ReturnThrustX(mo, mo->angle, -mo->radius << 1); y = P_ReturnThrustY(mo, mo->angle, -mo->radius << 1); segment = P_SpawnMobjFromMobj(mo, x, y, 0, MT_DRAGONTAIL); + if (P_MobjWasRemoved(segment)) + continue; P_SetTarget(&segment->target, mo); P_SetTarget(&mo->tracer, segment); segment->angle = mo->angle; @@ -14641,6 +14889,8 @@ void A_DragonbomberSpawn(mobj_t *actor) for (i = 0; i < 2; i++) // spawn wings { mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_DRAGONWING); + if (P_MobjWasRemoved(mo)) + continue; P_SetTarget(&mo->target, actor); mo->movedir = ANGLE_90 + i * ANGLE_180; } diff --git a/src/p_floor.c b/src/p_floor.c index 38f0c5a0f..118b4bdbc 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -1212,6 +1212,19 @@ static boolean T_SectorHasEnemies(sector_t *sec) return false; } +static void T_UpdateMobjPlaneZ(sector_t *sec) +{ + msecnode_t *node = sec->touching_thinglist; // things touching this sector + mobj_t *mo; + while (node) + { + mo = node->m_thing; + mo->floorz = P_FloorzAtPos(mo->x, mo->y, mo->z, mo->height); + mo->ceilingz = P_CeilingzAtPos(mo->x, mo->y, mo->z, mo->height); + node = node->m_thinglist_next; + } +} + // // T_NoEnemiesThinker // @@ -1921,6 +1934,9 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover) for (c = topz; c > bottomz; c -= spacing) { spawned = P_SpawnMobj(a, b, c, type); + if (P_MobjWasRemoved(spawned)) + continue; + spawned->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones if (fromcenter) @@ -1938,6 +1954,7 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover) // no longer exists (can't collide with again) rover->fofflags &= ~FOF_EXISTS; rover->master->frontsector->moved = true; + T_UpdateMobjPlaneZ(sec); // prevent objects from floating P_RecalcPrecipInSector(sec); } diff --git a/src/p_inter.c b/src/p_inter.c index c3811cbe4..d8765e7a2 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -256,6 +256,9 @@ void P_DoNightsScore(player_t *player) player->linktimer = nightslinktics; } + if (P_MobjWasRemoved(dummymo)) + return; + // Award 10-100 score, doubled if bonus time is active P_AddPlayerScore(player, min(player->linkcount,10)*(player->bonustime ? 20 : 10)); P_SetMobjState(dummymo, (player->bonustime ? dummymo->info->xdeathstate : dummymo->info->spawnstate) + min(player->linkcount,10)-1); @@ -526,7 +529,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) else if (player->pflags & PF_GLIDING && !P_IsObjectOnGround(toucher)) { player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE); - P_SetPlayerMobjState(toucher, S_PLAY_FALL); + P_SetMobjState(toucher, S_PLAY_FALL); toucher->momz += P_MobjFlip(toucher) * (player->speed >> 3); toucher->momx = 7*toucher->momx>>3; toucher->momy = 7*toucher->momy>>3; @@ -1246,7 +1249,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_ResetPlayer(player); - P_SetPlayerMobjState(toucher, S_PLAY_FALL); + P_SetMobjState(toucher, S_PLAY_FALL); } } return; @@ -1301,7 +1304,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { // A flicky orbits us now mobj_t *flickyobj = P_SpawnMobj(toucher->x, toucher->y, toucher->z + toucher->info->height, MT_NIGHTOPIANHELPER); - P_SetTarget(&flickyobj->target, toucher); + if (!P_MobjWasRemoved(flickyobj)) + P_SetTarget(&flickyobj->target, toucher); player->powers[pw_nights_helper] = (UINT16)special->info->speed; } @@ -1312,7 +1316,8 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (playeringame[i] && players[i].mo && players[i].powers[pw_carry] == CR_NIGHTSMODE) { players[i].powers[pw_nights_helper] = (UINT16)special->info->speed; flickyobj = P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z + players[i].mo->info->height, MT_NIGHTOPIANHELPER); - P_SetTarget(&flickyobj->target, players[i].mo); + if (!P_MobjWasRemoved(flickyobj)) + P_SetTarget(&flickyobj->target, players[i].mo); } if (special->info->deathsound != sfx_None) S_StartSound(NULL, special->info->deathsound); @@ -1553,7 +1558,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->pflags & PF_GLIDING && !P_IsObjectOnGround(toucher)) { player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE); - P_SetPlayerMobjState(toucher, S_PLAY_FALL); + P_SetMobjState(toucher, S_PLAY_FALL); toucher->momz += P_MobjFlip(toucher) * (player->speed >> 3); toucher->momx = 7*toucher->momx>>3; toucher->momy = 7*toucher->momy>>3; @@ -1604,7 +1609,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->pflags & PF_GLIDING && !P_IsObjectOnGround(toucher)) { player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE); - P_SetPlayerMobjState(toucher, S_PLAY_FALL); + P_SetMobjState(toucher, S_PLAY_FALL); toucher->momz += P_MobjFlip(toucher) * (player->speed >> 3); toucher->momx = 7*toucher->momx>>3; toucher->momy = 7*toucher->momy>>3; @@ -1640,7 +1645,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->momx = special->momy = 0; // Buenos Dias Mandy - P_SetPlayerMobjState(toucher, S_PLAY_STUN); + P_SetMobjState(toucher, S_PLAY_STUN); player->pflags &= ~PF_APPLYAUTOBRAKE; P_ResetPlayer(player); player->drawangle = special->angle + ANGLE_180; @@ -1719,7 +1724,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { player->powers[pw_carry] = CR_MACESPIN; S_StartSound(toucher, sfx_spin); - P_SetPlayerMobjState(toucher, S_PLAY_ROLL); + P_SetMobjState(toucher, S_PLAY_ROLL); } else player->powers[pw_carry] = CR_GENERIC; @@ -1780,7 +1785,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { if (player->bot && player->bot != BOT_MPAI && toucher->state-states != S_PLAY_GASP) S_StartSound(toucher, special->info->deathsound); // Force it to play a sound for bots - P_SetPlayerMobjState(toucher, S_PLAY_GASP); + P_SetMobjState(toucher, S_PLAY_GASP); P_ResetPlayer(player); } @@ -1821,9 +1826,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!player->bot && player->bot != BOT_MPAI && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15))) { mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART); - P_SetTarget(&mcart->target, toucher); - mcart->angle = toucher->angle = player->drawangle = special->angle; - mcart->friction = FRACUNIT; + if (!P_MobjWasRemoved(mcart)) + { + P_SetTarget(&mcart->target, toucher); + mcart->angle = toucher->angle = player->drawangle = special->angle; + mcart->friction = FRACUNIT; + } P_ResetPlayer(player); player->pflags |= PF_JUMPDOWN; @@ -1847,7 +1855,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) toucher->momz = toucher->tracer->momz + P_AproxDistance(toucher->tracer->momx, toucher->tracer->momy)/2; P_ResetPlayer(player); player->pflags &= ~PF_APPLYAUTOBRAKE; - P_SetPlayerMobjState(toucher, S_PLAY_FALL); + P_SetMobjState(toucher, S_PLAY_FALL); P_SetTarget(&toucher->tracer->target, NULL); P_KillMobj(toucher->tracer, toucher, special, 0); P_SetTarget(&toucher->tracer, NULL); @@ -2236,7 +2244,7 @@ void P_CheckTimeLimit(void) } if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } //Optional tie-breaker for Match/CTF @@ -2299,11 +2307,11 @@ void P_CheckTimeLimit(void) } } if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } /** Checks if a player's score is over the pointlimit and the round should end. @@ -2332,7 +2340,7 @@ void P_CheckPointLimit(void) if ((UINT32)cv_pointlimit.value <= redscore || (UINT32)cv_pointlimit.value <= bluescore) { if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } } else @@ -2345,7 +2353,7 @@ void P_CheckPointLimit(void) if ((UINT32)cv_pointlimit.value <= players[i].score) { if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); return; } } @@ -2389,7 +2397,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("The IT player has left the game.\n")); if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); return; } @@ -2409,7 +2417,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("All players have been tagged!\n")); if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } return; @@ -2421,7 +2429,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("There are no players able to become IT.\n")); if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } return; @@ -2433,7 +2441,7 @@ void P_CheckSurvivors(void) { CONS_Printf(M_GetText("All players have been tagged!\n")); if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } } @@ -2593,7 +2601,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget break; } - P_SetMobjState(scoremobj, scorestate); + if (!P_MobjWasRemoved(scoremobj)) + P_SetMobjState(scoremobj, scorestate); source->player->scoreadd = locscoreadd; } @@ -2754,6 +2763,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget mo = P_SpawnMobj(inflictor->x + inflictor->momx, inflictor->y + inflictor->momy, inflictor->z + (inflictor->height / 2) + inflictor->momz, MT_EXTRALARGEBUBBLE); else mo = P_SpawnMobj(target->x, target->y, target->z, MT_EXTRALARGEBUBBLE); + if (P_MobjWasRemoved(mo)) + break; mo->destscale = target->scale; P_SetScale(mo, mo->destscale); P_SetMobjState(mo, mo->info->raisestate); @@ -2832,8 +2843,11 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget mo->angle = FixedAngle((P_RandomKey(36)*10)<angle = mo->angle; - P_SetMobjState(mo2, S_BOSSSEBH2); + if (!P_MobjWasRemoved(mo2)) + { + mo2->angle = mo->angle; + P_SetMobjState(mo2, S_BOSSSEBH2); + } if (++i == 2) // we've already removed 2 of these, let's stop now break; @@ -2932,7 +2946,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (flip) momz *= -1; #define makechunk(angtweak, xmov, ymov) \ + do {\ chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE);\ + if (P_MobjWasRemoved(chunk))\ + break;\ P_SetMobjState(chunk, target->info->xdeathstate);\ chunk->health = 0;\ chunk->angle = angtweak;\ @@ -2942,7 +2959,8 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget chunk->y += ymov;\ P_SetThingPosition(chunk);\ P_InstaThrust(chunk,chunk->angle, 4*scale);\ - chunk->momz = momz + chunk->momz = momz;\ + } while (0) makechunk(ang + ANGLE_180, -xoffs, -yoffs); makechunk(ang, xoffs, yoffs); @@ -2955,20 +2973,23 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget momz *= -1; chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE); - P_SetMobjState(chunk, target->info->deathstate); - chunk->health = 0; - chunk->angle = ang + ANGLE_180; - P_UnsetThingPosition(chunk); - chunk->flags = MF_NOCLIP; - chunk->x -= xoffs; - chunk->y -= yoffs; - if (flip) - chunk->z -= 12*scale; - else - chunk->z += 12*scale; - P_SetThingPosition(chunk); - P_InstaThrust(chunk, chunk->angle, 2*scale); - chunk->momz = momz; + if (!P_MobjWasRemoved(chunk)) + { + P_SetMobjState(chunk, target->info->deathstate); + chunk->health = 0; + chunk->angle = ang + ANGLE_180; + P_UnsetThingPosition(chunk); + chunk->flags = MF_NOCLIP; + chunk->x -= xoffs; + chunk->y -= yoffs; + if (flip) + chunk->z -= 12*scale; + else + chunk->z += 12*scale; + P_SetThingPosition(chunk); + P_InstaThrust(chunk, chunk->angle, 2*scale); + chunk->momz = momz; + } P_SetMobjState(target, target->info->deathstate); target->health = 0; @@ -2977,7 +2998,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->flags = MF_NOCLIP; target->x += xoffs; target->y += yoffs; - target->z = chunk->z; + if (flip) + target->z -= 12*scale; + else + target->z += 12*scale; P_SetThingPosition(target); P_InstaThrust(target, target->angle, 2*scale); target->momz = momz; @@ -3000,21 +3024,25 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget sprflip = P_RandomChance(FRACUNIT/2); #define makechunk(angtweak, xmov, ymov) \ - chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);\ - P_SetMobjState(chunk, target->info->xdeathstate);\ - chunk->health = 0;\ - chunk->angle = target->angle;\ - P_UnsetThingPosition(chunk);\ - chunk->flags = MF_NOCLIP;\ - chunk->x += xmov - forwardxoffs;\ - chunk->y += ymov - forwardyoffs;\ - P_SetThingPosition(chunk);\ - P_InstaThrust(chunk, angtweak, 4*scale);\ - chunk->momz = P_RandomRange(5, 7)*scale;\ - if (flip)\ - chunk->momz *= -1;\ - if (sprflip)\ - chunk->frame |= FF_VERTICALFLIP + do {\ + chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);\ + if (P_MobjWasRemoved(chunk))\ + break;\ + P_SetMobjState(chunk, target->info->xdeathstate);\ + chunk->health = 0;\ + chunk->angle = target->angle;\ + P_UnsetThingPosition(chunk);\ + chunk->flags = MF_NOCLIP;\ + chunk->x += xmov - forwardxoffs;\ + chunk->y += ymov - forwardyoffs;\ + P_SetThingPosition(chunk);\ + P_InstaThrust(chunk, angtweak, 4*scale);\ + chunk->momz = P_RandomRange(5, 7)*scale;\ + if (flip)\ + chunk->momz *= -1;\ + if (sprflip)\ + chunk->frame |= FF_VERTICALFLIP;\ + } while (0) makechunk(ang + ANGLE_180, -xoffs, -yoffs); sprflip = !sprflip; @@ -3026,21 +3054,23 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget sprflip = P_RandomChance(FRACUNIT/2); chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE); - - P_SetMobjState(chunk, target->info->deathstate); - chunk->health = 0; - chunk->angle = target->angle; - P_UnsetThingPosition(chunk); - chunk->flags = MF_NOCLIP; - chunk->x += forwardxoffs - xoffs; - chunk->y += forwardyoffs - yoffs; - P_SetThingPosition(chunk); - P_InstaThrust(chunk, ang + ANGLE_180, 2*scale); - chunk->momz = P_RandomRange(5, 7)*scale; - if (flip) - chunk->momz *= -1; - if (sprflip) - chunk->frame |= FF_VERTICALFLIP; + if (!P_MobjWasRemoved(chunk)) + { + P_SetMobjState(chunk, target->info->deathstate); + chunk->health = 0; + chunk->angle = target->angle; + P_UnsetThingPosition(chunk); + chunk->flags = MF_NOCLIP; + chunk->x += forwardxoffs - xoffs; + chunk->y += forwardyoffs - yoffs; + P_SetThingPosition(chunk); + P_InstaThrust(chunk, ang + ANGLE_180, 2*scale); + chunk->momz = P_RandomRange(5, 7)*scale; + if (flip) + chunk->momz *= -1; + if (sprflip) + chunk->frame |= FF_VERTICALFLIP; + } P_SetMobjState(target, target->info->deathstate); target->health = 0; @@ -3059,9 +3089,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget else if (target->player) { if (damagetype == DMG_DROWNED || damagetype == DMG_SPACEDROWN) - P_SetPlayerMobjState(target, target->info->xdeathstate); + P_SetMobjState(target, target->info->xdeathstate); else - P_SetPlayerMobjState(target, target->info->deathstate); + P_SetMobjState(target, target->info->deathstate); } else #ifdef DEBUG_NULL_DEATHSTATE @@ -3115,7 +3145,7 @@ static void P_NiGHTSDamage(mobj_t *target, mobj_t *source) } player->powers[pw_flashing] = flashingtics; - P_SetPlayerMobjState(target, S_PLAY_NIGHTS_STUN); + P_SetMobjState(target, S_PLAY_NIGHTS_STUN); S_StartSound(target, sfx_nghurt); player->mo->spriteroll = 0; @@ -3336,7 +3366,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) if (!player->spectator) player->mo->flags2 &= ~MF2_DONTDRAW; - P_SetPlayerMobjState(player->mo, player->mo->info->deathstate); + P_SetMobjState(player->mo, player->mo->info->deathstate); if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) { P_PlayerFlagBurst(player, false); @@ -3410,7 +3440,7 @@ static void P_SuperDamage(player_t *player, mobj_t *inflictor, mobj_t *source, I P_InstaThrust(player->mo, ang, fallbackspeed); - P_SetPlayerMobjState(player->mo, S_PLAY_STUN); + P_SetMobjState(player->mo, S_PLAY_STUN); P_ResetPlayer(player); @@ -3921,6 +3951,8 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) z += player->mo->height - mobjinfo[objType].height; mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); + if (P_MobjWasRemoved(mo)) + continue; mo->fuse = 8*TICRATE; P_SetTarget(&mo->target, player->mo); @@ -4054,6 +4086,9 @@ void P_PlayerWeaponPanelBurst(player_t *player) z += player->mo->height - mobjinfo[weptype].height; mo = P_SpawnMobj(player->mo->x, player->mo->y, z, weptype); + if (P_MobjWasRemoved(mo)) + continue; + mo->reactiontime = ammoamt; mo->flags2 |= MF2_DONTRESPAWN; mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); @@ -4087,13 +4122,13 @@ void P_PlayerWeaponAmmoBurst(player_t *player) mobj_t *mo; angle_t fa; fixed_t ns; - INT32 i = 0; + INT32 i; fixed_t z; mobjtype_t weptype = 0; powertype_t power = 0; - while (true) + for (i = 0;; i++) { if (player->powers[pw_bouncering]) { @@ -4138,6 +4173,8 @@ void P_PlayerWeaponAmmoBurst(player_t *player) z += player->mo->height - mobjinfo[weptype].height; mo = P_SpawnMobj(player->mo->x, player->mo->y, z, weptype); + if (P_MobjWasRemoved(mo)) + continue; mo->health = player->powers[power]; mo->flags2 |= MF2_DONTRESPAWN; mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); @@ -4163,8 +4200,6 @@ void P_PlayerWeaponAmmoBurst(player_t *player) if (i & 1) P_SetObjectMomZ(mo, 3*FRACUNIT, true); - - i++; } } @@ -4189,45 +4224,50 @@ void P_PlayerWeaponPanelOrAmmoBurst(player_t *player) player->ringweapons &= ~rwflag; \ SETUP_DROP(pickup) \ mo = P_SpawnMobj(player->mo->x, player->mo->y, z, pickup); \ - mo->reactiontime = 0; \ - mo->flags2 |= MF2_DONTRESPAWN; \ - mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \ - P_SetTarget(&mo->target, player->mo); \ - mo->fuse = 12*TICRATE; \ - mo->destscale = player->mo->scale; \ - P_SetScale(mo, player->mo->scale); \ - mo->momx = FixedMul(FINECOSINE(fa),ns); \ - if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \ - mo->momy = FixedMul(FINESINE(fa),ns); \ - P_SetObjectMomZ(mo, 4*FRACUNIT, false); \ - if (i & 1) \ - P_SetObjectMomZ(mo, 4*FRACUNIT, true); \ - if (player->mo->eflags & MFE_VERTICALFLIP) \ - mo->flags2 |= MF2_OBJECTFLIP; \ - ++i; \ + if (!P_MobjWasRemoved(mo)) \ + { \ + mo->reactiontime = 0; \ + mo->flags2 |= MF2_DONTRESPAWN; \ + mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \ + P_SetTarget(&mo->target, player->mo); \ + mo->fuse = 12*TICRATE; \ + mo->destscale = player->mo->scale; \ + P_SetScale(mo, player->mo->scale); \ + mo->momx = FixedMul(FINECOSINE(fa),ns); \ + if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \ + mo->momy = FixedMul(FINESINE(fa),ns); \ + P_SetObjectMomZ(mo, 4*FRACUNIT, false); \ + if (i & 1) \ + P_SetObjectMomZ(mo, 4*FRACUNIT, true); \ + if (player->mo->eflags & MFE_VERTICALFLIP) \ + mo->flags2 |= MF2_OBJECTFLIP; \ + } \ } \ else if (player->powers[power] > 0) \ { \ SETUP_DROP(ammo) \ mo = P_SpawnMobj(player->mo->x, player->mo->y, z, ammo); \ - mo->health = player->powers[power]; \ - mo->flags2 |= MF2_DONTRESPAWN; \ - mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \ - P_SetTarget(&mo->target, player->mo); \ - mo->fuse = 12*TICRATE; \ - mo->destscale = player->mo->scale; \ - P_SetScale(mo, player->mo->scale); \ - mo->momx = FixedMul(FINECOSINE(fa),ns); \ - if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \ - mo->momy = FixedMul(FINESINE(fa),ns); \ - P_SetObjectMomZ(mo, 3*FRACUNIT, false); \ - if (i & 1) \ - P_SetObjectMomZ(mo, 3*FRACUNIT, true); \ - if (player->mo->eflags & MFE_VERTICALFLIP) \ - mo->flags2 |= MF2_OBJECTFLIP; \ - player->powers[power] = 0; \ - ++i; \ - } + if (!P_MobjWasRemoved(mo)) \ + { \ + mo->health = player->powers[power]; \ + mo->flags2 |= MF2_DONTRESPAWN; \ + mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \ + P_SetTarget(&mo->target, player->mo); \ + mo->fuse = 12*TICRATE; \ + mo->destscale = player->mo->scale; \ + P_SetScale(mo, player->mo->scale); \ + mo->momx = FixedMul(FINECOSINE(fa),ns); \ + if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \ + mo->momy = FixedMul(FINESINE(fa),ns); \ + P_SetObjectMomZ(mo, 3*FRACUNIT, false); \ + if (i & 1) \ + P_SetObjectMomZ(mo, 3*FRACUNIT, true); \ + if (player->mo->eflags & MFE_VERTICALFLIP) \ + mo->flags2 |= MF2_OBJECTFLIP; \ + player->powers[power] = 0; \ + } \ + } \ + ++i DROP_WEAPON(RW_BOUNCE, MT_BOUNCEPICKUP, MT_BOUNCERING, pw_bouncering); DROP_WEAPON(RW_RAIL, MT_RAILPICKUP, MT_RAILRING, pw_railring); @@ -4351,23 +4391,26 @@ void P_PlayerEmeraldBurst(player_t *player, boolean toss) momy = 0; mo = P_SpawnMobj(player->mo->x, player->mo->y, z, MT_FLINGEMERALD); - mo->health = 1; - mo->threshold = stoneflag; - mo->flags2 |= (MF2_DONTRESPAWN|MF2_SLIDEPUSH); - mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); - P_SetTarget(&mo->target, player->mo); - mo->fuse = 12*TICRATE; - P_SetMobjState(mo, statenum); - - mo->momx = momx; - mo->momy = momy; - - P_SetObjectMomZ(mo, 3*FRACUNIT, false); - - if (player->mo->eflags & MFE_VERTICALFLIP) + if (!P_MobjWasRemoved(mo)) { - mo->momz = -mo->momz; - mo->flags2 |= MF2_OBJECTFLIP; + mo->health = 1; + mo->threshold = stoneflag; + mo->flags2 |= (MF2_DONTRESPAWN|MF2_SLIDEPUSH); + mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); + P_SetTarget(&mo->target, player->mo); + mo->fuse = 12*TICRATE; + P_SetMobjState(mo, statenum); + + mo->momx = momx; + mo->momy = momy; + + P_SetObjectMomZ(mo, 3*FRACUNIT, false); + + if (player->mo->eflags & MFE_VERTICALFLIP) + { + mo->momz = -mo->momz; + mo->flags2 |= MF2_OBJECTFLIP; + } } if (toss) @@ -4395,6 +4438,8 @@ void P_PlayerFlagBurst(player_t *player, boolean toss) type = MT_BLUEFLAG; flag = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, type); + if (P_MobjWasRemoved(flag)) + return; if (player->mo->eflags & MFE_VERTICALFLIP) { diff --git a/src/p_local.h b/src/p_local.h index ba033dc06..6126dedbe 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -71,6 +71,7 @@ typedef enum NUM_THINKERLISTS } thinklistnum_t; /**< Thinker lists. */ extern thinker_t thlist[]; +extern mobj_t *mobjcache; void P_InitThinkers(void); void P_AddThinker(const thinklistnum_t n, thinker_t *thinker); @@ -203,7 +204,7 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius); void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius); boolean P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user boolean P_SuperReady(player_t *player, boolean transform); -void P_DoJump(player_t *player, boolean soundandstate); +void P_DoJump(player_t *player, boolean soundandstate, boolean allowflip); void P_DoSpinDashDust(player_t *player); #define P_AnalogMove(player) (P_ControlStyle(player) == CS_LMAOGALOG) boolean P_TransferToNextMare(player_t *player); @@ -288,7 +289,7 @@ mobjtype_t P_GetMobjtype(UINT16 mthingtype); void P_RespawnSpecials(void); -mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...); void P_RecalcPrecipInSector(sector_t *sector); void P_PrecipitationEffects(void); @@ -296,7 +297,6 @@ void P_PrecipitationEffects(void); void P_RemoveMobj(mobj_t *th); boolean P_MobjWasRemoved(mobj_t *th); void P_RemoveSavegameMobj(mobj_t *th); -boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state); boolean P_SetMobjState(mobj_t *mobj, statenum_t state); void P_RunShields(void); void P_RunOverlays(void); @@ -424,6 +424,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); boolean P_Move(mobj_t *actor, fixed_t speed); boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); boolean P_MoveOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); +boolean P_LineIsBlocking(mobj_t *mo, line_t *li); void P_SlideMove(mobj_t *mo); void P_BounceMove(mobj_t *mo); boolean P_CheckSight(mobj_t *t1, mobj_t *t2); diff --git a/src/p_map.c b/src/p_map.c index 6a152c563..21582bbd7 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -264,7 +264,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) UINT8 secondjump = object->player->secondjump; UINT16 tailsfly = object->player->powers[pw_tailsfly]; if (object->player->pflags & PF_GLIDING) - P_SetPlayerMobjState(object, S_PLAY_FALL); + P_SetMobjState(object, S_PLAY_FALL); P_ResetPlayer(object->player); object->player->pflags |= pflags; object->player->secondjump = secondjump; @@ -403,7 +403,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) } if (object->player->pflags & PF_GLIDING) - P_SetPlayerMobjState(object, S_PLAY_FALL); + P_SetMobjState(object, S_PLAY_FALL); if ((spring->info->painchance == 3)) { if (!(pflags = (object->player->pflags & PF_SPINNING)) && @@ -411,11 +411,11 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) || (spring->flags2 & MF2_AMBUSH))) { pflags = PF_SPINNING; - P_SetPlayerMobjState(object, S_PLAY_ROLL); + P_SetMobjState(object, S_PLAY_ROLL); S_StartSound(object, sfx_spin); } else - P_SetPlayerMobjState(object, S_PLAY_ROLL); + P_SetMobjState(object, S_PLAY_ROLL); } else { @@ -424,7 +424,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) pflags = object->player->pflags & (PF_STARTJUMP | PF_JUMPED | PF_NOJUMPDAMAGE | PF_SPINNING | PF_THOKKED | PF_BOUNCING); // I still need these. if (wasSpindashing) // Ensure we're in the rolling state, and not spindash. - P_SetPlayerMobjState(object, S_PLAY_ROLL); + P_SetMobjState(object, S_PLAY_ROLL); if (object->player->charability == CA_GLIDEANDCLIMB && object->player->skidtime && (pflags & PF_JUMPED)) { @@ -439,7 +439,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities. { object->player->pflags |= P_GetJumpFlags(object->player); - P_SetPlayerMobjState(object, S_PLAY_JUMP); + P_SetMobjState(object, S_PLAY_JUMP); } else if ((spring->info->painchance == 2) || ((spring->info->painchance != 3) && (pflags & PF_BOUNCING))) // Adding momentum only. { @@ -456,16 +456,16 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) object->player->secondjump = secondjump; } else if (object->player->dashmode >= DASHMODE_THRESHOLD) - P_SetPlayerMobjState(object, S_PLAY_DASH); + P_SetMobjState(object, S_PLAY_DASH); else if (P_IsObjectOnGround(object)) - P_SetPlayerMobjState(object, (horizspeed >= FixedMul(object->player->runspeed, object->scale)) ? S_PLAY_RUN : S_PLAY_WALK); + P_SetMobjState(object, (horizspeed >= FixedMul(object->player->runspeed, object->scale)) ? S_PLAY_RUN : S_PLAY_WALK); else - P_SetPlayerMobjState(object, (object->momz > 0) ? S_PLAY_SPRING : S_PLAY_FALL); + P_SetMobjState(object, (object->momz > 0) ? S_PLAY_SPRING : S_PLAY_FALL); } else if (P_MobjFlip(object)*vertispeed > 0) - P_SetPlayerMobjState(object, S_PLAY_SPRING); + P_SetMobjState(object, S_PLAY_SPRING); else - P_SetPlayerMobjState(object, S_PLAY_FALL); + P_SetMobjState(object, S_PLAY_FALL); } else if (horizspeed && object->tracer @@ -547,7 +547,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) if (p && !p->powers[pw_tailsfly] && !p->powers[pw_carry]) // doesn't reset anim for Tails' flight { P_ResetPlayer(p); - P_SetPlayerMobjState(object, S_PLAY_FALL); + P_SetMobjState(object, S_PLAY_FALL); P_SetTarget(&object->tracer, spring); p->powers[pw_carry] = CR_FAN; } @@ -565,7 +565,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) { P_ResetPlayer(p); if (p->panim != PA_FALL) - P_SetPlayerMobjState(object, S_PLAY_FALL); + P_SetMobjState(object, S_PLAY_FALL); } break; default: @@ -1047,7 +1047,7 @@ static boolean PIT_CheckThing(mobj_t *thing) thing->flags2 &= ~MF2_DONTDRAW; // don't leave the rock invisible if it was flashing prior to boarding P_SetTarget(&thing->tracer, tmthing); P_ResetPlayer(tmthing->player); - P_SetPlayerMobjState(tmthing, S_PLAY_WALK); + P_SetMobjState(tmthing, S_PLAY_WALK); tmthing->player->powers[pw_carry] = CR_ROLLOUT; P_SetTarget(&tmthing->tracer, thing); if (!P_IsObjectOnGround(thing)) @@ -2865,6 +2865,8 @@ boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) { boolean moveok; mobj_t *hack = P_SpawnMobjFromMobj(thing, 0, 0, 0, MT_RAY); + if (P_MobjWasRemoved(hack)) + return false; hack->radius = thing->radius; hack->height = thing->height; @@ -3403,36 +3405,41 @@ static boolean P_IsClimbingValid(player_t *player, angle_t angle) return false; } -static boolean PTR_LineIsBlocking(line_t *li) +// +// P_LineIsBlocking +// +// Determines if line would block mo's movement +// +boolean P_LineIsBlocking(mobj_t *mo, line_t *li) { // one-sided linedefs are always solid to sliding movement. if (!li->backsector) - return !P_PointOnLineSide(slidemo->x, slidemo->y, li); + return !P_PointOnLineSide(mo->x, mo->y, li); - if (!(slidemo->flags & MF_MISSILE)) + if (!(mo->flags & MF_MISSILE)) { if (li->flags & ML_IMPASSIBLE) return true; - if ((slidemo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS) + if ((mo->flags & (MF_ENEMY|MF_BOSS)) && li->flags & ML_BLOCKMONSTERS) return true; } // set openrange, opentop, openbottom - P_LineOpening(li, slidemo); + P_LineOpening(li, mo); - if (openrange < slidemo->height) + if (openrange < mo->height) return true; // doesn't fit - if (opentop - slidemo->z < slidemo->height) + if (opentop - mo->z < mo->height) return true; // mobj is too high - if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale)) + if (openbottom - mo->z > FixedMul(MAXSTEPMOVE, mo->scale)) return true; // too big a step up - if (slidemo->player - && openrange < P_GetPlayerHeight(slidemo->player) - && !P_PlayerCanEnterSpinGaps(slidemo->player)) + if (mo->player + && openrange < P_GetPlayerHeight(mo->player) + && !P_PlayerCanEnterSpinGaps(mo->player)) return true; // nonspin character should not take this path return false; @@ -3475,7 +3482,7 @@ static void PTR_GlideClimbTraverse(line_t *li) } // see about climbing on the wall - if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL) + if (!(checkline->flags & ML_NOCLIMB) && checkline->special != SPECIAL_HORIZON_LINE) { boolean canclimb; angle_t climbangle, climbline; @@ -3536,7 +3543,7 @@ static boolean PTR_SlideTraverse(intercept_t *in) li = in->d.line; - if (!PTR_LineIsBlocking(li)) + if (!P_LineIsBlocking(slidemo, li)) return true; // the line blocks movement, diff --git a/src/p_mobj.c b/src/p_mobj.c index 1b2065920..f16fef2f0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -45,6 +45,8 @@ actioncache_t actioncachehead; static mobj_t *overlaycap = NULL; +mobj_t *mobjcache = NULL; + void P_InitCachedActions(void) { actioncachehead.prev = actioncachehead.next = &actioncachehead; @@ -177,7 +179,7 @@ static void P_CyclePlayerMobjState(mobj_t *mobj) // you can cycle through multiple states in a tic if (!mobj->tics && mobj->state) - if (!P_SetPlayerMobjState(mobj, mobj->state->nextstate)) + if (!P_SetMobjState(mobj, mobj->state->nextstate)) return; // freed itself } } @@ -188,7 +190,7 @@ static void P_CyclePlayerMobjState(mobj_t *mobj) // // Separate from P_SetMobjState because of the pw_flashing check and Super states // -boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) +static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) { state_t *st; player_t *player = mobj->player; @@ -210,7 +212,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) return P_SetPlayerMobjState(mobj, S_PLAY_FALL); // Catch swimming versus flying - if ((state == S_PLAY_FLY || (state == S_PLAY_GLIDE && skins[player->skin].sprites[SPR2_SWIM].numframes)) + if ((state == S_PLAY_FLY || (state == S_PLAY_GLIDE && skins[player->skin]->sprites[SPR2_SWIM].numframes)) && player->mo->eflags & MFE_UNDERWATER && !player->skidtime) return P_SetPlayerMobjState(player->mo, S_PLAY_SWIM); else if (state == S_PLAY_SWIM && !(player->mo->eflags & MFE_UNDERWATER)) @@ -514,10 +516,8 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state) statenum_t i = state; // initial state statenum_t tempstate[NUMSTATES]; // for use with recursion -#ifdef PARANOIA if (mobj->player != NULL) - I_Error("P_SetMobjState used for player mobj. Use P_SetPlayerMobjState instead!\n(State called: %d)", state); -#endif + return P_SetPlayerMobjState(mobj, state); if (recursion++) // if recursion detected, memset(seenstate = tempstate, 0, sizeof tempstate); // clear state table @@ -915,29 +915,41 @@ void P_ExplodeMissile(mobj_t *mo) P_RadiusAttack(mo, mo, 96*FRACUNIT, 0, true); explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE); - P_SetScale(explodemo, mo->scale); - explodemo->destscale = mo->destscale; - explodemo->momx += (P_RandomByte() % 32) * FixedMul(FRACUNIT/8, explodemo->scale); - explodemo->momy += (P_RandomByte() % 32) * FixedMul(FRACUNIT/8, explodemo->scale); - S_StartSound(explodemo, sfx_pop); + if (!P_MobjWasRemoved(explodemo)) + { + P_SetScale(explodemo, mo->scale); + explodemo->destscale = mo->destscale; + explodemo->momx += (P_RandomByte() % 32) * FixedMul(FRACUNIT/8, explodemo->scale); + explodemo->momy += (P_RandomByte() % 32) * FixedMul(FRACUNIT/8, explodemo->scale); + S_StartSound(explodemo, sfx_pop); + } explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE); - P_SetScale(explodemo, mo->scale); - explodemo->destscale = mo->destscale; - explodemo->momx += (P_RandomByte() % 64) * FixedMul(FRACUNIT/8, explodemo->scale); - explodemo->momy -= (P_RandomByte() % 64) * FixedMul(FRACUNIT/8, explodemo->scale); - S_StartSound(explodemo, sfx_dmpain); + if (!P_MobjWasRemoved(explodemo)) + { + P_SetScale(explodemo, mo->scale); + explodemo->destscale = mo->destscale; + explodemo->momx += (P_RandomByte() % 64) * FixedMul(FRACUNIT/8, explodemo->scale); + explodemo->momy -= (P_RandomByte() % 64) * FixedMul(FRACUNIT/8, explodemo->scale); + S_StartSound(explodemo, sfx_dmpain); + } explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE); - P_SetScale(explodemo, mo->scale); - explodemo->destscale = mo->destscale; - explodemo->momx -= (P_RandomByte() % 128) * FixedMul(FRACUNIT/8, explodemo->scale); - explodemo->momy += (P_RandomByte() % 128) * FixedMul(FRACUNIT/8, explodemo->scale); - S_StartSound(explodemo, sfx_pop); + if (!P_MobjWasRemoved(explodemo)) + { + P_SetScale(explodemo, mo->scale); + explodemo->destscale = mo->destscale; + explodemo->momx -= (P_RandomByte() % 128) * FixedMul(FRACUNIT/8, explodemo->scale); + explodemo->momy += (P_RandomByte() % 128) * FixedMul(FRACUNIT/8, explodemo->scale); + S_StartSound(explodemo, sfx_pop); + } explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_EXPLODE); - P_SetScale(explodemo, mo->scale); - explodemo->destscale = mo->destscale; - explodemo->momx -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale); - explodemo->momy -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale); - S_StartSound(explodemo, sfx_cybdth); + if (!P_MobjWasRemoved(explodemo)) + { + P_SetScale(explodemo, mo->scale); + explodemo->destscale = mo->destscale; + explodemo->momx -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale); + explodemo->momy -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale); + S_StartSound(explodemo, sfx_cybdth); + } } mo->flags &= ~MF_MISSILE; @@ -1109,7 +1121,7 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t testy += y; // If the highest point is in the sector, then we have it easy! Just get the Z at that point - if (R_PointInSubsector(testx, testy)->sector == (boundsec ? boundsec : sector)) + if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy)) return P_GetSlopeZAt(slope, testx, testy); // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point @@ -1186,7 +1198,7 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed testy += y; // If the highest point is in the sector, then we have it easy! Just get the Z at that point - if (R_PointInSubsector(testx, testy)->sector == (boundsec ? boundsec : sector)) + if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy)) return P_GetSlopeZAt(slope, testx, testy); // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point @@ -1264,7 +1276,7 @@ fixed_t P_CameraFloorZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fix testy += y; // If the highest point is in the sector, then we have it easy! Just get the Z at that point - if (R_PointInSubsector(testx, testy)->sector == (boundsec ? boundsec : sector)) + if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy)) return P_GetSlopeZAt(slope, testx, testy); // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point @@ -1341,7 +1353,7 @@ fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, f testy += y; // If the highest point is in the sector, then we have it easy! Just get the Z at that point - if (R_PointInSubsector(testx, testy)->sector == (boundsec ? boundsec : sector)) + if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy)) return P_GetSlopeZAt(slope, testx, testy); // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point @@ -1648,7 +1660,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) { // if in a walking frame, stop moving if (player->panim == PA_WALK) - P_SetPlayerMobjState(mo, S_PLAY_STND); + P_SetMobjState(mo, S_PLAY_STND); mo->momx = player->cmomx; mo->momy = player->cmomy; } @@ -1664,8 +1676,6 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) mo->momx = FixedMul(mo->momx, mo->friction); mo->momy = FixedMul(mo->momy, mo->friction); } - - mo->friction = ORIG_FRICTION; } } else @@ -2370,6 +2380,55 @@ boolean P_ZMovement(mobj_t *mo) else if (!onground) P_SlopeLaunch(mo); } + + if (!mo->player && P_CheckDeathPitCollide(mo) && mo->health + && !(mo->flags & MF_NOCLIPHEIGHT) && !(mo->flags2 & MF2_BOSSDEAD)) + { + switch (mo->type) + { + case MT_GHOST: + case MT_METALSONIC_RACE: + case MT_EXPLODE: + case MT_BOSSEXPLODE: + case MT_SONIC3KBOSSEXPLODE: + break; + case MT_REDFLAG: + case MT_BLUEFLAG: + // Remove from death pits. DON'T FUCKING DESPAWN IT DAMMIT + mo->fuse = 1; + return false; + case MT_BOUNCERING: + case MT_INFINITYRING: + case MT_AUTOMATICRING: + case MT_RAILRING: + case MT_EXPLOSIONRING: + case MT_SCATTERRING: + case MT_GRENADERING: + case MT_BOUNCEPICKUP: + case MT_RAILPICKUP: + case MT_AUTOPICKUP: + case MT_EXPLODEPICKUP: + case MT_SCATTERPICKUP: + case MT_GRENADEPICKUP: + //Don't remove respawning ringslinger collectables on death pits + if (!(mo->flags2 & MF2_DONTRESPAWN)) + break; + /* FALLTHRU */ + default: + if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS || mo->type == MT_MINECART) + { + // Kill enemies, bosses and minecarts that fall into death pits. + P_KillMobj(mo, NULL, NULL, 0); + return !P_MobjWasRemoved(mo); // allows explosion states to run + } + else + { + P_RemoveMobj(mo); + return false; + } + break; + } + } switch (mo->type) { @@ -2397,19 +2456,7 @@ boolean P_ZMovement(mobj_t *mo) mo->flags |= MF_NOGRAVITY; } break; - case MT_SPINFIRE: - if (P_CheckDeathPitCollide(mo)) - { - P_RemoveMobj(mo); - return false; - } - break; case MT_GOOP: - if (P_CheckDeathPitCollide(mo)) - { - P_RemoveMobj(mo); - return false; - } if (mo->z <= mo->floorz && mo->momz) { P_SetMobjState(mo, mo->info->meleestate); @@ -2419,27 +2466,6 @@ boolean P_ZMovement(mobj_t *mo) S_StartSound(mo, mo->info->painsound); } break; - case MT_FALLINGROCK: - case MT_BIGTUMBLEWEED: - case MT_LITTLETUMBLEWEED: - case MT_SHELL: - // Remove stuff from death pits. - if (P_CheckDeathPitCollide(mo)) - { - P_RemoveMobj(mo); - return false; - } - break; - case MT_REDFLAG: - case MT_BLUEFLAG: - // Remove from death pits. DON'T FUCKING DESPAWN IT DAMMIT - if (P_CheckDeathPitCollide(mo)) - { - mo->fuse = 1; - return false; - } - break; - case MT_RING: // Ignore still rings case MT_COIN: case MT_BLUESPHERE: @@ -2453,15 +2479,6 @@ boolean P_ZMovement(mobj_t *mo) case MT_FLINGBLUESPHERE: case MT_FLINGNIGHTSCHIP: case MT_FLINGEMERALD: - // Remove flinged stuff from death pits. - if (P_CheckDeathPitCollide(mo)) - { - P_RemoveMobj(mo); - return false; - } - if (!(mo->momx || mo->momy || mo->momz)) - return true; - break; case MT_BOUNCERING: case MT_INFINITYRING: case MT_AUTOMATICRING: @@ -2475,12 +2492,6 @@ boolean P_ZMovement(mobj_t *mo) case MT_EXPLODEPICKUP: case MT_SCATTERPICKUP: case MT_GRENADEPICKUP: - // Remove flinged stuff from death pits. - if (P_CheckDeathPitCollide(mo) && (mo->flags2 & MF2_DONTRESPAWN)) - { - P_RemoveMobj(mo); - return false; - } if (!(mo->momx || mo->momy || mo->momz)) return true; break; @@ -2502,35 +2513,6 @@ boolean P_ZMovement(mobj_t *mo) break; } - if (!mo->player && P_CheckDeathPitCollide(mo)) - { - switch (mo->type) - { - case MT_GHOST: - case MT_METALSONIC_RACE: - case MT_EXPLODE: - case MT_BOSSEXPLODE: - case MT_SONIC3KBOSSEXPLODE: - break; - default: - if (mo->flags & MF_ENEMY || mo->flags & MF_BOSS || mo->type == MT_MINECART) - { - // Kill enemies, bosses and minecarts that fall into death pits. - if (mo->health) - { - P_KillMobj(mo, NULL, NULL, 0); - } - return !P_MobjWasRemoved(mo); // allows explosion states to run - } - else - { - P_RemoveMobj(mo); - return false; - } - break; - } - } - if (P_MobjFlip(mo)*mo->momz < 0 && (mo->flags2 & MF2_CLASSICPUSH)) mo->momx = mo->momy = 0; @@ -2974,7 +2956,7 @@ void P_PlayerZMovement(mobj_t *mo) } // Get up if you fell. if (mo->player->panim == PA_PAIN) - P_SetPlayerMobjState(mo, S_PLAY_WALK); + P_SetMobjState(mo, S_PLAY_WALK); if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) { // Handle landing on slope during Z movement @@ -2983,7 +2965,7 @@ void P_PlayerZMovement(mobj_t *mo) if (P_MobjFlip(mo)*mo->momz < 0) // falling { - boolean clipmomz = !(P_CheckDeathPitCollide(mo)); + boolean clipmomz; mo->pmomz = 0; // We're on a new floor, don't keep doing platform movement. @@ -3091,6 +3073,13 @@ boolean P_SceneryZMovement(mobj_t *mo) mo->eflags &= ~MFE_APPLYPMOMZ; } mo->z += mo->momz; + + if (!mo->player && P_CheckDeathPitCollide(mo) && mo->health + && !(mo->flags & MF_NOCLIPHEIGHT) && !(mo->flags2 & MF2_BOSSDEAD)) + { + P_RemoveMobj(mo); + return false; + } switch (mo->type) { @@ -3104,11 +3093,6 @@ boolean P_SceneryZMovement(mobj_t *mo) } break; case MT_MEDIUMBUBBLE: - if (P_CheckDeathPitCollide(mo)) // Don't split if you fell in a pit - { - P_RemoveMobj(mo); - return false; - } if ((!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= mo->floorz) || (mo->eflags & MFE_VERTICALFLIP && mo->z+mo->height >= mo->ceilingz)) // Hit the floor, so split! { @@ -3120,6 +3104,8 @@ boolean P_SceneryZMovement(mobj_t *mo) { prandom = P_RandomByte(); explodemo = P_SpawnMobj(mo->x, mo->y, mo->z, MT_SMALLBUBBLE); + if (P_MobjWasRemoved(explodemo)) + continue; explodemo->momx += ((prandom & 0x0F) << (FRACBITS-2)) * (i & 2 ? -1 : 1); explodemo->momy += ((prandom & 0xF0) << (FRACBITS-6)) * (i & 1 ? -1 : 1); explodemo->destscale = mo->scale; @@ -3141,11 +3127,6 @@ boolean P_SceneryZMovement(mobj_t *mo) } break; case MT_SEED: // now scenery - if (P_CheckDeathPitCollide(mo)) // No flowers for death pits - { - P_RemoveMobj(mo); - return false; - } // Soniccd seed turns into a flower! if ((!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= mo->floorz) || (mo->eflags & MFE_VERTICALFLIP && mo->z+mo->height >= mo->ceilingz)) @@ -3166,13 +3147,6 @@ boolean P_SceneryZMovement(mobj_t *mo) break; } - if (P_CheckDeathPitCollide(mo)) - { - if (mo->type != MT_GHOST) // ghosts play death animations instead, so don't remove them - P_RemoveMobj(mo); - return false; - } - // clip movement if (((mo->z <= mo->floorz && !(mo->eflags & MFE_VERTICALFLIP)) || (mo->z + mo->height >= mo->ceilingz && mo->eflags & MFE_VERTICALFLIP)) @@ -3392,13 +3366,19 @@ void P_MobjCheckWater(mobj_t *mobj) if (mobj->eflags & MFE_VERTICALFLIP) { splish = P_SpawnMobj(mobj->x, mobj->y, mobj->waterbottom-FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); - splish->flags2 |= MF2_OBJECTFLIP; - splish->eflags |= MFE_VERTICALFLIP; + if (!P_MobjWasRemoved(splish)) + { + splish->flags2 |= MF2_OBJECTFLIP; + splish->eflags |= MFE_VERTICALFLIP; + } } else splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype); - splish->destscale = mobj->scale; - P_SetScale(splish, mobj->scale); + if (!P_MobjWasRemoved(splish)) + { + splish->destscale = mobj->scale; + P_SetScale(splish, mobj->scale); + } } // skipping stone! @@ -3428,13 +3408,19 @@ void P_MobjCheckWater(mobj_t *mobj) if (mobj->eflags & MFE_VERTICALFLIP) { splish = P_SpawnMobj(mobj->x, mobj->y, mobj->waterbottom-FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); - splish->flags2 |= MF2_OBJECTFLIP; - splish->eflags |= MFE_VERTICALFLIP; + if (!P_MobjWasRemoved(splish)) + { + splish->flags2 |= MF2_OBJECTFLIP; + splish->eflags |= MFE_VERTICALFLIP; + } } else splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype); - splish->destscale = mobj->scale; - P_SetScale(splish, mobj->scale); + if (!P_MobjWasRemoved(splish)) + { + splish->destscale = mobj->scale; + P_SetScale(splish, mobj->scale); + } } } @@ -3957,7 +3943,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj) { mobj->player->secondjump = 0; mobj->player->powers[pw_tailsfly] = 0; - P_SetPlayerMobjState(mobj, S_PLAY_WALK); + P_SetMobjState(mobj, S_PLAY_WALK); } #endif mobj->eflags &= ~MFE_JUSTHITFLOOR; @@ -4444,11 +4430,14 @@ static void P_Boss3Thinker(mobj_t *mobj) } dummy = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->info->mass); - dummy->angle = mobj->angle; - dummy->threshold = way1; - P_SetTarget(&dummy->tracer, mobj); - dummy->movefactor = mobj->movefactor; - dummy->cusval = mobj->cusval; + if (!P_MobjWasRemoved(dummy)) + { + dummy->angle = mobj->angle; + dummy->threshold = way1; + P_SetTarget(&dummy->tracer, mobj); + dummy->movefactor = mobj->movefactor; + dummy->cusval = mobj->cusval; + } way2 = P_RandomKey(8-3); if (way2 >= curpath) @@ -4467,11 +4456,14 @@ static void P_Boss3Thinker(mobj_t *mobj) } dummy = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->info->mass); - dummy->angle = mobj->angle; - dummy->threshold = way2; - P_SetTarget(&dummy->tracer, mobj); - dummy->movefactor = mobj->movefactor; - dummy->cusval = mobj->cusval; + if (!P_MobjWasRemoved(dummy)) + { + dummy->angle = mobj->angle; + dummy->threshold = way2; + P_SetTarget(&dummy->tracer, mobj); + dummy->movefactor = mobj->movefactor; + dummy->cusval = mobj->cusval; + } CONS_Debug(DBG_GAMELOGIC, "Eggman path %d - Dummy selected paths %d and %d\n", way0, way1, way2); if (mobj->spawnpoint) @@ -4589,6 +4581,8 @@ static void P_Boss3Thinker(mobj_t *mobj) for (i = 0; i < numtospawn; i++) { shock = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SHOCKWAVE); + if (P_MobjWasRemoved(shock)) + continue; P_SetTarget(&shock->target, mobj); shock->fuse = shock->info->painchance; @@ -4608,7 +4602,8 @@ static void P_Boss3Thinker(mobj_t *mobj) ang += interval; sprev = shock; } - S_StartSound(mobj, shock->info->seesound); + if (!P_MobjWasRemoved(shock)) + S_StartSound(mobj, shock->info->seesound); // look for a new target P_BossTargetPlayer(mobj, true); @@ -4858,12 +4853,17 @@ static void P_Boss4Thinker(mobj_t *mobj) for (arm = 0; arm <3 ; arm++) { seg = P_SpawnMobj(mobj->x, mobj->y, z, MT_EGGMOBILE4_MACE); + if (P_MobjWasRemoved(seg)) + continue; + P_SetTarget(&base->tracer, seg); base = seg; P_SetTarget(&seg->target, mobj); for (i = 0; i < 9; i++) { P_SetTarget(&seg->hnext, P_SpawnMobj(mobj->x, mobj->y, z, MT_EGGMOBILE4_MACE)); + if (P_MobjWasRemoved(seg->hnext)) + continue; P_SetTarget(&seg->hnext->hprev, seg); seg = seg->hnext; } @@ -5134,9 +5134,12 @@ static void P_Boss7Thinker(mobj_t *mobj) if (mobj->health >= mobj->info->spawnhealth && (leveltime & 14) == 0) { mobj_t *smoke = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height, MT_SMOKE); - smoke->destscale = mobj->destscale; - P_SetScale(smoke, smoke->destscale); - smoke->momz = FixedMul(FRACUNIT, smoke->scale); + if (!P_MobjWasRemoved(smoke)) + { + smoke->destscale = mobj->destscale; + P_SetScale(smoke, smoke->destscale); + smoke->momz = FixedMul(FRACUNIT, smoke->scale); + } } if (mobj->state == &states[S_BLACKEGG_STND] && mobj->tics == mobj->state->tics) @@ -5410,6 +5413,8 @@ static void P_Boss7Thinker(mobj_t *mobj) y = mobj->y + FixedMul(FINECOSINE(fa),ns); mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE); + if (P_MobjWasRemoved(mo2)) + continue; ns = 16 * FRACUNIT; mo2->momx = FixedMul(FINESINE(fa),ns); mo2->momy = FixedMul(FINECOSINE(fa),ns); @@ -5603,76 +5608,84 @@ static void P_Boss9Thinker(mobj_t *mobj) mobj_t *missile; missile = P_SpawnMissile(mobj, mobj->target, MT_MSGATHER); - S_StopSound(missile); - if (mobj->extravalue1 >= 2) - P_SetScale(missile, FRACUNIT>>1); - missile->destscale = missile->scale>>1; - missile->fuse = TICRATE/2; - missile->scalespeed = abs(missile->destscale - missile->scale)/missile->fuse; - missile->z -= missile->height/2; - missile->momx *= -1; - missile->momy *= -1; - missile->momz *= -1; + if (!P_MobjWasRemoved(missile)) + { + S_StopSound(missile); + if (mobj->extravalue1 >= 2) + P_SetScale(missile, FRACUNIT>>1); + missile->destscale = missile->scale>>1; + missile->fuse = TICRATE/2; + missile->scalespeed = abs(missile->destscale - missile->scale)/missile->fuse; + missile->z -= missile->height/2; + missile->momx *= -1; + missile->momy *= -1; + missile->momz *= -1; - if (mobj->extravalue1 == 2) - { - UINT8 i; - mobj_t *spread; - for (i = 0; i < 5; i++) + if (mobj->extravalue1 == 2) { - if (i == 2) - continue; - spread = P_SpawnMobj(missile->x, missile->y, missile->z, missile->type); - spread->angle = missile->angle+(ANGLE_11hh/2)*(i-2); - P_InstaThrust(spread,spread->angle,-spread->info->speed); - spread->momz = missile->momz; - P_SetScale(spread, missile->scale); - spread->destscale = missile->destscale; - spread->scalespeed = missile->scalespeed; - spread->fuse = missile->fuse; - P_UnsetThingPosition(spread); - spread->x -= spread->fuse*spread->momx; - spread->y -= spread->fuse*spread->momy; - spread->z -= spread->fuse*spread->momz; - P_SetThingPosition(spread); - } - P_InstaThrust(missile,missile->angle,-missile->info->speed); - } - else if (mobj->extravalue1 >= 3) - { - UINT8 i; - mobj_t *spread; - mobj->target->z -= (4*missile->height); - for (i = 0; i < 5; i++) - { - if (i != 2) + UINT8 i; + mobj_t *spread; + for (i = 0; i < 5; i++) { - spread = P_SpawnMissile(mobj, mobj->target, missile->type); + if (i == 2) + continue; + spread = P_SpawnMobj(missile->x, missile->y, missile->z, missile->type); + if (P_MobjWasRemoved(spread)) + continue; + + spread->angle = missile->angle+(ANGLE_11hh/2)*(i-2); + P_InstaThrust(spread,spread->angle,-spread->info->speed); + spread->momz = missile->momz; P_SetScale(spread, missile->scale); spread->destscale = missile->destscale; + spread->scalespeed = missile->scalespeed; spread->fuse = missile->fuse; - spread->z -= spread->height/2; - spread->momx *= -1; - spread->momy *= -1; - spread->momz *= -1; P_UnsetThingPosition(spread); spread->x -= spread->fuse*spread->momx; spread->y -= spread->fuse*spread->momy; spread->z -= spread->fuse*spread->momz; P_SetThingPosition(spread); } - mobj->target->z += missile->height*2; + P_InstaThrust(missile,missile->angle,-missile->info->speed); } - mobj->target->z -= (6*missile->height); + else if (mobj->extravalue1 >= 3) + { + UINT8 i; + mobj_t *spread; + mobj->target->z -= (4*missile->height); + for (i = 0; i < 5; i++) + { + if (i != 2) + { + spread = P_SpawnMissile(mobj, mobj->target, missile->type); + if (P_MobjWasRemoved(spread)) + continue; + P_SetScale(spread, missile->scale); + spread->destscale = missile->destscale; + spread->fuse = missile->fuse; + spread->z -= spread->height/2; + spread->momx *= -1; + spread->momy *= -1; + spread->momz *= -1; + P_UnsetThingPosition(spread); + spread->x -= spread->fuse*spread->momx; + spread->y -= spread->fuse*spread->momy; + spread->z -= spread->fuse*spread->momz; + P_SetThingPosition(spread); + } + mobj->target->z += missile->height*2; + } + mobj->target->z -= (6*missile->height); + } + + P_UnsetThingPosition(missile); + missile->x -= missile->fuse*missile->momx; + missile->y -= missile->fuse*missile->momy; + missile->z -= missile->fuse*missile->momz; + P_SetThingPosition(missile); + + S_StartSound(mobj, sfx_s3kb3); } - - P_UnsetThingPosition(missile); - missile->x -= missile->fuse*missile->momx; - missile->y -= missile->fuse*missile->momy; - missile->z -= missile->fuse*missile->momz; - P_SetThingPosition(missile); - - S_StartSound(mobj, sfx_s3kb3); } } } @@ -5690,29 +5703,32 @@ static void P_Boss9Thinker(mobj_t *mobj) if (spawner && dist) { mobj_t *missile = P_SpawnMissile(spawner, mobj, MT_MSGATHER); - missile->fuse = (dist/P_AproxDistance(missile->momx, missile->momy)); - if (missile->fuse <= 0) // Prevents a division by zero when calculating missile->scalespeed - missile->fuse = 1; + if (!P_MobjWasRemoved(missile)) + { + missile->fuse = (dist/P_AproxDistance(missile->momx, missile->momy)); + if (missile->fuse <= 0) // Prevents a division by zero when calculating missile->scalespeed + missile->fuse = 1; - if (missile->fuse > mobj->fuse) - { - P_RemoveMobj(missile); - } - else - { - if (mobj->health > mobj->info->damage) + if (missile->fuse > mobj->fuse) { - P_SetScale(missile, FRACUNIT/3); - missile->color = SKINCOLOR_MAGENTA; // sonic OVA/4 purple power + P_RemoveMobj(missile); } else { - P_SetScale(missile, FRACUNIT/5); - missile->color = SKINCOLOR_SUNSET; // sonic cd electric power + if (mobj->health > mobj->info->damage) + { + P_SetScale(missile, FRACUNIT/3); + missile->color = SKINCOLOR_MAGENTA; // sonic OVA/4 purple power + } + else + { + P_SetScale(missile, FRACUNIT/5); + missile->color = SKINCOLOR_SUNSET; // sonic cd electric power + } + missile->destscale = missile->scale*2; + missile->scalespeed = abs(missile->scale - missile->destscale)/missile->fuse; + missile->colorized = true; } - missile->destscale = missile->scale*2; - missile->scalespeed = abs(missile->scale - missile->destscale)/missile->fuse; - missile->colorized = true; } } @@ -5746,6 +5762,7 @@ static void P_Boss9Thinker(mobj_t *mobj) // threshold is used for attacks/maneuvers. if (mobj->threshold && mobj->movecount != 2) { + mobj_t *ghost; fixed_t speed = 20*FRACUNIT + FixedMul(40*FRACUNIT, FixedDiv((mobj->info->spawnhealth - mobj->health)<info->spawnhealth<target, mobj->info->speed); - if (mobj->extravalue1 >= 2) + if (!P_MobjWasRemoved(missile)) { - missile->destscale = FRACUNIT>>1; - P_SetScale(missile, missile->destscale); - } - missile->fuse = 3*TICRATE; - missile->z -= missile->height/2; - - if (mobj->extravalue1 == 2) - { - UINT8 i; - mobj_t *spread; - for (i = 0; i < 5; i++) + if (mobj->extravalue1 >= 2) { - if (i == 2) - continue; - spread = P_SpawnMobj(missile->x, missile->y, missile->z, missile->type); - spread->angle = missile->angle+(ANGLE_11hh/2)*(i-2); - P_InstaThrust(spread,spread->angle,spread->info->speed); - spread->momz = missile->momz; - spread->destscale = FRACUNIT>>1; - P_SetScale(spread, spread->destscale); - spread->fuse = missile->fuse; + missile->destscale = FRACUNIT>>1; + P_SetScale(missile, missile->destscale); } - P_InstaThrust(missile,missile->angle,missile->info->speed); - } - else if (mobj->extravalue1 >= 3) - { - UINT8 i; - mobj_t *spread; - mobj->target->z -= (2*missile->height); - for (i = 0; i < 5; i++) + missile->fuse = 3*TICRATE; + missile->z -= missile->height/2; + + if (mobj->extravalue1 == 2) { - if (i != 2) + UINT8 i; + mobj_t *spread; + for (i = 0; i < 5; i++) { - spread = P_SpawnMissile(mobj, mobj->target, missile->type); + if (i == 2) + continue; + spread = P_SpawnMobj(missile->x, missile->y, missile->z, missile->type); + if (P_MobjWasRemoved(spread)) + continue; + + spread->angle = missile->angle+(ANGLE_11hh/2)*(i-2); + P_InstaThrust(spread,spread->angle,spread->info->speed); + spread->momz = missile->momz; spread->destscale = FRACUNIT>>1; P_SetScale(spread, spread->destscale); spread->fuse = missile->fuse; - spread->z -= spread->height/2; } - mobj->target->z += missile->height; + P_InstaThrust(missile,missile->angle,missile->info->speed); + } + else if (mobj->extravalue1 >= 3) + { + UINT8 i; + mobj_t *spread; + mobj->target->z -= (2*missile->height); + for (i = 0; i < 5; i++) + { + if (i != 2) + { + spread = P_SpawnMissile(mobj, mobj->target, missile->type); + if (!P_MobjWasRemoved(spread)) + { + spread->destscale = FRACUNIT>>1; + P_SetScale(spread, spread->destscale); + spread->fuse = missile->fuse; + spread->z -= spread->height/2; + } + } + mobj->target->z += missile->height; + } + mobj->target->z -= (3*missile->height); } - mobj->target->z -= (3*missile->height); } } else @@ -5867,7 +5893,9 @@ static void P_Boss9Thinker(mobj_t *mobj) return; } - P_SpawnGhostMobj(mobj)->colorized = false; + ghost = P_SpawnGhostMobj(mobj); + if (!P_MobjWasRemoved(ghost)) + ghost->colorized = false; // Vector form dodge! mobj->angle += mobj->movedir; @@ -5965,8 +5993,11 @@ static void P_Boss9Thinker(mobj_t *mobj) if (mobj->health > mobj->info->damage) { // No more bubble if we're broken (pinch phase) mobj_t *shield = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_MSSHIELD_FRONT); - P_SetTarget(&mobj->hprev, shield); - P_SetTarget(&shield->target, mobj); + if (!P_MobjWasRemoved(shield)) + { + P_SetTarget(&mobj->hprev, shield); + P_SetTarget(&shield->target, mobj); + } // Attack 2: Energy shot! switch (mobj->health) @@ -6301,7 +6332,8 @@ void P_SpawnHoopOfSomething(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT finalz = z + v.z; mobj = P_SpawnMobj(finalx, finaly, finalz, type); - mobj->z -= mobj->height/2; + if (!P_MobjWasRemoved(mobj)) + mobj->z -= mobj->height/2; } } @@ -6342,6 +6374,8 @@ void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 numb finalz = z + v.z; mobj = P_SpawnMobj(finalx, finaly, finalz, type); + if (P_MobjWasRemoved(mobj)) + continue; mobj->z -= mobj->height>>1; @@ -6766,15 +6800,33 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield) P_SetScale(thing, FixedMul(thing->target->scale, thing->target->player->shieldscale)); thing->destscale = thing->scale; + thing->old_scale = FixedMul(thing->target->old_scale, thing->target->player->shieldscale); + +#define NewMH(mobj) mobj->height // Ugly mobj-height and player-height defines, for the sake of prettier code +#define NewPH(player) P_GetPlayerHeight(player) +#define OldMH(mobj) FixedMul(mobj->height, FixedDiv(mobj->old_scale, mobj->scale)) +#define OldPH(player) FixedMul(player->height, player->mo->old_scale) P_UnsetThingPosition(thing); thing->x = thing->target->x; thing->y = thing->target->y; + thing->old_x = thing->target->old_x; + thing->old_y = thing->target->old_y; if (thing->eflags & MFE_VERTICALFLIP) - thing->z = thing->target->z + (thing->target->height - thing->height + FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) - FixedMul(2*FRACUNIT, thing->target->scale); + { + thing->z = thing->target->z + NewMH(thing->target) - NewMH(thing) + ((NewPH(thing->target->player) - NewMH(thing->target)) / 3) - (thing->target->scale * 2); + thing->old_z = thing->target->old_z + OldMH(thing->target) - OldMH(thing) + ((OldPH(thing->target->player) - OldMH(thing->target)) / 3) - (thing->target->old_scale * 2); + } else - thing->z = thing->target->z - (FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) + FixedMul(2*FRACUNIT, thing->target->scale); + { + thing->z = thing->target->z - ((NewPH(thing->target->player) - NewMH(thing->target)) / 3) + (thing->target->scale * 2); + thing->old_z = thing->target->old_z - ((OldPH(thing->target->player) - OldMH(thing->target)) / 3) + (thing->target->old_scale * 2); + } P_SetThingPosition(thing); P_CheckPosition(thing, thing->x, thing->y); +#undef NewMH +#undef NewPH +#undef OldMH +#undef OldPH if (P_MobjWasRemoved(thing)) return false; @@ -6840,10 +6892,11 @@ void P_RunOverlays(void) { // run overlays mobj_t *mo, *next = NULL; - fixed_t destx,desty,zoffs; for (mo = overlaycap; mo; mo = next) { + fixed_t zoffs; + I_Assert(!P_MobjWasRemoved(mo)); // grab next in chain, then unset the chain target @@ -6859,31 +6912,11 @@ void P_RunOverlays(void) continue; } - if (!splitscreen /*&& rendermode != render_soft*/) - { - angle_t viewingangle; - - if (players[displayplayer].awayviewtics && players[displayplayer].awayviewmobj != NULL && !P_MobjWasRemoved(players[displayplayer].awayviewmobj)) - viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y); - else if (!camera.chase && players[displayplayer].mo) - viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y); - else - viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, camera.x, camera.y); - - if (!(mo->state->frame & FF_ANIMATE) && mo->state->var1) - viewingangle += ANGLE_180; - destx = mo->target->x + P_ReturnThrustX(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale)); - desty = mo->target->y + P_ReturnThrustY(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale)); - } - else - { - destx = mo->target->x; - desty = mo->target->y; - } - mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP); mo->scale = mo->destscale = mo->target->scale; + mo->old_scale = mo->target->old_scale; mo->angle = (mo->target->player ? mo->target->player->drawangle : mo->target->angle) + mo->movedir; + mo->old_angle = (mo->target->player ? mo->target->player->old_drawangle : mo->target->old_angle) + mo->movedir; if (!(mo->state->frame & FF_ANIMATE)) zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale); @@ -6893,15 +6926,26 @@ void P_RunOverlays(void) zoffs = 0; P_UnsetThingPosition(mo); - mo->x = destx; - mo->y = desty; + mo->x = mo->target->x; + mo->y = mo->target->y; + mo->old_x = mo->target->old_x; + mo->old_y = mo->target->old_y; mo->radius = mo->target->radius; mo->height = mo->target->height; if (mo->eflags & MFE_VERTICALFLIP) - mo->z = (mo->target->z + mo->target->height - mo->height) - zoffs; + { + mo->z = mo->target->z + mo->target->height - mo->height - zoffs; + if (mo->scale == mo->old_scale) + mo->old_z = mo->target->old_z + mo->target->height - mo->height - zoffs; + else // Interpolate height scale changes - mo and mo->target have the same scales here, so don't interpolate them individually + mo->old_z = mo->target->old_z + FixedMul(mo->target->height - mo->height, FixedDiv(mo->old_scale, mo->scale)) - zoffs; + } else - mo->z = mo->target->z + zoffs; - if (mo->state->var1) + { + mo->z = mo->target->z + zoffs; + mo->old_z = mo->target->old_z + zoffs; + } + if (!(mo->state->frame & FF_ANIMATE) && mo->state->var1) P_SetUnderlayPosition(mo); else P_SetThingPosition(mo); @@ -6998,6 +7042,8 @@ static void P_KoopaThinker(mobj_t *koopa) { mobj_t *flame; flame = P_SpawnMobj(koopa->x - koopa->radius + FixedMul(5*FRACUNIT, koopa->scale), koopa->y, koopa->z + (P_RandomByte()<<(FRACBITS-2)), MT_KOOPAFLAME); + if (P_MobjWasRemoved(flame)) + return; flame->momx = -FixedMul(flame->info->speed, flame->scale); S_StartSound(flame, sfx_koopfr); } @@ -7005,6 +7051,8 @@ static void P_KoopaThinker(mobj_t *koopa) { mobj_t *hammer; hammer = P_SpawnMobj(koopa->x - koopa->radius, koopa->y, koopa->z + koopa->height, MT_HAMMER); + if (P_MobjWasRemoved(hammer)) + return; hammer->momx = FixedMul(-5*FRACUNIT, hammer->scale); hammer->momz = FixedMul(7*FRACUNIT, hammer->scale); } @@ -7023,6 +7071,9 @@ static void P_SpawnMinecartSegments(mobj_t *mobj, boolean mode) for (i = 0; i < 4; i++) { seg = P_SpawnMobj(x, y, z, MT_MINECARTSEG); + if (P_MobjWasRemoved(seg)) + continue; + P_SetMobjState(seg, (statenum_t)(S_MINECARTSEG_FRONT + i)); if (i >= 2) seg->extravalue1 = (i == 2) ? -18 : 18; // make -20/20 when papersprite projection fixed @@ -7078,6 +7129,8 @@ static void P_PyreFlyBurn(mobj_t *mobj, fixed_t hoffs, INT16 vrange, mobjtype_t fixed_t yoffs = FixedMul(FINESINE(fa), mobj->radius + hoffs); fixed_t zoffs = P_RandomRange(-vrange, vrange)*FRACUNIT; mobj_t *particle = P_SpawnMobjFromMobj(mobj, xoffs, yoffs, zoffs, mobjtype); + if (P_MobjWasRemoved(particle)) + return; particle->momz = momz; particle->flags2 |= MF2_LINKDRAW; P_SetTarget(&particle->tracer, mobj); @@ -7236,6 +7289,8 @@ static void P_FlameJetSceneryThink(mobj_t *mobj) mobj->fuse -= 2; flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME); + if (P_MobjWasRemoved(flame)) + return; P_SetMobjState(flame, S_FLAMEJETFLAME4); flame->angle = mobj->angle; @@ -7274,6 +7329,8 @@ static void P_VerticalFlameJetSceneryThink(mobj_t *mobj) mobj->fuse--; flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME); + if (P_MobjWasRemoved(flame)) + return; strength = (mobj->movedir ? mobj->movedir : 80)<<(FRACBITS-2); @@ -7347,13 +7404,16 @@ static boolean P_ParticleGenSceneryThink(mobj_t *mobj) mobj->y + FixedMul(FixedMul(mobj->friction, mobj->scale), FINESINE(mobj->angle >> ANGLETOFINESHIFT)), mobj->z, (mobjtype_t)mobj->threshold); - P_SetScale(spawn, mobj->scale); - spawn->momz = FixedMul(mobj->movefactor, spawn->scale); - spawn->destscale = spawn->scale/100; - spawn->scalespeed = spawn->scale/mobj->health; - spawn->tics = (tic_t)mobj->health; - spawn->flags2 |= (mobj->flags2 & MF2_OBJECTFLIP); - spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones + if (!P_MobjWasRemoved(spawn)) + { + P_SetScale(spawn, mobj->scale); + spawn->momz = FixedMul(mobj->movefactor, spawn->scale); + spawn->destscale = spawn->scale/100; + spawn->scalespeed = spawn->scale/mobj->health; + spawn->tics = (tic_t)mobj->health; + spawn->flags2 |= (mobj->flags2 & MF2_OBJECTFLIP); + spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones + } mobj->angle += mobj->movedir; } @@ -7549,7 +7609,7 @@ static void P_RosySceneryThink(mobj_t *mobj) if (stat == S_ROSY_HUG) { if (player->panim != PA_IDLE) - P_SetPlayerMobjState(mobj->target, S_PLAY_STND); + P_SetMobjState(mobj->target, S_PLAY_STND); player->pflags |= PF_STASIS; } @@ -7565,13 +7625,16 @@ static void P_RosySceneryThink(mobj_t *mobj) if (makeheart) { mobj_t *cdlhrt = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_CDLHRT); - cdlhrt->destscale = (5*mobj->scale) >> 4; - P_SetScale(cdlhrt, cdlhrt->destscale); - cdlhrt->fuse = (5*TICRATE) >> 1; - cdlhrt->momz = mobj->scale; - P_SetTarget(&cdlhrt->target, mobj); - cdlhrt->extravalue1 = mobj->x; - cdlhrt->extravalue2 = mobj->y; + if (!P_MobjWasRemoved(cdlhrt)) + { + cdlhrt->destscale = (5*mobj->scale) >> 4; + P_SetScale(cdlhrt, cdlhrt->destscale); + cdlhrt->fuse = (5*TICRATE) >> 1; + cdlhrt->momz = mobj->scale; + P_SetTarget(&cdlhrt->target, mobj); + cdlhrt->extravalue1 = mobj->x; + cdlhrt->extravalue2 = mobj->y; + } } } } @@ -7698,13 +7761,16 @@ static void P_MobjSceneryThink(mobj_t *mobj) && */ (mobj->target->player->pflags & PF_SHIELDABILITY)) { mobj_t *whoosh = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_GHOST); // done here so the offset is correct - P_SetMobjState(whoosh, mobj->info->raisestate); - whoosh->destscale = whoosh->scale << 1; - whoosh->scalespeed = FixedMul(whoosh->scalespeed, whoosh->scale); - whoosh->height = 38*whoosh->scale; - whoosh->fuse = 10; - whoosh->flags |= MF_NOCLIPHEIGHT; - whoosh->momz = mobj->target->momz; // Stay reasonably centered for a few frames + if (!P_MobjWasRemoved(whoosh)) + { + P_SetMobjState(whoosh, mobj->info->raisestate); + whoosh->destscale = whoosh->scale << 1; + whoosh->scalespeed = FixedMul(whoosh->scalespeed, whoosh->scale); + whoosh->height = 38*whoosh->scale; + whoosh->fuse = 10; + whoosh->flags |= MF_NOCLIPHEIGHT; + whoosh->momz = mobj->target->momz; // Stay reasonably centered for a few frames + } mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal whoosh } /* FALLTHRU */ @@ -7795,7 +7861,6 @@ static void P_MobjSceneryThink(mobj_t *mobj) if (!(mobj->eflags & MFE_UNDERWATER) || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z + mobj->height >= mobj->ceilingz) || (mobj->eflags & MFE_VERTICALFLIP && mobj->z <= mobj->floorz) - || (P_CheckDeathPitCollide(mobj)) || --mobj->fuse <= 0) // Bubbles eventually dissipate if they can't reach the surface. { // no playing sound: no point; the object is being removed @@ -8040,8 +8105,11 @@ static boolean P_MobjBossThink(mobj_t *mobj) P_RandomRange(rad, -rad) << FRACBITS, P_RandomRange(hei / 2, hei) << FRACBITS, MT_SMOKE); - P_SetObjectMomZ(particle, 2 << FRACBITS, false); - particle->momz += mobj->momz; + if (!P_MobjWasRemoved(particle)) + { + P_SetObjectMomZ(particle, 2 << FRACBITS, false); + particle->momz += mobj->momz; + } } if (mobj->flags2 & MF2_SKULLFLY) #if 1 @@ -8050,8 +8118,11 @@ static boolean P_MobjBossThink(mobj_t *mobj) { mobj_t *spawnmobj; spawnmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->info->painchance); - P_SetTarget(&spawnmobj->target, mobj); - spawnmobj->color = SKINCOLOR_GREY; + if (!P_MobjWasRemoved(spawnmobj)) + { + P_SetTarget(&spawnmobj->target, mobj); + spawnmobj->color = SKINCOLOR_GREY; + } } #endif P_Boss1Thinker(mobj); @@ -8066,8 +8137,11 @@ static boolean P_MobjBossThink(mobj_t *mobj) P_RandomRange(rad, -rad) << FRACBITS, P_RandomRange(hei/2, hei) << FRACBITS, MT_SMOKE); - P_SetObjectMomZ(particle, 2 << FRACBITS, false); - particle->momz += mobj->momz; + if (!P_MobjWasRemoved(particle)) + { + P_SetObjectMomZ(particle, 2 << FRACBITS, false); + particle->momz += mobj->momz; + } } P_Boss2Thinker(mobj); break; @@ -8081,8 +8155,11 @@ static boolean P_MobjBossThink(mobj_t *mobj) P_RandomRange(rad, -rad) << FRACBITS, P_RandomRange(hei/2, hei) << FRACBITS, MT_SMOKE); - P_SetObjectMomZ(particle, 2 << FRACBITS, false); - particle->momz += mobj->momz; + if (!P_MobjWasRemoved(particle)) + { + P_SetObjectMomZ(particle, 2 << FRACBITS, false); + particle->momz += mobj->momz; + } } P_Boss3Thinker(mobj); break; @@ -8096,8 +8173,11 @@ static boolean P_MobjBossThink(mobj_t *mobj) P_RandomRange(rad, -rad) << FRACBITS, P_RandomRange(hei/2, hei) << FRACBITS, MT_SMOKE); - P_SetObjectMomZ(particle, 2 << FRACBITS, false); - particle->momz += mobj->momz; + if (!P_MobjWasRemoved(particle)) + { + P_SetObjectMomZ(particle, 2 << FRACBITS, false); + particle->momz += mobj->momz; + } } P_Boss4Thinker(mobj); break; @@ -8225,6 +8305,9 @@ static boolean P_MobjDeadThink(mobj_t *mobj) y = mobj->y + FixedMul(FINECOSINE(fa), ns); mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE); + if (P_MobjWasRemoved(mo2)) + continue; + P_SetMobjStateNF(mo2, S_XPLD_EGGTRAP); // so the flickies don't lose their target if they spawn ns = 4*FRACUNIT; mo2->momx = FixedMul(FINESINE(fa), ns); @@ -8270,7 +8353,8 @@ static boolean P_MobjDeadThink(mobj_t *mobj) mobj->y + (P_RandomRange(r, -r) << FRACBITS), mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS), MT_SONIC3KBOSSEXPLODE); - S_StartSound(explosion, sfx_s3kb4); + if (!P_MobjWasRemoved(explosion)) + S_StartSound(explosion, sfx_s3kb4); } if (mobj->movedir == DMG_DROWNED) P_SetObjectMomZ(mobj, -FRACUNIT/2, true); // slower fall from drowning @@ -8288,7 +8372,8 @@ static boolean P_MobjDeadThink(mobj_t *mobj) mobj->y + (P_RandomRange(r, -r) << FRACBITS), mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS), MT_SONIC3KBOSSEXPLODE); - S_StartSound(explosion, sfx_s3kb4); + if (!P_MobjWasRemoved(explosion)) + S_StartSound(explosion, sfx_s3kb4); } P_SetObjectMomZ(mobj, -2*FRACUNIT/3, true); } @@ -8386,9 +8471,12 @@ static void P_ArrowThink(mobj_t *mobj) if (leveltime & 1) { mobj_t *dust = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_PARTICLE); - dust->tics = 18; - dust->scalespeed = 4096; - dust->destscale = FRACUNIT/32; + if (!P_MobjWasRemoved(dust)) + { + dust->tics = 18; + dust->scalespeed = 4096; + dust->destscale = FRACUNIT/32; + } } } else @@ -8991,6 +9079,9 @@ static boolean P_TurretThink(mobj_t *mobj) y = mobj->y + FixedMul(FINECOSINE(fa), ns); mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE); + if (P_MobjWasRemoved(mo2)) + continue; + ns = FixedMul(16*FRACUNIT, mobj->scale); mo2->momx = FixedMul(FINESINE(fa), ns); mo2->momy = FixedMul(FINECOSINE(fa), ns); @@ -9212,10 +9303,13 @@ static void P_DragonbomberThink(mobj_t *mobj) if (segment != mobj) // found an unactivated segment? { mobj_t *mine = P_SpawnMobjFromMobj(segment, 0, 0, 0, segment->info->painchance); - mine->angle = segment->angle; - P_InstaThrust(mine, mobj->angle, P_AproxDistance(mobj->momx, mobj->momy) >> 1); - P_SetObjectMomZ(mine, -2*FRACUNIT, true); - S_StartSound(mine, mine->info->seesound); + if (!P_MobjWasRemoved(mine)) + { + mine->angle = segment->angle; + P_InstaThrust(mine, mobj->angle, P_AproxDistance(mobj->momx, mobj->momy) >> 1); + P_SetObjectMomZ(mine, -2*FRACUNIT, true); + S_StartSound(mine, mine->info->seesound); + } P_SetMobjState(segment, segment->info->raisestate); mobj->threshold = mobj->info->painchance; } @@ -9434,9 +9528,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (!mobj->threshold && !mobj->target && mobj->reactiontime) { mobj_t *emerald = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->reactiontime); - emerald->threshold = 42; - P_SetTarget(&mobj->target, emerald); - P_SetTarget(&emerald->target, mobj); + if (!P_MobjWasRemoved(emerald)) + { + emerald->threshold = 42; + P_SetTarget(&mobj->target, emerald); + P_SetTarget(&emerald->target, mobj); + } } } break; @@ -9783,6 +9880,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_TRAINDUSTSPAWNER: if (leveltime % 5 == 0) { mobj_t* traindust = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PARTICLE); + if (P_MobjWasRemoved(traindust)) + break; traindust->flags = MF_SCENERY; P_SetMobjState(traindust, S_TRAINDUST); traindust->frame = P_RandomRange(0, 8)|FF_TRANS90; @@ -9796,6 +9895,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_TRAINSTEAMSPAWNER: if (leveltime % 5 == 0) { mobj_t *steam = P_SpawnMobj(mobj->x + FRACUNIT*P_SignedRandom()/2, mobj->y + FRACUNIT*P_SignedRandom()/2, mobj->z, MT_PARTICLE); + if (P_MobjWasRemoved(steam)) + break; P_SetMobjState(steam, S_TRAINSTEAM); steam->frame = P_RandomRange(0, 1)|FF_TRANS90; steam->tics = TICRATE*8; @@ -9997,7 +10098,8 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type); // Transfer flags2 (ambush, strongbox, objectflip) - newmobj->flags2 = mobj->flags2; + if (!P_MobjWasRemoved(newmobj)) + newmobj->flags2 = mobj->flags2; P_RemoveMobj(mobj); // make sure they disappear } @@ -10019,6 +10121,8 @@ static void P_FlagFuseThink(mobj_t *mobj) else z = ss->sector->floorheight + z; flagmo = P_SpawnMobj(x, y, z, mobj->type); + if (P_MobjWasRemoved(flagmo)) + return; flagmo->spawnpoint = mobj->spawnpoint; if (mobj->spawnpoint->options & MTF_OBJECTFLIP) { @@ -10456,12 +10560,15 @@ void P_PushableThinker(mobj_t *mobj) z = ss->sector->floorheight; spawnmo = P_SpawnMobj(x, y, z, mobj->type); - spawnmo->spawnpoint = mobj->spawnpoint; - P_UnsetThingPosition(spawnmo); - spawnmo->flags = mobj->flags; - P_SetThingPosition(spawnmo); - spawnmo->flags2 = mobj->flags2; - spawnmo->flags |= MF_PUSHABLE; + if (!P_MobjWasRemoved(spawnmo)) + { + spawnmo->spawnpoint = mobj->spawnpoint; + P_UnsetThingPosition(spawnmo); + spawnmo->flags = mobj->flags; + P_SetThingPosition(spawnmo); + spawnmo->flags2 = mobj->flags2; + spawnmo->flags |= MF_PUSHABLE; + } P_RemoveMobj(mobj); break; default: @@ -10637,29 +10744,28 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing) // // P_SpawnMobj // -mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) +mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...) { const mobjinfo_t *info = &mobjinfo[type]; SINT8 sc = -1; state_t *st; mobj_t *mobj; + int status; + va_list args; if (type == MT_NULL) - { -#if 0 -#ifdef PARANOIA - I_Error("Tried to spawn MT_NULL\n"); -#endif return NULL; -#endif - // Hack: Some code assumes that P_SpawnMobj can never return NULL - // So replace MT_NULL with MT_RAY in the meantime - // Remove when dealt properly - CONS_Debug(DBG_GAMELOGIC, "Tried to spawn MT_NULL, using MT_RAY\n"); - type = MT_RAY; - } - mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); + if (mobjcache != NULL) + { + mobj = mobjcache; + mobjcache = mobjcache->hnext; + memset(mobj, 0, sizeof(*mobj)); + } + else + { + mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); + } // this is officially a mobj, declared as soon as possible. mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; @@ -10751,9 +10857,27 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // Set shadowscale here, before spawn hook so that Lua can change it mobj->shadowscale = P_DefaultMobjShadowScale(mobj); + if (!(mobj->flags & MF_NOTHINK)) + P_AddThinker(THINK_MOBJ, &mobj->thinker); + + + if (type == MT_PLAYER) + { + // when spawning MT_PLAYER, set mobj->player before calling MobjSpawn hook to prevent P_RemoveMobj from succeeding on player mobj. + va_start(args, type); + mobj->player = va_arg(args, player_t *); + va_end(args); + } + + // increment mobj reference, so we don't get a dangling reference in case MobjSpawn calls P_RemoveMobj + mobj->thinker.references++; + // DANGER! This can cause P_SpawnMobj to return NULL! // Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks! - if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjSpawn))) + status = LUA_HookMobj(mobj, MOBJ_HOOK(MobjSpawn)); + mobj->thinker.references--; + + if (status) { if (P_MobjWasRemoved(mobj)) return NULL; @@ -10775,6 +10899,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_BLACKEGGMAN: { mobj_t *spawn = P_SpawnMobj(mobj->x, mobj->z, mobj->z+mobj->height-16*FRACUNIT, MT_BLACKEGGMAN_HELPER); + if (P_MobjWasRemoved(spawn)) + break; + spawn->destscale = mobj->scale; P_SetScale(spawn, mobj->scale); P_SetTarget(&spawn->target, mobj); @@ -10790,6 +10917,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_EGGGUARD: { mobj_t *spawn = P_SpawnMobj(x, y, z, MT_EGGSHIELD); + if (P_MobjWasRemoved(spawn)) + break; + spawn->destscale = mobj->scale; P_SetScale(spawn, mobj->scale); P_SetTarget(&mobj->tracer, spawn); @@ -10805,6 +10935,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) for (i = 0; i < mobj->info->damage; i++) { ball = P_SpawnMobj(x, y, z, mobj->info->painchance); + if (P_MobjWasRemoved(ball)) + continue; + ball->destscale = mobj->scale; P_SetScale(ball, mobj->scale); P_SetTarget(&ball->target, mobj); @@ -10824,6 +10957,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) for (q = 0; q < mobj->info->painchance; q++) { ball = P_SpawnMobj(x, y, z, mobj->info->mass); + if (P_MobjWasRemoved(ball)) + continue; + ball->destscale = mobj->scale; P_SetScale(ball, mobj->scale); P_SetTarget(&lastball->tracer, ball); @@ -10835,18 +10971,24 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_CRUSHSTACEAN: { mobj_t *bigmeatyclaw = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_CRUSHCLAW); - bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);; - P_SetTarget(&mobj->tracer, bigmeatyclaw); - P_SetTarget(&bigmeatyclaw->tracer, mobj); + if (!P_MobjWasRemoved(bigmeatyclaw)) + { + bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270); + P_SetTarget(&mobj->tracer, bigmeatyclaw); + P_SetTarget(&bigmeatyclaw->tracer, mobj); + } mobj->reactiontime >>= 1; } break; case MT_BANPYURA: { mobj_t *bigmeatyclaw = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_BANPSPRING); - bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);; - P_SetTarget(&mobj->tracer, bigmeatyclaw); - P_SetTarget(&bigmeatyclaw->tracer, mobj); + if (!P_MobjWasRemoved(bigmeatyclaw)) + { + bigmeatyclaw->angle = mobj->angle + ((mobj->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270); + P_SetTarget(&mobj->tracer, bigmeatyclaw); + P_SetTarget(&bigmeatyclaw->tracer, mobj); + } mobj->reactiontime >>= 1; } break; @@ -10861,6 +11003,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) for (i = 0; i <= 16; i++) // probably should be < but staying authentic to the Lua version { cur = P_SpawnMobjFromMobj(mobj, 0, 0, 0, ((mobj->type == MT_WAVINGFLAG1) ? MT_WAVINGFLAGSEG1 : MT_WAVINGFLAGSEG2));; + if (P_MobjWasRemoved(cur)) + continue; + P_SetTarget(&prev->tracer, cur); cur->extravalue1 = i; prev = cur; @@ -10898,11 +11043,17 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { mobj_t *fire; fire = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SPINBOBERT_FIRE1); - P_SetTarget(&fire->target, mobj); - P_SetTarget(&mobj->hnext, fire); + if (!P_MobjWasRemoved(fire)) + { + P_SetTarget(&fire->target, mobj); + P_SetTarget(&mobj->hnext, fire); + } fire = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SPINBOBERT_FIRE2); - P_SetTarget(&fire->target, mobj); - P_SetTarget(&mobj->hprev, fire); + if (!P_MobjWasRemoved(fire)) + { + P_SetTarget(&fire->target, mobj); + P_SetTarget(&mobj->hprev, fire); + } } break; case MT_REDRING: // Make MT_REDRING red by default @@ -10919,8 +11070,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_EGGCAPSULE: mobj->reactiontime = 0; mobj->extravalue1 = mobj->cvmem =\ - mobj->cusval = mobj->movecount =\ - mobj->lastlook = mobj->extravalue2 = -1; + mobj->cusval = mobj->movecount =\ + mobj->lastlook = mobj->extravalue2 = -1; break; case MT_REDTEAMRING: mobj->color = skincolor_redteam; @@ -10935,10 +11086,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) nummaprings++; break; case MT_METALSONIC_RACE: - mobj->skin = &skins[5]; + mobj->skin = skins[5]; /* FALLTHRU */ case MT_METALSONIC_BATTLE: - mobj->color = skins[5].prefcolor; + mobj->color = skins[5]->prefcolor; sc = 5; break; case MT_FANG: @@ -10956,6 +11107,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_OILLAMP: { mobj_t* overlay = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_OVERLAY); + if (P_MobjWasRemoved(overlay)) + break; P_SetTarget(&overlay->target, mobj); P_SetMobjState(overlay, S_OILLAMPFLARE); break; @@ -10965,13 +11118,19 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->flags2 |= MF2_INVERTAIMABLE; break; case MT_MINECARTEND: - P_SetTarget(&mobj->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_MINECARTENDSOLID)); - mobj->tracer->angle = mobj->angle + ANGLE_90; + { + mobj_t *mcsolid = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_MINECARTENDSOLID); + if (P_MobjWasRemoved(mcsolid)) + break; + P_SetTarget(&mobj->tracer, mcsolid); + mcsolid->angle = mobj->angle + ANGLE_90; + } break; case MT_TORCHFLOWER: { mobj_t *fire = P_SpawnMobjFromMobj(mobj, 0, 0, 46*FRACUNIT, MT_FLAME); - P_SetTarget(&mobj->target, fire); + if (!P_MobjWasRemoved(fire)) + P_SetTarget(&mobj->target, fire); break; } case MT_PYREFLY: @@ -10980,10 +11139,16 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->fuse = 100; break; case MT_SIGN: - P_SetTarget(&mobj->tracer, P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY)); - P_SetTarget(&mobj->tracer->target, mobj); - P_SetMobjState(mobj->tracer, S_SIGNBOARD); - mobj->tracer->movedir = ANGLE_90; + { + mobj_t *sign = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY); + if (P_MobjWasRemoved(sign)) + break; + + P_SetTarget(&mobj->tracer, sign); + P_SetTarget(&sign->target, mobj); + P_SetMobjState(sign, S_SIGNBOARD); + sign->movedir = ANGLE_90; + } default: break; } @@ -11006,9 +11171,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) } } - if (!(mobj->flags & MF_NOTHINK)) - P_AddThinker(THINK_MOBJ, &mobj->thinker); - if (mobj->skin) // correct inadequecies above. { mobj->sprite2 = P_GetSkinSprite2(mobj->skin, (mobj->frame & FF_FRAMEMASK), NULL); @@ -11218,7 +11380,9 @@ void P_RemoveMobj(mobj_t *mobj) INT32 prevreferences; if (!mobj->thinker.references) { - Z_Free(mobj); // No refrrences? Can be removed immediately! :D + // no references, dump it directly in the mobj cache + mobj->hnext = mobjcache; + mobjcache = mobj; return; } @@ -11617,8 +11781,10 @@ void P_SpawnPlayer(INT32 playernum) if ((netgame || multiplayer) && ((gametyperules & GTR_SPAWNINVUL) || leveltime) && !p->spectator && !(maptol & TOL_NIGHTS)) p->powers[pw_flashing] = flashingtics-1; // Babysitting deterrent - mobj = P_SpawnMobj(0, 0, 0, MT_PLAYER); - (mobj->player = p)->mo = mobj; + // MT_PLAYER cannot be removed, so this shouldn't be able to return NULL. + mobj = P_SpawnMobj(0, 0, 0, MT_PLAYER, p); + I_Assert(mobj != NULL); + p->mo = mobj; mobj->angle = 0; @@ -11628,7 +11794,7 @@ void P_SpawnPlayer(INT32 playernum) // set 'spritedef' override in mobj for player skins.. (see ProjectSprite) // (usefulness: when body mobj is detached from player (who respawns), // the dead body mobj retains the skin through the 'spritedef' override). - mobj->skin = &skins[p->skin]; + mobj->skin = skins[p->skin]; P_SetupStateAnimation(mobj, mobj->state); mobj->health = 1; @@ -11636,14 +11802,14 @@ void P_SpawnPlayer(INT32 playernum) p->bonustime = false; p->realtime = leveltime; - p->followitem = skins[p->skin].followitem; + p->followitem = skins[p->skin]->followitem; // Make sure player's stats are reset if they were in dashmode! if (p->dashmode) { p->dashmode = 0; - p->normalspeed = skins[p->skin].normalspeed; - p->jumpfactor = skins[p->skin].jumpfactor; + p->normalspeed = skins[p->skin]->normalspeed; + p->jumpfactor = skins[p->skin]->jumpfactor; } // Clear lastlinehit and lastsidehit @@ -11659,7 +11825,7 @@ void P_SpawnPlayer(INT32 playernum) P_FlashPal(p, 0, 0); // Resets // Set bounds accurately. - mobj->radius = FixedMul(skins[p->skin].radius, mobj->scale); + mobj->radius = FixedMul(skins[p->skin]->radius, mobj->scale); mobj->height = P_GetPlayerHeight(p); if (!leveltime && !p->spectator && ((maptol & TOL_NIGHTS) == TOL_NIGHTS) != (G_IsSpecialStage(gamemap))) // non-special NiGHTS stage or special non-NiGHTS stage @@ -11669,10 +11835,13 @@ void P_SpawnPlayer(INT32 playernum) if (p == players) // this is totally the wrong place to do this aaargh. { mobj_t *idya = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_GOTEMERALD); - idya->health = 0; // for identification - P_SetTarget(&idya->target, mobj); - P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate); - P_SetTarget(&mobj->tracer, idya); + if (!P_MobjWasRemoved(idya)) + { + idya->health = 0; // for identification + P_SetTarget(&idya->target, mobj); + P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate); + P_SetTarget(&mobj->tracer, idya); + } } } else if (sstimer) @@ -11771,9 +11940,9 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing) mobj->flags2 |= MF2_OBJECTFLIP; } if (mthing->args[0]) - P_SetPlayerMobjState(mobj, S_PLAY_FALL); + P_SetMobjState(mobj, S_PLAY_FALL); else if (metalrecording) - P_SetPlayerMobjState(mobj, S_PLAY_WAIT); + P_SetMobjState(mobj, S_PLAY_WAIT); } else z = floor; @@ -12394,8 +12563,10 @@ static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) mphase = (FixedAngle(mphase << FRACBITS) >> ANGLETOFINESHIFT); mroll = (FixedAngle(mroll << FRACBITS) >> ANGLETOFINESHIFT); -#define makemace(mobjtype, dist, moreflags2) {\ +#define makemace(mobjtype, dist, moreflags2) do {\ spawnee = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobjtype);\ + if (P_MobjWasRemoved(spawnee))\ + break;\ P_SetTarget(&spawnee->tracer, mobj);\ spawnee->threshold = mphase;\ spawnee->friction = mroll;\ @@ -12408,7 +12579,7 @@ static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) P_SetTarget(&hprev->hnext, spawnee);\ P_SetTarget(&spawnee->hprev, hprev);\ hprev = spawnee;\ -} +} while (0) mdosound = (mspeed && !(mthing->args[8] & TMM_SILENT)); mdocenter = (macetype && (mthing->args[8] & TMM_CENTERLINK)); @@ -12473,7 +12644,7 @@ static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) if (!mwidth) { - if (mdosound && mnumspokes <= mmin) // Can it make a sound? + if (!P_MobjWasRemoved(spawnee) && mdosound && mnumspokes <= mmin) // Can it make a sound? spawnee->flags2 |= MF2_BOSSNOTRAP; } else @@ -12486,7 +12657,7 @@ static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) while ((mwidthset -= widthfactor) > -mwidth) { makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH); - if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound? + if (!P_MobjWasRemoved(spawnee) && mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound? spawnee->flags2 |= MF2_BOSSNOTRAP; } } @@ -12495,7 +12666,7 @@ static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) while ((mwidthset += widthfactor) < -mwidth) { makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH); - if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound? + if (!P_MobjWasRemoved(spawnee) && mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound? spawnee->flags2 |= MF2_BOSSNOTRAP; } } @@ -12677,15 +12848,21 @@ static boolean P_SetupNiGHTSDrone(mapthing_t *mthing, mobj_t *mobj) mobj_t *droneman = P_SpawnMobjFromMobj(mobj, 0, 0, dronemanoffset, MT_NIGHTSDRONE_MAN); P_SetTarget(&mobj->target, goalpost); - P_SetTarget(&goalpost->target, sparkle); - P_SetTarget(&goalpost->tracer, droneman); + if (!P_MobjWasRemoved(goalpost)) + { + P_SetTarget(&goalpost->target, sparkle); + P_SetTarget(&goalpost->tracer, droneman); + } // correct Z position if (flip) { - P_MoveOrigin(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset); - P_MoveOrigin(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset); - P_MoveOrigin(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset); + if (!P_MobjWasRemoved(goalpost)) + P_MoveOrigin(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset); + if (!P_MobjWasRemoved(sparkle)) + P_MoveOrigin(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset); + if (!P_MobjWasRemoved(droneman)) + P_MoveOrigin(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset); } // Remember position preference for later @@ -12707,9 +12884,12 @@ static boolean P_SetupNiGHTSDrone(mapthing_t *mthing, mobj_t *mobj) } // Remember old Z position and flags for correction detection - goalpost->movefactor = mobj->z; - goalpost->friction = mobj->height; - goalpost->threshold = mobj->flags & (MF_SLIDEME|MF_GRENADEBOUNCE); + if (!P_MobjWasRemoved(goalpost)) + { + goalpost->movefactor = mobj->z; + goalpost->friction = mobj->height; + goalpost->threshold = mobj->flags & (MF_SLIDEME|MF_GRENADEBOUNCE); + } } return true; } @@ -12727,30 +12907,54 @@ static boolean P_SetupBooster(mapthing_t* mthing, mobj_t* mobj, boolean strong) statenum_t rollerstate = strong ? S_REDBOOSTERROLLER : S_YELLOWBOOSTERROLLER; mobj_t *seg = P_SpawnMobjFromMobj(mobj, 26*x1, 26*y1, 0, MT_BOOSTERSEG); - seg->angle = angle - ANGLE_90; - P_SetMobjState(seg, facestate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle - ANGLE_90; + P_SetMobjState(seg, facestate); + } seg = P_SpawnMobjFromMobj(mobj, -26*x1, -26*y1, 0, MT_BOOSTERSEG); - seg->angle = angle + ANGLE_90; - P_SetMobjState(seg, facestate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle + ANGLE_90; + P_SetMobjState(seg, facestate); + } seg = P_SpawnMobjFromMobj(mobj, 21*x2, 21*y2, 0, MT_BOOSTERSEG); - seg->angle = angle; - P_SetMobjState(seg, leftstate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle; + P_SetMobjState(seg, leftstate); + } seg = P_SpawnMobjFromMobj(mobj, -21*x2, -21*y2, 0, MT_BOOSTERSEG); - seg->angle = angle; - P_SetMobjState(seg, rightstate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle; + P_SetMobjState(seg, rightstate); + } seg = P_SpawnMobjFromMobj(mobj, 13*(x1 + x2), 13*(y1 + y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; - P_SetMobjState(seg, rollerstate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle; + P_SetMobjState(seg, rollerstate); + } seg = P_SpawnMobjFromMobj(mobj, 13*(x1 - x2), 13*(y1 - y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; - P_SetMobjState(seg, rollerstate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle; + P_SetMobjState(seg, rollerstate); + } seg = P_SpawnMobjFromMobj(mobj, -13*(x1 + x2), -13*(y1 + y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; - P_SetMobjState(seg, rollerstate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle; + P_SetMobjState(seg, rollerstate); + } seg = P_SpawnMobjFromMobj(mobj, -13*(x1 - x2), -13*(y1 - y2), 0, MT_BOOSTERROLLER); - seg->angle = angle; - P_SetMobjState(seg, rollerstate); + if (!P_MobjWasRemoved(seg)) + { + seg->angle = angle; + P_SetMobjState(seg, rollerstate); + } return true; } @@ -12758,6 +12962,9 @@ static boolean P_SetupBooster(mapthing_t* mthing, mobj_t* mobj, boolean strong) static mobj_t *P_MakeSoftwareCorona(mobj_t *mo, INT32 height) { mobj_t *corona = P_SpawnMobjFromMobj(mo, 0, 0, height<sprite = SPR_FLAM; corona->frame = (FF_FULLBRIGHT|FF_TRANS90|12); corona->tics = -1; @@ -12893,6 +13100,9 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean if (mthing->args[0]) { mobj_t *corona = P_MakeSoftwareCorona(mobj, 20); + if (P_MobjWasRemoved(corona)) + break; + P_SetScale(corona, (corona->destscale = mobj->scale*3)); P_SetTarget(&mobj->tracer, corona); } @@ -12901,11 +13111,17 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean if (!(mthing->args[0] & TMFH_NOFLAME)) // Spawn the fire { mobj_t *flame = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_FLAME); + if (P_MobjWasRemoved(flame)) + break; + P_SetTarget(&flame->target, mobj); flame->flags2 |= MF2_BOSSNOTRAP; if (mthing->args[0] & TMFH_CORONA) { mobj_t *corona = P_MakeSoftwareCorona(flame, 20); + if (P_MobjWasRemoved(corona)) + break; + P_SetScale(corona, (corona->destscale = flame->scale*3)); P_SetTarget(&flame->tracer, corona); } @@ -12922,6 +13138,8 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean if (!(mthing->args[0])) // take the torch out of the crafting recipe { mobj_t *overlay = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY); + if (P_MobjWasRemoved(overlay)) + break; P_SetTarget(&overlay->target, mobj); P_SetMobjState(overlay, mobj->info->raisestate); } @@ -13001,9 +13219,15 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean case MT_THZTREE: { // Spawn the branches angle_t mobjangle = FixedAngle((mthing->angle % 113) << FRACBITS); - P_SpawnMobjFromMobj(mobj, FRACUNIT, 0, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_22h; - P_SpawnMobjFromMobj(mobj, 0, FRACUNIT, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_157h; - P_SpawnMobjFromMobj(mobj, -FRACUNIT, 0, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_270; + mobj_t *branch = P_SpawnMobjFromMobj(mobj, FRACUNIT, 0, 0, MT_THZTREEBRANCH); + if (!P_MobjWasRemoved(branch)) + branch->angle = mobjangle + ANGLE_22h; + branch = P_SpawnMobjFromMobj(mobj, 0, FRACUNIT, 0, MT_THZTREEBRANCH); + if (!P_MobjWasRemoved(branch)) + branch->angle = mobjangle + ANGLE_157h; + branch = P_SpawnMobjFromMobj(mobj, -FRACUNIT, 0, 0, MT_THZTREEBRANCH); + if (!P_MobjWasRemoved(branch)) + branch->angle = mobjangle + ANGLE_270; } break; case MT_TUTORIALPLANT: @@ -13013,26 +13237,34 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean for (i = 0; i < 6; i++) { segment = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_TUTORIALLEAF); + if (P_MobjWasRemoved(segment)) + continue; segment->angle = mobj->angle + FixedAngle(i*60*FRACUNIT); P_SetMobjState(segment, S_TUTORIALLEAF1 + mthing->args[0]); } for (i = 0; i < 3; i++) { segment = P_SpawnMobjFromMobj(mobj, 0, 0, 112*FRACUNIT, MT_TUTORIALFLOWER); + if (P_MobjWasRemoved(segment)) + continue; segment->angle = mobj->angle + FixedAngle(i*120*FRACUNIT); P_SetMobjState(segment, S_TUTORIALFLOWER1 + mthing->args[0]); } - P_SetMobjState(P_SpawnMobjFromMobj(mobj, 0, 0, 112*FRACUNIT, MT_TUTORIALFLOWERF), S_TUTORIALFLOWERF1 + mthing->args[0]); + segment = P_SpawnMobjFromMobj(mobj, 0, 0, 112*FRACUNIT, MT_TUTORIALFLOWERF); + if (!P_MobjWasRemoved(segment)) + P_SetMobjState(segment, S_TUTORIALFLOWERF1 + mthing->args[0]); } break; case MT_CEZPOLE1: case MT_CEZPOLE2: { // Spawn the banner angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS); - P_SpawnMobjFromMobj(mobj, + mobj_t *banner = P_SpawnMobjFromMobj(mobj, P_ReturnThrustX(mobj, mobjangle, 4 << FRACBITS), P_ReturnThrustY(mobj, mobjangle, 4 << FRACBITS), - 0, ((mobj->type == MT_CEZPOLE1) ? MT_CEZBANNER1 : MT_CEZBANNER2))->angle = mobjangle + ANGLE_90; + 0, ((mobj->type == MT_CEZPOLE1) ? MT_CEZBANNER1 : MT_CEZBANNER2)); + if (!P_MobjWasRemoved(banner)) + banner->angle = mobjangle + ANGLE_90; } break; case MT_HHZTREE_TOP: @@ -13041,8 +13273,11 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean mobj_t* leaf; #define doleaf(x, y) \ leaf = P_SpawnMobjFromMobj(mobj, x, y, 0, MT_HHZTREE_PART);\ - leaf->angle = mobjangle;\ - P_SetMobjState(leaf, leaf->info->seestate);\ + if (!P_MobjWasRemoved(leaf))\ + {\ + leaf->angle = mobjangle;\ + P_SetMobjState(leaf, leaf->info->seestate);\ + }\ mobjangle += ANGLE_90 doleaf(FRACUNIT, 0); doleaf(0, FRACUNIT); @@ -13081,8 +13316,9 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean angle_t fa = (angle >> ANGLETOFINESHIFT) & FINEMASK; fixed_t xoffs = FINECOSINE(fa); fixed_t yoffs = FINESINE(fa); - mobj_t* leaf = P_SpawnMobjFromMobj(mobj, xoffs, yoffs, 0, MT_BIGFERNLEAF); - leaf->angle = angle; + mobj_t *leaf = P_SpawnMobjFromMobj(mobj, xoffs, yoffs, 0, MT_BIGFERNLEAF); + if (!P_MobjWasRemoved(leaf)) + leaf->angle = angle; angle += ANGLE_45; } break; @@ -13119,6 +13355,8 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean { mobj_t* elecmobj; elecmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_CYBRAKDEMON_ELECTRIC_BARRIER); + if (P_MobjWasRemoved(elecmobj)) + break; P_SetTarget(&elecmobj->target, mobj); elecmobj->angle = FixedAngle(mthing->angle << FRACBITS); elecmobj->destscale = mobj->scale*2; @@ -13173,6 +13411,12 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean mobj->x - P_ReturnThrustX(mobj, mobjangle, baseradius), mobj->y - P_ReturnThrustY(mobj, mobjangle, baseradius), mobj->z, MT_WALLSPIKEBASE); + if (P_MobjWasRemoved(base)) + { + // if we can't spawn the base, don't spawn the spike at all. + P_RemoveMobj(mobj); + return false; + } base->angle = mobjangle + ANGLE_90; base->destscale = mobj->destscale; P_SetScale(base, mobj->scale); @@ -13348,6 +13592,8 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, boolean doangle = true; mobj = P_SpawnMobj(x, y, z, i); + if (mobj == NULL) + return NULL; mobj->spawnpoint = mthing; P_SetScale(mobj, FixedMul(mobj->scale, mthing->scale)); @@ -13445,6 +13691,9 @@ void P_SpawnHoop(mapthing_t *mthing) fixed_t z = P_GetMobjSpawnHeight(MT_HOOP, x, y, mthing->z << FRACBITS, 0, false, mthing->scale, mthing->options & MTF_ABSOLUTEZ); hoopcenter = P_SpawnMobj(x, y, z, MT_HOOPCENTER); + if (P_MobjWasRemoved(hoopcenter)) + return; + hoopcenter->spawnpoint = mthing; hoopcenter->z -= hoopcenter->height/2; @@ -13475,6 +13724,8 @@ void P_SpawnHoop(mapthing_t *mthing) FV4_Copy(&v, FM_MultMatrixVec4(&yawmatrix, &v, &res)); mobj = P_SpawnMobj(x + v.x, y + v.y, z + v.z, MT_HOOP); + if (P_MobjWasRemoved(mobj)) + continue; mobj->z -= mobj->height/2; if (maptol & TOL_XMAS) @@ -13519,6 +13770,8 @@ void P_SpawnHoop(mapthing_t *mthing) FV4_Copy(&v, FM_MultMatrixVec4(&yawmatrix, &v, &res)); mobj = P_SpawnMobj(x + v.x, y + v.y, z + v.z, MT_HOOPCOLLIDE); + if (P_MobjWasRemoved(mobj)) + continue; mobj->z -= mobj->height/2; // Link all the collision sprites together. @@ -13791,6 +14044,8 @@ mobj_t *P_SpawnXYZMissile(mobj_t *source, mobj_t *dest, mobjtype_t type, z -= FixedMul(mobjinfo[type].height, source->scale); th = P_SpawnMobj(x, y, z, type); + if (P_MobjWasRemoved(th)) + return NULL; if (source->eflags & MFE_VERTICALFLIP) th->flags2 |= MF2_OBJECTFLIP; @@ -13853,6 +14108,8 @@ mobj_t *P_SpawnAlteredDirectionMissile(mobj_t *source, mobjtype_t type, fixed_t z -= FixedMul(mobjinfo[type].height, source->scale); th = P_SpawnMobj(x, y, z, type); + if (P_MobjWasRemoved(th)) + return NULL; if (source->eflags & MFE_VERTICALFLIP) th->flags2 |= MF2_OBJECTFLIP; @@ -13918,6 +14175,8 @@ mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za, z -= FixedMul(mobjinfo[type].height, source->scale); th = P_SpawnMobj(x, y, z, type); + if (P_MobjWasRemoved(th)) + return NULL; if (source->eflags & MFE_VERTICALFLIP) th->flags2 |= MF2_OBJECTFLIP; @@ -13988,6 +14247,8 @@ mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type) z -= FixedMul(mobjinfo[type].height, source->scale); th = P_SpawnMobj(source->x, source->y, z, type); + if (P_MobjWasRemoved(th)) + return NULL; if (source->eflags & MFE_VERTICALFLIP) th->flags2 |= MF2_OBJECTFLIP; @@ -14089,6 +14350,8 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai z = source->z + source->height/3; th = P_SpawnMobj(x, y, z, type); + if (P_MobjWasRemoved(th)) + return NULL; if (source->eflags & MFE_VERTICALFLIP) th->flags2 |= MF2_OBJECTFLIP; diff --git a/src/p_polyobj.c b/src/p_polyobj.c index b207bb740..331bc5c7f 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -1243,6 +1243,8 @@ boolean Polyobj_rotate(polyobj_t *po, angle_t delta, boolean turnplayers, boolea // Returns NULL if no such polyobject exists. polyobj_t *Polyobj_GetForNum(INT32 id) { + if (numPolyObjects == 0) + return NULL; INT32 curidx = PolyObjects[id % numPolyObjects].first; while (curidx != numPolyObjects && PolyObjects[curidx].id != id) diff --git a/src/p_saveg.c b/src/p_saveg.c index 94ce35f5a..41d7e3c80 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -41,20 +41,19 @@ UINT8 *save_p; // Block UINT32s to attempt to ensure that the correct data is // being sent and received -#define ARCHIVEBLOCK_MISC 0x7FEEDEED -#define ARCHIVEBLOCK_PLAYERS 0x7F448008 -#define ARCHIVEBLOCK_WORLD 0x7F8C08C0 -#define ARCHIVEBLOCK_POBJS 0x7F928546 -#define ARCHIVEBLOCK_THINKERS 0x7F37037C -#define ARCHIVEBLOCK_SPECIALS 0x7F228378 -#define ARCHIVEBLOCK_EMBLEMS 0x7F4A5445 +#define ARCHIVEBLOCK_MISC 0x7FEEDEED +#define ARCHIVEBLOCK_PLAYERS 0x7F448008 +#define ARCHIVEBLOCK_WORLD 0x7F8C08C0 +#define ARCHIVEBLOCK_POBJS 0x7F928546 +#define ARCHIVEBLOCK_THINKERS 0x7F37037C +#define ARCHIVEBLOCK_SPECIALS 0x7F228378 +#define ARCHIVEBLOCK_EMBLEMS 0x7F4A5445 +#define ARCHIVEBLOCK_SECPORTALS 0x7FBE34C9 // Note: This cannot be bigger // than an UINT16 typedef enum { -// RFLAGPOINT = 0x01, -// BFLAGPOINT = 0x02, CAPSULE = 0x04, AWAYVIEW = 0x08, FIRSTAXIS = 0x10, @@ -78,11 +77,11 @@ static inline void P_ArchivePlayer(void) // Write skin names, so that loading skins in different orders // doesn't change who the save file is for! - WRITESTRINGN(save_p, skins[player->skin].name, SKINNAMESIZE); + WRITESTRINGN(save_p, skins[player->skin]->name, SKINNAMESIZE); if (botskin != 0) { - WRITESTRINGN(save_p, skins[botskin-1].name, SKINNAMESIZE); + WRITESTRINGN(save_p, skins[botskin-1]->name, SKINNAMESIZE); } else { @@ -853,38 +852,71 @@ static void P_NetUnArchiveWaypoints(void) #define SD_DIFF3 0x80 // diff3 flags -#define SD_TAGLIST 0x01 -#define SD_COLORMAP 0x02 +#define SD_TAGLIST 0x01 +#define SD_COLORMAP 0x02 #define SD_CRUMBLESTATE 0x04 -#define SD_FLOORLIGHT 0x08 -#define SD_CEILLIGHT 0x10 -#define SD_FLAG 0x20 -#define SD_SPECIALFLAG 0x40 -#define SD_DIFF4 0x80 +#define SD_FLOORLIGHT 0x08 +#define SD_CEILLIGHT 0x10 +#define SD_FLAG 0x20 +#define SD_SPECIALFLAG 0x40 +#define SD_DIFF4 0x80 -//diff4 flags +// diff4 flags #define SD_DAMAGETYPE 0x01 #define SD_TRIGGERTAG 0x02 #define SD_TRIGGERER 0x04 -#define SD_GRAVITY 0x08 +#define SD_FXSCALE 0x08 +#define SD_FYSCALE 0x10 +#define SD_CXSCALE 0x20 +#define SD_CYSCALE 0x40 +#define SD_DIFF5 0x80 -#define LD_FLAG 0x01 -#define LD_SPECIAL 0x02 -#define LD_CLLCOUNT 0x04 -#define LD_S1TEXOFF 0x08 -#define LD_S1TOPTEX 0x10 -#define LD_S1BOTTEX 0x20 -#define LD_S1MIDTEX 0x40 -#define LD_DIFF2 0x80 +// diff5 flags +#define SD_GRAVITY 0x01 +#define SD_FLOORPORTAL 0x02 +#define SD_CEILPORTAL 0x04 + +// diff1 flags +#define LD_FLAG 0x01 +#define LD_SPECIAL 0x02 +#define LD_CLLCOUNT 0x04 +#define LD_ARGS 0x08 +#define LD_STRINGARGS 0x10 +#define LD_SIDE1 0x20 +#define LD_SIDE2 0x40 +#define LD_DIFF2 0x80 // diff2 flags -#define LD_S2TEXOFF 0x01 -#define LD_S2TOPTEX 0x02 -#define LD_S2BOTTEX 0x04 -#define LD_S2MIDTEX 0x08 -#define LD_ARGS 0x10 -#define LD_STRINGARGS 0x20 -#define LD_EXECUTORDELAY 0x40 +#define LD_EXECUTORDELAY 0x01 +#define LD_TRANSFPORTAL 0x02 + +// sidedef flags +enum +{ + LD_SDTEXOFFX = 1, + LD_SDTEXOFFY = 1<<1, + LD_SDTOPTEX = 1<<2, + LD_SDBOTTEX = 1<<3, + LD_SDMIDTEX = 1<<4, + LD_SDTOPOFFX = 1<<5, + LD_SDTOPOFFY = 1<<6, + LD_SDMIDOFFX = 1<<7, + LD_SDMIDOFFY = 1<<8, + LD_SDBOTOFFX = 1<<9, + LD_SDBOTOFFY = 1<<10, + LD_SDTOPSCALEX = 1<<11, + LD_SDTOPSCALEY = 1<<12, + LD_SDMIDSCALEX = 1<<13, + LD_SDMIDSCALEY = 1<<14, + LD_SDBOTSCALEX = 1<<15, + LD_SDBOTSCALEY = 1<<16, + LD_SDLIGHT = 1<<17, + LD_SDTOPLIGHT = 1<<18, + LD_SDMIDLIGHT = 1<<19, + LD_SDBOTLIGHT = 1<<20, + LD_SDREPEATCNT = 1<<21, + LD_SDFLAGS = 1<<22 +}; static boolean P_AreArgsEqual(const line_t *li, const line_t *spawnli) { @@ -1005,11 +1037,11 @@ static void ArchiveSectors(void) size_t i, j; const sector_t *ss = sectors; const sector_t *spawnss = spawnsectors; - UINT8 diff, diff2, diff3, diff4; + UINT8 diff, diff2, diff3, diff4, diff5; for (i = 0; i < numsectors; i++, ss++, spawnss++) { - diff = diff2 = diff3 = diff4 = 0; + diff = diff2 = diff3 = diff4 = diff5 = 0; if (ss->floorheight != spawnss->floorheight) diff |= SD_FLOORHT; if (ss->ceilingheight != spawnss->ceilingheight) @@ -1035,6 +1067,14 @@ static void ArchiveSectors(void) diff2 |= SD_CXOFFS; if (ss->ceilingyoffset != spawnss->ceilingyoffset) diff2 |= SD_CYOFFS; + if (ss->floorxscale != spawnss->floorxscale) + diff2 |= SD_FXSCALE; + if (ss->flooryscale != spawnss->flooryscale) + diff2 |= SD_FYSCALE; + if (ss->ceilingxscale != spawnss->ceilingxscale) + diff2 |= SD_CXSCALE; + if (ss->ceilingyscale != spawnss->ceilingyscale) + diff2 |= SD_CYSCALE; if (ss->floorangle != spawnss->floorangle) diff2 |= SD_FLOORANG; if (ss->ceilingangle != spawnss->ceilingangle) @@ -1063,11 +1103,18 @@ static void ArchiveSectors(void) if (ss->triggerer != spawnss->triggerer) diff4 |= SD_TRIGGERER; if (ss->gravity != spawnss->gravity) - diff4 |= SD_GRAVITY; + diff5 |= SD_GRAVITY; + if (ss->portal_floor != spawnss->portal_floor) + diff5 |= SD_FLOORPORTAL; + if (ss->portal_ceiling != spawnss->portal_ceiling) + diff5 |= SD_CEILPORTAL; if (ss->ffloors && CheckFFloorDiff(ss)) diff |= SD_FFLOORS; + if (diff5) + diff4 |= SD_DIFF5; + if (diff4) diff3 |= SD_DIFF4; @@ -1087,6 +1134,8 @@ static void ArchiveSectors(void) WRITEUINT8(save_p, diff3); if (diff3 & SD_DIFF4) WRITEUINT8(save_p, diff4); + if (diff4 & SD_DIFF5) + WRITEUINT8(save_p, diff5); if (diff & SD_FLOORHT) WRITEFIXED(save_p, ss->floorheight); if (diff & SD_CEILHT) @@ -1143,8 +1192,20 @@ static void ArchiveSectors(void) WRITEINT16(save_p, ss->triggertag); if (diff4 & SD_TRIGGERER) WRITEUINT8(save_p, ss->triggerer); - if (diff4 & SD_GRAVITY) + if (diff4 & SD_FXSCALE) + WRITEFIXED(save_p, ss->floorxscale); + if (diff4 & SD_FYSCALE) + WRITEFIXED(save_p, ss->flooryscale); + if (diff4 & SD_CXSCALE) + WRITEFIXED(save_p, ss->ceilingxscale); + if (diff4 & SD_CYSCALE) + WRITEFIXED(save_p, ss->ceilingyscale); + if (diff5 & SD_GRAVITY) WRITEFIXED(save_p, ss->gravity); + if (diff5 & SD_FLOORPORTAL) + WRITEUINT32(save_p, ss->portal_floor); + if (diff5 & SD_CEILPORTAL) + WRITEUINT32(save_p, ss->portal_ceiling); if (diff & SD_FFLOORS) ArchiveFFloors(ss); } @@ -1157,7 +1218,7 @@ static void UnArchiveSectors(void) { UINT32 i; UINT16 j; - UINT8 diff, diff2, diff3, diff4; + UINT8 diff, diff2, diff3, diff4, diff5; for (;;) { i = READUINT32(save_p); @@ -1181,6 +1242,10 @@ static void UnArchiveSectors(void) diff4 = READUINT8(save_p); else diff4 = 0; + if (diff4 & SD_DIFF5) + diff5 = READUINT8(save_p); + else + diff5 = 0; if (diff & SD_FLOORHT) sectors[i].floorheight = READFIXED(save_p); @@ -1264,26 +1329,124 @@ static void UnArchiveSectors(void) sectors[i].triggertag = READINT16(save_p); if (diff4 & SD_TRIGGERER) sectors[i].triggerer = READUINT8(save_p); - if (diff4 & SD_GRAVITY) + if (diff4 & SD_FXSCALE) + sectors[i].floorxscale = READFIXED(save_p); + if (diff4 & SD_FYSCALE) + sectors[i].flooryscale = READFIXED(save_p); + if (diff4 & SD_CXSCALE) + sectors[i].ceilingxscale = READFIXED(save_p); + if (diff4 & SD_CYSCALE) + sectors[i].ceilingyscale = READFIXED(save_p); + if (diff5 & SD_GRAVITY) sectors[i].gravity = READFIXED(save_p); + if (diff5 & SD_FLOORPORTAL) + sectors[i].portal_floor = READUINT32(save_p); + if (diff5 & SD_CEILPORTAL) + sectors[i].portal_ceiling = READUINT32(save_p); if (diff & SD_FFLOORS) UnArchiveFFloors(§ors[i]); } } +static UINT32 GetSideDiff(const side_t *si, const side_t *spawnsi) +{ + UINT32 diff = 0; + if (si->textureoffset != spawnsi->textureoffset) + diff |= LD_SDTEXOFFX; + if (si->rowoffset != spawnsi->rowoffset) + diff |= LD_SDTEXOFFY; + //SoM: 4/1/2000: Some textures are colormaps. Don't worry about invalid textures. + if (si->toptexture != spawnsi->toptexture) + diff |= LD_SDTOPTEX; + if (si->bottomtexture != spawnsi->bottomtexture) + diff |= LD_SDBOTTEX; + if (si->midtexture != spawnsi->midtexture) + diff |= LD_SDMIDTEX; + if (si->offsetx_top != spawnsi->offsetx_top) + diff |= LD_SDTOPOFFX; + if (si->offsetx_mid != spawnsi->offsetx_mid) + diff |= LD_SDMIDOFFX; + if (si->offsetx_bottom != spawnsi->offsetx_bottom) + diff |= LD_SDBOTOFFX; + if (si->offsety_top != spawnsi->offsety_top) + diff |= LD_SDTOPOFFY; + if (si->offsety_mid != spawnsi->offsety_mid) + diff |= LD_SDMIDOFFY; + if (si->offsety_bottom != spawnsi->offsety_bottom) + diff |= LD_SDBOTOFFY; + if (si->scalex_top != spawnsi->scalex_top) + diff |= LD_SDTOPSCALEX; + if (si->scalex_mid != spawnsi->scalex_mid) + diff |= LD_SDMIDSCALEX; + if (si->scalex_bottom != spawnsi->scalex_bottom) + diff |= LD_SDBOTSCALEX; + if (si->scaley_top != spawnsi->scaley_top) + diff |= LD_SDTOPSCALEY; + if (si->scaley_mid != spawnsi->scaley_mid) + diff |= LD_SDMIDSCALEY; + if (si->scaley_bottom != spawnsi->scaley_bottom) + diff |= LD_SDBOTSCALEY; + if (si->repeatcnt != spawnsi->repeatcnt) + diff |= LD_SDREPEATCNT; + return diff; +} + +static void ArchiveSide(const side_t *si, UINT32 diff) +{ + WRITEUINT32(save_p, diff); + + if (diff & LD_SDTEXOFFX) + WRITEFIXED(save_p, si->textureoffset); + if (diff & LD_SDTEXOFFY) + WRITEFIXED(save_p, si->rowoffset); + if (diff & LD_SDTOPTEX) + WRITEINT32(save_p, si->toptexture); + if (diff & LD_SDBOTTEX) + WRITEINT32(save_p, si->bottomtexture); + if (diff & LD_SDMIDTEX) + WRITEINT32(save_p, si->midtexture); + if (diff & LD_SDTOPOFFX) + WRITEFIXED(save_p, si->offsetx_top); + if (diff & LD_SDMIDOFFX) + WRITEFIXED(save_p, si->offsetx_mid); + if (diff & LD_SDBOTOFFX) + WRITEFIXED(save_p, si->offsetx_bottom); + if (diff & LD_SDTOPOFFY) + WRITEFIXED(save_p, si->offsety_top); + if (diff & LD_SDMIDOFFY) + WRITEFIXED(save_p, si->offsety_mid); + if (diff & LD_SDBOTOFFY) + WRITEFIXED(save_p, si->offsety_bottom); + if (diff & LD_SDTOPSCALEX) + WRITEFIXED(save_p, si->scalex_top); + if (diff & LD_SDMIDSCALEX) + WRITEFIXED(save_p, si->scalex_mid); + if (diff & LD_SDBOTSCALEX) + WRITEFIXED(save_p, si->scalex_bottom); + if (diff & LD_SDTOPSCALEY) + WRITEFIXED(save_p, si->scaley_top); + if (diff & LD_SDMIDSCALEY) + WRITEFIXED(save_p, si->scaley_mid); + if (diff & LD_SDBOTSCALEY) + WRITEFIXED(save_p, si->scaley_bottom); + if (diff & LD_SDREPEATCNT) + WRITEINT16(save_p, si->repeatcnt); +} + static void ArchiveLines(void) { size_t i; const line_t *li = lines; const line_t *spawnli = spawnlines; - const side_t *si; - const side_t *spawnsi; - UINT8 diff, diff2; // no diff3 + UINT8 diff, diff2; + UINT32 side1diff; + UINT32 side2diff; for (i = 0; i < numlines; i++, spawnli++, li++) { diff = diff2 = 0; + side1diff = side2diff = 0; if (li->special != spawnli->special) diff |= LD_SPECIAL; @@ -1292,40 +1455,28 @@ static void ArchiveLines(void) diff |= LD_CLLCOUNT; if (!P_AreArgsEqual(li, spawnli)) - diff2 |= LD_ARGS; + diff |= LD_ARGS; if (!P_AreStringArgsEqual(li, spawnli)) - diff2 |= LD_STRINGARGS; + diff |= LD_STRINGARGS; if (li->executordelay != spawnli->executordelay) diff2 |= LD_EXECUTORDELAY; + if (li->secportal != spawnli->secportal) + diff2 |= LD_TRANSFPORTAL; + if (li->sidenum[0] != NO_SIDEDEF) { - si = &sides[li->sidenum[0]]; - spawnsi = &spawnsides[li->sidenum[0]]; - if (si->textureoffset != spawnsi->textureoffset) - diff |= LD_S1TEXOFF; - //SoM: 4/1/2000: Some textures are colormaps. Don't worry about invalid textures. - if (si->toptexture != spawnsi->toptexture) - diff |= LD_S1TOPTEX; - if (si->bottomtexture != spawnsi->bottomtexture) - diff |= LD_S1BOTTEX; - if (si->midtexture != spawnsi->midtexture) - diff |= LD_S1MIDTEX; + side1diff = GetSideDiff(&sides[li->sidenum[0]], &spawnsides[li->sidenum[0]]); + if (side1diff) + diff |= LD_SIDE1; } if (li->sidenum[1] != NO_SIDEDEF) { - si = &sides[li->sidenum[1]]; - spawnsi = &spawnsides[li->sidenum[1]]; - if (si->textureoffset != spawnsi->textureoffset) - diff2 |= LD_S2TEXOFF; - if (si->toptexture != spawnsi->toptexture) - diff2 |= LD_S2TOPTEX; - if (si->bottomtexture != spawnsi->bottomtexture) - diff2 |= LD_S2BOTTEX; - if (si->midtexture != spawnsi->midtexture) - diff2 |= LD_S2MIDTEX; + side2diff = GetSideDiff(&sides[li->sidenum[1]], &spawnsides[li->sidenum[1]]); + if (side2diff) + diff |= LD_SIDE2; } if (diff2) @@ -1343,33 +1494,13 @@ static void ArchiveLines(void) WRITEINT16(save_p, li->special); if (diff & LD_CLLCOUNT) WRITEINT16(save_p, li->callcount); - - si = &sides[li->sidenum[0]]; - if (diff & LD_S1TEXOFF) - WRITEFIXED(save_p, si->textureoffset); - if (diff & LD_S1TOPTEX) - WRITEINT32(save_p, si->toptexture); - if (diff & LD_S1BOTTEX) - WRITEINT32(save_p, si->bottomtexture); - if (diff & LD_S1MIDTEX) - WRITEINT32(save_p, si->midtexture); - - si = &sides[li->sidenum[1]]; - if (diff2 & LD_S2TEXOFF) - WRITEFIXED(save_p, si->textureoffset); - if (diff2 & LD_S2TOPTEX) - WRITEINT32(save_p, si->toptexture); - if (diff2 & LD_S2BOTTEX) - WRITEINT32(save_p, si->bottomtexture); - if (diff2 & LD_S2MIDTEX) - WRITEINT32(save_p, si->midtexture); - if (diff2 & LD_ARGS) + if (diff & LD_ARGS) { UINT8 j; for (j = 0; j < NUMLINEARGS; j++) WRITEINT32(save_p, li->args[j]); } - if (diff2 & LD_STRINGARGS) + if (diff & LD_STRINGARGS) { UINT8 j; for (j = 0; j < NUMLINESTRINGARGS; j++) @@ -1388,19 +1519,66 @@ static void ArchiveLines(void) WRITECHAR(save_p, li->stringargs[j][k]); } } + if (diff & LD_SIDE1) + ArchiveSide(&sides[li->sidenum[0]], side1diff); + if (diff & LD_SIDE2) + ArchiveSide(&sides[li->sidenum[1]], side2diff); if (diff2 & LD_EXECUTORDELAY) WRITEINT32(save_p, li->executordelay); + if (diff2 & LD_TRANSFPORTAL) + WRITEUINT32(save_p, li->secportal); } } WRITEUINT32(save_p, 0xffffffff); } +static void UnArchiveSide(side_t *si) +{ + UINT32 diff = READUINT32(save_p); + + if (diff & LD_SDTEXOFFX) + si->textureoffset = READFIXED(save_p); + if (diff & LD_SDTEXOFFY) + si->rowoffset = READFIXED(save_p); + if (diff & LD_SDTOPTEX) + si->toptexture = READINT32(save_p); + if (diff & LD_SDBOTTEX) + si->bottomtexture = READINT32(save_p); + if (diff & LD_SDMIDTEX) + si->midtexture = READINT32(save_p); + if (diff & LD_SDTOPOFFX) + si->offsetx_top = READFIXED(save_p); + if (diff & LD_SDMIDOFFX) + si->offsetx_mid = READFIXED(save_p); + if (diff & LD_SDBOTOFFX) + si->offsetx_bottom = READFIXED(save_p); + if (diff & LD_SDTOPOFFY) + si->offsety_top = READFIXED(save_p); + if (diff & LD_SDMIDOFFY) + si->offsety_mid = READFIXED(save_p); + if (diff & LD_SDBOTOFFY) + si->offsety_bottom = READFIXED(save_p); + if (diff & LD_SDTOPSCALEX) + si->scalex_top = READFIXED(save_p); + if (diff & LD_SDMIDSCALEX) + si->scalex_mid = READFIXED(save_p); + if (diff & LD_SDBOTSCALEX) + si->scalex_bottom = READFIXED(save_p); + if (diff & LD_SDTOPSCALEY) + si->scaley_top = READFIXED(save_p); + if (diff & LD_SDMIDSCALEY) + si->scaley_mid = READFIXED(save_p); + if (diff & LD_SDBOTSCALEY) + si->scaley_bottom = READFIXED(save_p); + if (diff & LD_SDREPEATCNT) + si->repeatcnt = READINT16(save_p); +} + static void UnArchiveLines(void) { UINT32 i; line_t *li; - side_t *si; - UINT8 diff, diff2; // no diff3 + UINT8 diff, diff2; for (;;) { @@ -1412,12 +1590,11 @@ static void UnArchiveLines(void) I_Error("Invalid line number %u from server", i); diff = READUINT8(save_p); - li = &lines[i]; - if (diff & LD_DIFF2) diff2 = READUINT8(save_p); else diff2 = 0; + li = &lines[i]; if (diff & LD_FLAG) li->flags = READINT16(save_p); @@ -1425,33 +1602,13 @@ static void UnArchiveLines(void) li->special = READINT16(save_p); if (diff & LD_CLLCOUNT) li->callcount = READINT16(save_p); - - si = &sides[li->sidenum[0]]; - if (diff & LD_S1TEXOFF) - si->textureoffset = READFIXED(save_p); - if (diff & LD_S1TOPTEX) - si->toptexture = READINT32(save_p); - if (diff & LD_S1BOTTEX) - si->bottomtexture = READINT32(save_p); - if (diff & LD_S1MIDTEX) - si->midtexture = READINT32(save_p); - - si = &sides[li->sidenum[1]]; - if (diff2 & LD_S2TEXOFF) - si->textureoffset = READFIXED(save_p); - if (diff2 & LD_S2TOPTEX) - si->toptexture = READINT32(save_p); - if (diff2 & LD_S2BOTTEX) - si->bottomtexture = READINT32(save_p); - if (diff2 & LD_S2MIDTEX) - si->midtexture = READINT32(save_p); - if (diff2 & LD_ARGS) + if (diff & LD_ARGS) { UINT8 j; for (j = 0; j < NUMLINEARGS; j++) li->args[j] = READINT32(save_p); } - if (diff2 & LD_STRINGARGS) + if (diff & LD_STRINGARGS) { UINT8 j; for (j = 0; j < NUMLINESTRINGARGS; j++) @@ -1472,9 +1629,14 @@ static void UnArchiveLines(void) li->stringargs[j][len] = '\0'; } } + if (diff & LD_SIDE1) + UnArchiveSide(&sides[li->sidenum[0]]); + if (diff & LD_SIDE2) + UnArchiveSide(&sides[li->sidenum[1]]); if (diff2 & LD_EXECUTORDELAY) li->executordelay = READINT32(save_p); - + if (diff2 & LD_TRANSFPORTAL) + li->secportal = READUINT32(save_p); } } @@ -1943,7 +2105,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) if (diff2 & MD2_CVMEM) WRITEINT32(save_p, mobj->cvmem); if (diff2 & MD2_SKIN) - WRITEUINT8(save_p, (UINT8)((skin_t *)mobj->skin - skins)); + WRITEUINT8(save_p, (UINT8)(((skin_t *)mobj->skin)->skinnum)); if (diff2 & MD2_COLOR) WRITEUINT16(save_p, mobj->color); if (diff2 & MD2_EXTVAL1) @@ -2997,7 +3159,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) if (diff2 & MD2_CVMEM) mobj->cvmem = READINT32(save_p); if (diff2 & MD2_SKIN) - mobj->skin = &skins[READUINT8(save_p)]; + mobj->skin = skins[READUINT8(save_p)]; if (diff2 & MD2_COLOR) mobj->color = READUINT16(save_p); if (diff2 & MD2_EXTVAL1) @@ -4736,6 +4898,86 @@ static inline void P_NetUnArchiveEmblems(void) } } +static void P_NetArchiveSectorPortals(void) +{ + WRITEUINT32(save_p, ARCHIVEBLOCK_SECPORTALS); + + WRITEUINT32(save_p, secportalcount); + + for (size_t i = 0; i < secportalcount; i++) + { + UINT8 type = secportals[i].type; + + WRITEUINT8(save_p, type); + WRITEFIXED(save_p, secportals[i].origin.x); + WRITEFIXED(save_p, secportals[i].origin.y); + + switch (type) + { + case SECPORTAL_LINE: + WRITEUINT32(save_p, SaveLine(secportals[i].line.start)); + WRITEUINT32(save_p, SaveLine(secportals[i].line.dest)); + break; + case SECPORTAL_PLANE: + case SECPORTAL_HORIZON: + case SECPORTAL_FLOOR: + case SECPORTAL_CEILING: + WRITEUINT32(save_p, SaveSector(secportals[i].sector)); + break; + case SECPORTAL_OBJECT: + if (secportals[i].mobj && !P_MobjWasRemoved(secportals[i].mobj)) + SaveMobjnum(secportals[i].mobj); + else + WRITEUINT32(save_p, 0); + break; + default: + break; + } + } +} + +static void P_NetUnArchiveSectorPortals(void) +{ + if (READUINT32(save_p) != ARCHIVEBLOCK_SECPORTALS) + I_Error("Bad $$$.sav at archive block Secportals"); + + Z_Free(secportals); + P_InitSectorPortals(); + + UINT32 count = READUINT32(save_p); + + for (UINT32 i = 0; i < count; i++) + { + UINT32 id = P_NewSectorPortal(); + + sectorportal_t *secportal = &secportals[id]; + + secportal->type = READUINT8(save_p); + secportal->origin.x = READFIXED(save_p); + secportal->origin.y = READFIXED(save_p); + + switch (secportal->type) + { + case SECPORTAL_LINE: + secportal->line.start = LoadLine(READUINT32(save_p)); + secportal->line.dest = LoadLine(READUINT32(save_p)); + break; + case SECPORTAL_PLANE: + case SECPORTAL_HORIZON: + case SECPORTAL_FLOOR: + case SECPORTAL_CEILING: + secportal->sector = LoadSector(READUINT32(save_p)); + break; + case SECPORTAL_OBJECT: + id = READUINT32(save_p); + secportal->mobj = (id == 0) ? NULL : P_FindNewPosition(id); + break; + default: + break; + } + } +} + static inline void P_ArchiveLuabanksAndConsistency(void) { UINT8 i, banksinuse = NUM_LUABANKS; @@ -4822,6 +5064,7 @@ void P_SaveNetGame(boolean resending) P_NetArchiveSpecials(); P_NetArchiveColormaps(); P_NetArchiveWaypoints(); + P_NetArchiveSectorPortals(); } LUA_Archive(); @@ -4862,6 +5105,7 @@ boolean P_LoadNetGame(boolean reloading) P_NetUnArchiveSpecials(); P_NetUnArchiveColormaps(); P_NetUnArchiveWaypoints(); + P_NetUnArchiveSectorPortals(); P_RelinkPointers(); P_FinishMobjs(); } diff --git a/src/p_setup.c b/src/p_setup.c index 3e18c5b2d..1c0315847 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -993,6 +993,9 @@ static void P_InitializeSector(sector_t *ss) ss->lightingdata = NULL; ss->fadecolormapdata = NULL; + ss->portal_floor = UINT32_MAX; + ss->portal_ceiling = UINT32_MAX; + ss->heightsec = -1; ss->camsec = -1; @@ -1055,6 +1058,9 @@ static void P_LoadSectors(UINT8 *data) ss->floorxoffset = ss->flooryoffset = 0; ss->ceilingxoffset = ss->ceilingyoffset = 0; + ss->floorxscale = ss->flooryscale = FRACUNIT; + ss->ceilingxscale = ss->ceilingyscale = FRACUNIT; + ss->floorangle = ss->ceilingangle = 0; ss->floorlightlevel = ss->ceilinglightlevel = 0; @@ -1107,6 +1113,7 @@ static void P_InitializeLinedef(line_t *ld) ld->polyobj = NULL; ld->callcount = 0; + ld->secportal = UINT32_MAX; // cph 2006/09/30 - fix sidedef errors right away. // cph 2002/07/20 - these errors are fatal if not fixed, so apply them @@ -1352,8 +1359,11 @@ static void P_LoadSidedefs(UINT8 *data) } sd->rowoffset = SHORT(msd->rowoffset)<offsetx_top = sd->offsetx_mid = sd->offsetx_bot = 0; - sd->offsety_top = sd->offsety_mid = sd->offsety_bot = 0; + sd->offsetx_top = sd->offsetx_mid = sd->offsetx_bottom = 0; + sd->offsety_top = sd->offsety_mid = sd->offsety_bottom = 0; + + sd->scalex_top = sd->scalex_mid = sd->scalex_bottom = FRACUNIT; + sd->scaley_top = sd->scaley_mid = sd->scaley_bottom = FRACUNIT; P_SetSidedefSector(i, (UINT16)SHORT(msd->sector)); @@ -1693,6 +1703,14 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char sectors[i].ceilingxoffset = FLOAT_TO_FIXED(atof(val)); else if (fastcmp(param, "ypanningceiling")) sectors[i].ceilingyoffset = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "xscalefloor")) + sectors[i].floorxscale = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "yscalefloor")) + sectors[i].flooryscale = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "xscaleceiling")) + sectors[i].ceilingxscale = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "yscaleceiling")) + sectors[i].ceilingyscale = FLOAT_TO_FIXED(atof(val)); else if (fastcmp(param, "rotationfloor")) sectors[i].floorangle = FixedAngle(FLOAT_TO_FIXED(atof(val))); else if (fastcmp(param, "rotationceiling")) @@ -1888,13 +1906,25 @@ static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char else if (fastcmp(param, "offsetx_mid")) sides[i].offsetx_mid = atol(val) << FRACBITS; else if (fastcmp(param, "offsetx_bottom")) - sides[i].offsetx_bot = atol(val) << FRACBITS; + sides[i].offsetx_bottom = atol(val) << FRACBITS; else if (fastcmp(param, "offsety_top")) sides[i].offsety_top = atol(val) << FRACBITS; else if (fastcmp(param, "offsety_mid")) sides[i].offsety_mid = atol(val) << FRACBITS; else if (fastcmp(param, "offsety_bottom")) - sides[i].offsety_bot = atol(val) << FRACBITS; + sides[i].offsety_bottom = atol(val) << FRACBITS; + else if (fastcmp(param, "scalex_top")) + sides[i].scalex_top = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "scalex_mid")) + sides[i].scalex_mid = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "scalex_bottom")) + sides[i].scalex_bottom = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "scaley_top")) + sides[i].scaley_top = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "scaley_mid")) + sides[i].scaley_mid = FLOAT_TO_FIXED(atof(val)); + else if (fastcmp(param, "scaley_bottom")) + sides[i].scaley_bottom = FLOAT_TO_FIXED(atof(val)); else if (fastcmp(param, "texturetop")) sides[i].toptexture = R_TextureNumForName(val); else if (fastcmp(param, "texturebottom")) @@ -2599,10 +2629,22 @@ static void P_WriteTextmap(void) fprintf(f, "offsetx_mid = %d;\n", wsides[i].offsetx_mid >> FRACBITS); if (wsides[i].offsety_mid != 0) fprintf(f, "offsety_mid = %d;\n", wsides[i].offsety_mid >> FRACBITS); - if (wsides[i].offsetx_bot != 0) - fprintf(f, "offsetx_bottom = %d;\n", wsides[i].offsetx_bot >> FRACBITS); - if (wsides[i].offsety_bot != 0) - fprintf(f, "offsety_bottom = %d;\n", wsides[i].offsety_bot >> FRACBITS); + if (wsides[i].offsetx_bottom != 0) + fprintf(f, "offsetx_bottom = %d;\n", wsides[i].offsetx_bottom >> FRACBITS); + if (wsides[i].offsety_bottom != 0) + fprintf(f, "offsety_bottom = %d;\n", wsides[i].offsety_bottom >> FRACBITS); + if (wsides[i].scalex_top != FRACUNIT) + fprintf(f, "scalex_top = %f;\n", FIXED_TO_FLOAT(wsides[i].scalex_top)); + if (wsides[i].scaley_top != FRACUNIT) + fprintf(f, "scaley_top = %f;\n", FIXED_TO_FLOAT(wsides[i].scaley_top)); + if (wsides[i].scalex_mid != FRACUNIT) + fprintf(f, "scalex_mid = %f;\n", FIXED_TO_FLOAT(wsides[i].scalex_mid)); + if (wsides[i].scaley_mid != FRACUNIT) + fprintf(f, "scaley_mid = %f;\n", FIXED_TO_FLOAT(wsides[i].scaley_mid)); + if (wsides[i].scalex_bottom != FRACUNIT) + fprintf(f, "scalex_bottom = %f;\n", FIXED_TO_FLOAT(wsides[i].scalex_bottom)); + if (wsides[i].scaley_bottom != FRACUNIT) + fprintf(f, "scaley_bottom = %f;\n", FIXED_TO_FLOAT(wsides[i].scaley_bottom)); if (wsides[i].toptexture > 0 && wsides[i].toptexture < numtextures) fprintf(f, "texturetop = \"%.*s\";\n", 8, textures[wsides[i].toptexture]->name); if (wsides[i].bottomtexture > 0 && wsides[i].bottomtexture < numtextures) @@ -2658,6 +2700,14 @@ static void P_WriteTextmap(void) fprintf(f, "xpanningceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceilingxoffset)); if (tempsec.ceilingyoffset != 0) fprintf(f, "ypanningceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceilingyoffset)); + if (tempsec.floorxscale != 0) + fprintf(f, "xscalefloor = %f;\n", FIXED_TO_FLOAT(tempsec.floorxscale)); + if (tempsec.flooryscale != 0) + fprintf(f, "yscalefloor = %f;\n", FIXED_TO_FLOAT(tempsec.flooryscale)); + if (tempsec.ceilingxscale != 0) + fprintf(f, "xscaleceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceilingxscale)); + if (tempsec.ceilingyscale != 0) + fprintf(f, "yscaleceiling = %f;\n", FIXED_TO_FLOAT(tempsec.ceilingyscale)); if (wsectors[i].floorangle != 0) fprintf(f, "rotationfloor = %f;\n", FIXED_TO_FLOAT(AngleFixed(wsectors[i].floorangle))); if (wsectors[i].ceilingangle != 0) @@ -2893,6 +2943,9 @@ static void P_LoadTextmap(void) sc->floorxoffset = sc->flooryoffset = 0; sc->ceilingxoffset = sc->ceilingyoffset = 0; + sc->floorxscale = sc->flooryscale = FRACUNIT; + sc->ceilingxscale = sc->ceilingyscale = FRACUNIT; + sc->floorangle = sc->ceilingangle = 0; sc->floorlightlevel = sc->ceilinglightlevel = 0; @@ -2927,22 +2980,26 @@ static void P_LoadTextmap(void) P_InitializeSector(sc); if (textmap_colormap.used) { - INT32 rgba = P_ColorToRGBA(textmap_colormap.lightcolor, textmap_colormap.lightalpha); - INT32 fadergba = P_ColorToRGBA(textmap_colormap.fadecolor, textmap_colormap.fadealpha); + // Convert alpha values from old 0-25 (A-Z) range to 0-255 range + UINT8 lightalpha = (textmap_colormap.lightalpha * 102) / 10; + UINT8 fadealpha = (textmap_colormap.fadealpha * 102) / 10; + + INT32 rgba = P_ColorToRGBA(textmap_colormap.lightcolor, lightalpha); + INT32 fadergba = P_ColorToRGBA(textmap_colormap.fadecolor, fadealpha); sc->extra_colormap = sc->spawn_extra_colormap = R_CreateColormap(rgba, fadergba, textmap_colormap.fadestart, textmap_colormap.fadeend, textmap_colormap.flags); } if (textmap_planefloor.defined == (PD_A|PD_B|PD_C|PD_D)) - { + { sc->f_slope = MakeViaEquationConstants(textmap_planefloor.a, textmap_planefloor.b, textmap_planefloor.c, textmap_planefloor.d); sc->hasslope = true; - } + } if (textmap_planeceiling.defined == (PD_A|PD_B|PD_C|PD_D)) - { + { sc->c_slope = MakeViaEquationConstants(textmap_planeceiling.a, textmap_planeceiling.b, textmap_planeceiling.c, textmap_planeceiling.d); sc->hasslope = true; - } + } TextmapFixFlatOffsets(sc); } @@ -2979,8 +3036,10 @@ static void P_LoadTextmap(void) // Defaults. sd->textureoffset = 0; sd->rowoffset = 0; - sd->offsetx_top = sd->offsetx_mid = sd->offsetx_bot = 0; - sd->offsety_top = sd->offsety_mid = sd->offsety_bot = 0; + sd->offsetx_top = sd->offsetx_mid = sd->offsetx_bottom = 0; + sd->offsety_top = sd->offsety_mid = sd->offsety_bottom = 0; + sd->scalex_top = sd->scalex_mid = sd->scalex_bottom = FRACUNIT; + sd->scaley_top = sd->scaley_mid = sd->scaley_bottom = FRACUNIT; sd->toptexture = R_TextureNumForName("-"); sd->midtexture = R_TextureNumForName("-"); sd->bottomtexture = R_TextureNumForName("-"); @@ -3306,7 +3365,7 @@ static void P_LoadSegs(UINT8 *data) seg->length = P_SegLength(seg); #ifdef HWRENDER - seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0; + seg->flength = P_SegLengthFloat(seg); #endif seg->glseg = false; @@ -3548,7 +3607,7 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype } seg->length = P_SegLength(seg); #ifdef HWRENDER - seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0; + seg->flength = P_SegLengthFloat(seg); #endif } @@ -7212,7 +7271,7 @@ static void P_ForceCharacter(const char *forcecharskin) SetPlayerSkinByNum(i, skinnum); if (!netgame) - players[i].skincolor = skins[skinnum].prefcolor; + players[i].skincolor = skins[skinnum]->prefcolor; } } @@ -7255,8 +7314,8 @@ static void P_LoadRecordGhosts(void) if (cv_ghost_bestscore.value == 1 && players[consoleplayer].skin != i) continue; - if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i]->name))) + G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i]->name)); } } @@ -7268,8 +7327,8 @@ static void P_LoadRecordGhosts(void) if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i) continue; - if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i]->name))) + G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i]->name)); } } @@ -7281,8 +7340,8 @@ static void P_LoadRecordGhosts(void) if (cv_ghost_bestrings.value == 1 && players[consoleplayer].skin != i) continue; - if (FIL_FileExists(va("%s-%s-rings-best.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-rings-best.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-rings-best.lmp", gpath, skins[i]->name))) + G_AddGhost(va("%s-%s-rings-best.lmp", gpath, skins[i]->name)); } } @@ -7294,8 +7353,8 @@ static void P_LoadRecordGhosts(void) if (cv_ghost_last.value == 1 && players[consoleplayer].skin != i) continue; - if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i]->name))) + G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i]->name)); } } @@ -7325,8 +7384,8 @@ static void P_LoadNightsGhosts(void) if (cv_ghost_bestscore.value == 1 && players[consoleplayer].skin != i) continue; - if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i]->name))) + G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i]->name)); } } @@ -7338,8 +7397,8 @@ static void P_LoadNightsGhosts(void) if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i) continue; - if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i]->name))) + G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i]->name)); } } @@ -7351,8 +7410,8 @@ static void P_LoadNightsGhosts(void) if (cv_ghost_last.value == 1 && players[consoleplayer].skin != i) continue; - if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i].name))) - G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i].name)); + if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i]->name))) + G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i]->name)); } } @@ -7821,12 +7880,14 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) Patch_FreeTag(PU_PATCH_LOWPRIORITY); Patch_FreeTag(PU_PATCH_ROTATED); Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); + mobjcache = NULL; R_InitializeLevelInterpolators(); P_InitThinkers(); R_InitMobjInterpolators(); P_InitCachedActions(); + P_InitSectorPortals(); // internal game map maplumpname = G_BuildMapName(gamemap); @@ -8259,7 +8320,7 @@ static boolean P_LoadAddon(UINT16 numlumps) { CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap); if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } return true; diff --git a/src/p_spec.c b/src/p_spec.c index 7dc16378e..7bec07c92 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -52,6 +52,10 @@ mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpoint mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs +size_t secportalcount; +size_t secportalcapacity; +sectorportal_t *secportals; + /** Animated texture descriptor * This keeps track of an animated texture or an animated flat. * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t @@ -1836,7 +1840,7 @@ void P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller) return; if (!triggerline->stringargs[0]) return; - if (!(stricmp(triggerline->stringargs[0], skins[actor->player->skin].name) == 0) ^ !!(triggerline->args[1])) + if (!(stricmp(triggerline->stringargs[0], skins[actor->player->skin]->name) == 0) ^ !!(triggerline->args[1])) return; break; case 334: // object dye @@ -2735,7 +2739,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) mo->player->rmomx = mo->player->rmomy = 1; mo->player->cmomx = mo->player->cmomy = 0; P_ResetPlayer(mo->player); - P_SetPlayerMobjState(mo, S_PLAY_STND); + P_SetMobjState(mo, S_PLAY_STND); // Reset bot too. if (bot) { @@ -2746,7 +2750,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) bot->player->rmomx = bot->player->rmomy = 1; bot->player->cmomx = bot->player->cmomy = 0; P_ResetPlayer(bot->player); - P_SetPlayerMobjState(bot, S_PLAY_STND); + P_SetMobjState(bot, S_PLAY_STND); } } break; @@ -4566,7 +4570,7 @@ static void P_ProcessSpeedPad(player_t *player, sector_t *sector, sector_t *rove if (!(player->pflags & PF_SPINNING)) player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); } player->powers[pw_flashing] = TICRATE/3; @@ -4744,7 +4748,7 @@ static void P_ProcessZoomTube(player_t *player, mtag_t sectag, boolean end) if (player->mo->state-states != S_PLAY_ROLL) { - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_spin); } } @@ -4958,7 +4962,7 @@ static void P_ProcessRopeHang(player_t *player, mtag_t sectag) player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY); player->climbing = 0; P_SetThingPosition(player->mo); - P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); + P_SetMobjState(player->mo, S_PLAY_RIDE); } static boolean P_SectorHasSpecial(sector_t *sec) @@ -5017,7 +5021,7 @@ static void P_EvaluateSpecialFlags(player_t *player, sector_t *sector, sector_t if (!player->powers[pw_carry]) { P_ResetPlayer(player); - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); P_SetTarget(&player->mo->tracer, player->mo); player->powers[pw_carry] = CR_FAN; } @@ -5032,7 +5036,7 @@ static void P_EvaluateSpecialFlags(player_t *player, sector_t *sector, sector_t if (!(player->pflags & PF_SPINNING)) { player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartAttackSound(player->mo, sfx_spin); if (abs(player->rmomx) < FixedMul(5*FRACUNIT, player->mo->scale) @@ -5605,6 +5609,8 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, I fflr->bottompic = &sec2->floorpic; fflr->bottomxoffs = &sec2->floorxoffset; fflr->bottomyoffs = &sec2->flooryoffset; + fflr->bottomxscale = &sec2->floorxscale; + fflr->bottomyscale = &sec2->flooryscale; fflr->bottomangle = &sec2->floorangle; // Add the ceiling @@ -5613,6 +5619,8 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, I fflr->toplightlevel = &sec2->lightlevel; fflr->topxoffs = &sec2->ceilingxoffset; fflr->topyoffs = &sec2->ceilingyoffset; + fflr->topxscale = &sec2->ceilingxscale; + fflr->topyscale = &sec2->ceilingyscale; fflr->topangle = &sec2->ceilingangle; // Add slopes @@ -6195,6 +6203,196 @@ fixed_t P_GetSectorGravityFactor(sector_t *sec) return sec->gravity; } +void P_InitSectorPortals(void) +{ + secportalcount = 0; + secportalcapacity = 0; + secportals = NULL; +} + +UINT32 P_NewSectorPortal(void) +{ + size_t i = secportalcount++; + if (i == UINT32_MAX) + I_Error("Too many sector portals"); + + if (secportalcapacity == 0 || secportalcount == secportalcapacity) + { + secportalcapacity = secportalcapacity ? (secportalcapacity * 2) : 16; + secportals = Z_Realloc(secportals, secportalcapacity * sizeof(sectorportal_t), PU_LEVEL, NULL); + } + + secportals[i].type = SECPORTAL_NONE; + + return (UINT32)i; +} + +boolean P_IsSectorPortalValid(sectorportal_t *secportal) +{ + if (secportal == NULL) + return false; + + switch (secportal->type) + { + case SECPORTAL_LINE: + case SECPORTAL_FLOOR: + case SECPORTAL_CEILING: + return true; + case SECPORTAL_OBJECT: + return secportal->mobj && !P_MobjWasRemoved(secportal->mobj); + case SECPORTAL_SKYBOX: + return skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0]); + case SECPORTAL_PLANE: + case SECPORTAL_HORIZON: + return true; + default: + return false; + } +} + +boolean P_SectorHasPortal(sector_t *sector) +{ + return P_SectorHasFloorPortal(sector) || P_SectorHasCeilingPortal(sector); +} + +sectorportal_t *P_SectorGetFloorPortal(sector_t *sector) +{ + UINT32 num = sector->portal_floor; + if (num >= secportalcount) + return NULL; + + return &secportals[num]; +} + +sectorportal_t *P_SectorGetCeilingPortal(sector_t *sector) +{ + UINT32 num = sector->portal_ceiling; + if (num >= secportalcount) + return NULL; + + return &secportals[num]; +} + +boolean P_SectorHasFloorPortal(sector_t *sector) +{ + return P_IsSectorPortalValid(P_SectorGetFloorPortal(sector)); +} + +boolean P_SectorHasCeilingPortal(sector_t *sector) +{ + return P_IsSectorPortalValid(P_SectorGetCeilingPortal(sector)); +} + +boolean P_CompareSectorPortals(sectorportal_t *a, sectorportal_t *b) +{ + if (a == NULL && b == NULL) + return true; + else if (!a || !b) + return false; + else if (a->type != b->type) + return false; + + switch (a->type) + { + case SECPORTAL_LINE: + return a->line.start == b->line.start && a->line.dest == b->line.dest; + case SECPORTAL_FLOOR: + case SECPORTAL_CEILING: + return a->sector == b->sector; + case SECPORTAL_OBJECT: + return a->mobj == b->mobj; + default: + return true; + } +} + +static mobj_t *P_GetMobjByTag(INT32 tag) +{ + INT32 mtnum = -1; + + TAG_ITER_THINGS(tag, mtnum) + { + mobj_t *mo = mapthings[mtnum].mobj; + if (mo) + return mo; + } + + return NULL; +} + +static void P_DoPortalCopyFromLine(sector_t *dest_sector, int plane_type, int tag) +{ + INT32 secnum = -1; + TAG_ITER_SECTORS(tag, secnum) + { + sector_t *src_sector = §ors[secnum]; + if (plane_type == TMP_FLOOR || plane_type == TMP_BOTH) + dest_sector->portal_floor = src_sector->portal_floor; + if (plane_type == TMP_CEILING || plane_type == TMP_BOTH) + dest_sector->portal_ceiling = src_sector->portal_ceiling; + } +} + +static sectorportal_t *P_SectorGetPortalOrCreate(sector_t *sector, UINT32 *num, UINT32 *result) +{ + sectorportal_t *secportal = NULL; + + if (*num >= secportalcount) + { + *num = P_NewSectorPortal(); + secportal = &secportals[*num]; + secportal->origin.x = sector->soundorg.x; + secportal->origin.y = sector->soundorg.y; + *result = *num; + } + else + { + *result = *num; + secportal = &secportals[*num]; + } + + return secportal; +} + +static sectorportal_t *P_SectorGetFloorPortalOrCreate(sector_t *sector, UINT32 *result) +{ + return P_SectorGetPortalOrCreate(sector, §or->portal_floor, result); +} + +static sectorportal_t *P_SectorGetCeilingPortalOrCreate(sector_t *sector, UINT32 *result) +{ + return P_SectorGetPortalOrCreate(sector, §or->portal_ceiling, result); +} + +static void P_CopySectorPortalToLines(UINT32 portal_num, int sector_tag) +{ + for (size_t i = 0; i < numlines; i++) + { + if (lines[i].special != SPECIAL_SECTOR_SETPORTAL) + continue; + + if (lines[i].args[1] != TMSECPORTAL_COPY_PORTAL_TO_LINE) + continue; + + if (lines[i].args[3] != sector_tag) + continue; + + if (lines[i].args[0] != 0) + { + INT32 linenum = -1; + TAG_ITER_LINES(lines[i].args[0], linenum) + { + lines[linenum].secportal = portal_num; + } + } + else + { + // Just transfer it to this line + lines[i].secportal = portal_num; + } + } +} + /** After the map has loaded, scans for specials that spawn 3Dfloors and * thinkers. * @@ -6361,6 +6559,146 @@ void P_SpawnSpecials(boolean fromnetsave) P_AddCameraScanner(§ors[sec], §ors[s], R_PointToAngle2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y)); break; + case SPECIAL_SECTOR_SETPORTAL: // Sector portal + { + int target_sector_tag = lines[i].args[0]; + int portal_type = lines[i].args[1]; + int plane_type = lines[i].args[2]; + int misc = lines[i].args[3]; + + boolean floor, ceiling; + if (plane_type == TMP_BOTH) + floor = ceiling = true; + else + { + floor = plane_type == TMP_FLOOR; + ceiling = plane_type == TMP_CEILING; + } + + UINT32 portal_num = UINT32_MAX; + + // Eternity's floor and horizon portal types + if (portal_type == TMSECPORTAL_PLANE || portal_type == TMSECPORTAL_HORIZON) + { + secportaltype_e type = portal_type == TMSECPORTAL_HORIZON ? SECPORTAL_HORIZON : SECPORTAL_PLANE; + if (floor) + { + sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(lines[i].frontsector, &portal_num); + floorportal->type = type; + floorportal->sector = lines[i].frontsector; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + if (ceiling) + { + sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(lines[i].frontsector, &portal_num); + ceilportal->type = type; + ceilportal->sector = lines[i].frontsector; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + break; + } + + INT32 s1 = -1; + + TAG_ITER_SECTORS(target_sector_tag, s1) + { + sector_t *target_sector = §ors[s1]; + + // Line portal + if (portal_type == TMSECPORTAL_NORMAL) + { + INT32 linenum = -1; + TAG_ITER_LINES(misc, linenum) + { + if (lines[linenum].special == SPECIAL_SECTOR_SETPORTAL + && lines[linenum].args[0] == target_sector_tag + && lines[linenum].args[1] == portal_type + && lines[linenum].args[2] == plane_type + && lines[linenum].args[3] == 1) + { + if (floor) + { + sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector, &portal_num); + floorportal->type = SECPORTAL_LINE; + floorportal->line.start = &lines[i]; + floorportal->line.dest = &lines[linenum]; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + if (ceiling) + { + sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector, &portal_num); + ceilportal->type = SECPORTAL_LINE; + ceilportal->line.start = &lines[i]; + ceilportal->line.dest = &lines[linenum]; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + } + } + } + // Skybox portal + else if (portal_type == TMSECPORTAL_SKYBOX) + { + if (floor) + { + sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector, &portal_num); + floorportal->type = SECPORTAL_SKYBOX; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + if (ceiling) + { + sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector, &portal_num); + ceilportal->type = SECPORTAL_SKYBOX; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + } + // Plane portal + else if (portal_type == TMSECPORTAL_SECTOR) + { + INT32 s2 = -1; + TAG_ITER_SECTORS(misc, s2) // Sector tag to make a portal to + { + sector_t *view_sector = §ors[s2]; + if (floor) + { + sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector, &portal_num); + floorportal->type = SECPORTAL_CEILING; + floorportal->sector = view_sector; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + if (ceiling) + { + sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector, &portal_num); + ceilportal->type = SECPORTAL_FLOOR; + ceilportal->sector = view_sector; + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + } + } + // Use mobj as viewpoint + else if (portal_type == TMSECPORTAL_OBJECT) + { + mobj_t *mobj = P_GetMobjByTag(misc); + if (!mobj) + break; + if (floor) + { + sectorportal_t *floorportal = P_SectorGetFloorPortalOrCreate(target_sector, &portal_num); + floorportal->type = SECPORTAL_OBJECT; + P_SetTarget(&floorportal->mobj, mobj); + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + if (ceiling) + { + sectorportal_t *ceilportal = P_SectorGetCeilingPortalOrCreate(target_sector, &portal_num); + ceilportal->type = SECPORTAL_OBJECT; + P_SetTarget(&ceilportal->mobj, mobj); + P_CopySectorPortalToLines(portal_num, target_sector_tag); + } + } + } + break; + } + case 7: // Flat alignment - redone by toast { // Set calculated offsets such that line's v1 is the apparent origin @@ -7136,10 +7474,6 @@ void P_SpawnSpecials(boolean fromnetsave) } } - - - - // Allocate each list for (i = 0; i < numsectors; i++) if(secthinkers[i].thinkers) @@ -7168,6 +7502,33 @@ void P_SpawnSpecials(boolean fromnetsave) } } + // Copy portals + for (i = 0; i < numlines; i++) + { + if (lines[i].special != SPECIAL_SECTOR_SETPORTAL) + continue; + + int portal_type = lines[i].args[1]; + if (portal_type != TMSECPORTAL_COPIED) + continue; + + int target_sector_tag = lines[i].args[0]; + int plane_type = lines[i].args[2]; + int tag_to_copy = lines[i].args[3]; + + if (plane_type == 3) + plane_type = TMP_BOTH; + + if (target_sector_tag == 0) + P_DoPortalCopyFromLine(lines[i].frontsector, plane_type, tag_to_copy); + else + { + INT32 s1 = -1; + TAG_ITER_SECTORS(target_sector_tag, s1) + P_DoPortalCopyFromLine(§ors[s1], plane_type, tag_to_copy); + } + } + if (!fromnetsave) P_RunLevelLoadExecutors(); } diff --git a/src/p_spec.h b/src/p_spec.h index 50ab6410f..3bbaba58b 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -21,6 +21,10 @@ extern mobj_t *skyboxmo[2]; // current skybox mobjs: 0 = viewpoint, 1 = centerpo extern mobj_t *skyboxviewpnts[16]; // array of MT_SKYBOX viewpoint mobjs extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs +extern size_t secportalcount; +extern size_t secportalcapacity; +extern sectorportal_t *secportals; + // Amount (dx, dy) vector linedef is shifted right to get scroll amount #define SCROLL_SHIFT 5 @@ -472,6 +476,20 @@ typedef enum TMB_MODULATE = 4, } textmapblendmodes_t; +typedef enum +{ + TMSECPORTAL_NORMAL = 0, + TMSECPORTAL_COPIED = 1, + TMSECPORTAL_SKYBOX = 2, + TMSECPORTAL_PLANE = 3, + TMSECPORTAL_HORIZON = 4, + TMSECPORTAL_COPY_PORTAL_TO_LINE = 5, + TMSECPORTAL_INTERACTIVE = 6, // unimplemented + // The two portal types below are new to SRB2 + TMSECPORTAL_SECTOR = 7, + TMSECPORTAL_OBJECT = 8 +} textmapsecportaltype_t; + // GETSECSPECIAL (specialval, section) // // Pulls out the special # from a particular section. @@ -521,6 +539,19 @@ INT32 P_FindMinSurroundingLight(sector_t *sector, INT32 max); void P_SetupSignExit(player_t *player); boolean P_IsFlagAtBase(mobjtype_t flag); +void P_InitSectorPortals(void); +UINT32 P_NewSectorPortal(void); + +boolean P_IsSectorPortalValid(sectorportal_t *secportal); + +sectorportal_t *P_SectorGetFloorPortal(sector_t *sector); +sectorportal_t *P_SectorGetCeilingPortal(sector_t *sector); + +boolean P_SectorHasPortal(sector_t *sector); +boolean P_SectorHasFloorPortal(sector_t *sector); +boolean P_SectorHasCeilingPortal(sector_t *sector); +boolean P_CompareSectorPortals(sectorportal_t *a, sectorportal_t *b); + boolean P_IsMobjTouchingSectorPlane(mobj_t *mo, sector_t *sec); boolean P_IsMobjTouching3DFloor(mobj_t *mo, ffloor_t *ffloor, sector_t *sec); boolean P_IsMobjTouchingPolyobj(mobj_t *mo, polyobj_t *po, sector_t *polysec); diff --git a/src/p_telept.c b/src/p_telept.c index 66b05ff01..32669edaf 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -96,7 +96,7 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, P_ClearStarPost(starpostnum); P_ResetPlayer(thing->player); - P_SetPlayerMobjState(thing, S_PLAY_STND); + P_SetMobjState(thing, S_PLAY_STND); P_FlashPal(thing->player, PAL_MIXUP, 10); } @@ -153,7 +153,7 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle thing->player->rmomx = thing->player->rmomy = 0; thing->player->speed = 0; P_ResetPlayer(thing->player); - P_SetPlayerMobjState(thing, S_PLAY_STND); + P_SetMobjState(thing, S_PLAY_STND); thing->reactiontime = TICRATE/2; // don't move for about half a second thing->player->drawangle = angle; diff --git a/src/p_tick.c b/src/p_tick.c index 1bc7b78bf..4ab388486 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -217,6 +217,7 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker) thlist[n].prev = thinker; thinker->references = 0; // killough 11/98: init reference counter to 0 + thinker->cachable = n == THINK_MOBJ; #ifdef PARANOIA thinker->debug_mobjtype = MT_NULL; @@ -226,21 +227,22 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker) #ifdef PARANOIA static const char *MobjTypeName(const mobj_t *mobj) { + mobjtype_t type; actionf_p1 p1 = mobj->thinker.function.acp1; if (p1 == (actionf_p1)P_MobjThinker) - { - return MOBJTYPE_LIST[mobj->type]; - } - else if (p1 == (actionf_p1)P_RemoveThinkerDelayed) - { - if (mobj->thinker.debug_mobjtype != MT_NULL) - { - return MOBJTYPE_LIST[mobj->thinker.debug_mobjtype]; - } - } + type = mobj->type; + else if (p1 == (actionf_p1)P_RemoveThinkerDelayed && mobj->thinker.debug_mobjtype != MT_NULL) + type = mobj->thinker.debug_mobjtype; + else + return ""; - return ""; + if (type < 0 || type >= NUMMOBJTYPES || (type >= MT_FIRSTFREESLOT && !FREE_MOBJS[type - MT_FIRSTFREESLOT])) + return ""; + else if (type >= MT_FIRSTFREESLOT) + return FREE_MOBJS[type - MT_FIRSTFREESLOT]; // This doesn't include "MT_"... + else + return MOBJTYPE_LIST[type]; } static const char *MobjThinkerName(const mobj_t *mobj) @@ -319,7 +321,16 @@ void P_RemoveThinkerDelayed(thinker_t *thinker) (next->prev = currentthinker = thinker->prev)->next = next; R_DestroyLevelInterpolators(thinker); - Z_Free(thinker); + if (thinker->cachable) + { + // put cachable thinkers in the mobj cache, so we can avoid allocations + ((mobj_t *)thinker)->hnext = mobjcache; + mobjcache = (mobj_t *)thinker; + } + else + { + Z_Free(thinker); + } } // @@ -690,25 +701,11 @@ void P_Ticker(boolean run) { INT32 i; - // Increment jointime and quittime even if paused + // Increment jointime even if paused for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) - { players[i].jointime++; - if (players[i].quittime) - { - players[i].quittime++; - - if (players[i].quittime == 30 * TICRATE && G_TagGametype()) - P_CheckSurvivors(); - - if (server && players[i].quittime >= (tic_t)FixedMul(cv_rejointimeout.value, 60 * TICRATE) - && !(players[i].quittime % TICRATE)) - SendKick(i, KICK_MSG_PLAYER_QUIT); - } - } - if (objectplacing) { if (OP_FreezeObjectplace()) @@ -758,7 +755,9 @@ void P_Ticker(boolean run) ps_lua_mobjhooks.value.i = 0; ps_checkposition_calls.value.i = 0; - LUA_HOOK(PreThinkFrame); + PS_START_TIMING(ps_lua_prethinkframe_time); + LUA_HookPreThinkFrame(); + PS_STOP_TIMING(ps_lua_prethinkframe_time); PS_START_TIMING(ps_playerthink_time); for (i = 0; i < MAXPLAYERS; i++) @@ -856,7 +855,9 @@ void P_Ticker(boolean run) if (modeattacking) G_GhostTicker(); - LUA_HOOK(PostThinkFrame); + PS_START_TIMING(ps_lua_postthinkframe_time); + LUA_HookPostThinkFrame(); + PS_STOP_TIMING(ps_lua_postthinkframe_time); } if (run) diff --git a/src/p_user.c b/src/p_user.c index a856d2592..1ba29c3f3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -393,6 +393,8 @@ void P_GiveFinishFlags(player_t *player) fixed_t xoffs = FINECOSINE(fa); fixed_t yoffs = FINESINE(fa); mobj_t* flag = P_SpawnMobjFromMobj(player->mo, xoffs, yoffs, 0, MT_FINISHFLAG); + if (P_MobjWasRemoved(flag)) + continue; flag->angle = angle; angle += FixedAngle(120*FRACUNIT); @@ -683,8 +685,8 @@ static void P_DeNightserizePlayer(player_t *player) player->mo->flags &= ~MF_NOGRAVITY; - player->mo->skin = &skins[player->skin]; - player->followitem = skins[player->skin].followitem; + player->mo->skin = skins[player->skin]; + player->followitem = skins[player->skin]->followitem; player->mo->color = P_GetPlayerColor(player); G_GhostAddColor(GHC_RETURNSKIN); @@ -694,7 +696,7 @@ static void P_DeNightserizePlayer(player_t *player) else if (player == &players[secondarydisplayplayer]) localaiming2 = 0; - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); // If in a special stage, add some preliminary exit time. if (G_IsSpecialStage(gamemap)) @@ -784,12 +786,12 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->mo->height = P_GetPlayerHeight(player); // Just to make sure jumping into the drone doesn't result in a squashed hitbox. player->oldscale = player->mo->scale; - if (skins[player->skin].sprites[SPR2_NFLY].numframes == 0) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin. + if (skins[player->skin]->sprites[SPR2_NFLY].numframes == 0) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin. { - player->mo->skin = &skins[DEFAULTNIGHTSSKIN]; + player->mo->skin = skins[DEFAULTNIGHTSSKIN]; if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) - player->mo->color = skins[DEFAULTNIGHTSSKIN].prefcolor; - player->followitem = skins[DEFAULTNIGHTSSKIN].followitem; + player->mo->color = skins[DEFAULTNIGHTSSKIN]->prefcolor; + player->followitem = skins[DEFAULTNIGHTSSKIN]->followitem; G_GhostAddColor(GHC_NIGHTSSKIN); } } @@ -948,7 +950,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) P_RunNightserizeExecutors(player->mo); player->powers[pw_carry] = CR_NIGHTSMODE; - P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_TRANS1); + P_SetMobjState(player->mo, S_PLAY_NIGHTS_TRANS1); } pflags_t P_GetJumpFlags(player_t *player) @@ -994,7 +996,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor) fixed_t fallbackspeed; P_ResetPlayer(player); - P_SetPlayerMobjState(player->mo, player->mo->info->painstate); + P_SetMobjState(player->mo, player->mo->info->painstate); if (player->mo->eflags & MFE_VERTICALFLIP) player->mo->z--; @@ -1345,7 +1347,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings) player->mo->momx = player->mo->momy = player->mo->momz = player->cmomx = player->cmomy = player->rmomx = player->rmomy = 0; // Transformation animation - P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS1); + P_SetMobjState(player->mo, S_PLAY_SUPER_TRANS1); if (giverings && player->rings < 50) player->rings = 50; @@ -1398,7 +1400,7 @@ static void P_DoSuperDetransformation(player_t *player) player->powers[pw_flashing] = flashingtics-1; if (player->mo->sprite2 & FF_SPR2SUPER) - P_SetPlayerMobjState(player->mo, player->mo->state-states); + P_SetMobjState(player->mo, player->mo->state-states); // Inform the netgame that the champion has fallen in the heat of battle. if (!G_CoopGametype()) @@ -1889,6 +1891,9 @@ void P_SpawnShieldOrb(player_t *player) } shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype); + if (P_MobjWasRemoved(shieldobj)) + return; + shieldobj->flags2 |= MF2_SHIELD; P_SetTarget(&shieldobj->target, player->mo); P_SetTarget(&shieldobj->dontdrawforviewmobj, player->mo); // Hide the shield in first-person @@ -1904,24 +1909,33 @@ void P_SpawnShieldOrb(player_t *player) if (shieldobj->info->seestate) { ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY); - P_SetTarget(&ov->target, shieldobj); + if (!P_MobjWasRemoved(ov)) + { + P_SetTarget(&ov->target, shieldobj); P_SetTarget(&ov->dontdrawforviewmobj, player->mo); // Hide the shield in first-person - P_SetMobjState(ov, shieldobj->info->seestate); - P_SetTarget(&shieldobj->tracer, ov); + P_SetMobjState(ov, shieldobj->info->seestate); + P_SetTarget(&shieldobj->tracer, ov); + } } if (shieldobj->info->meleestate) { ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY); - P_SetTarget(&ov->target, shieldobj); + if (!P_MobjWasRemoved(ov)) + { + P_SetTarget(&ov->target, shieldobj); P_SetTarget(&ov->dontdrawforviewmobj, player->mo); // Hide the shield in first-person - P_SetMobjState(ov, shieldobj->info->meleestate); + P_SetMobjState(ov, shieldobj->info->meleestate); + } } if (shieldobj->info->missilestate) { ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY); - P_SetTarget(&ov->target, shieldobj); + if (!P_MobjWasRemoved(ov)) + { + P_SetTarget(&ov->target, shieldobj); P_SetTarget(&ov->dontdrawforviewmobj, player->mo); // Hide the shield in first-person - P_SetMobjState(ov, shieldobj->info->missilestate); + P_SetMobjState(ov, shieldobj->info->missilestate); + } } if (player->powers[pw_shield] & SH_FORCE) { @@ -2017,6 +2031,8 @@ void P_SetPower(player_t *player, powertype_t power, UINT16 value) mobj_t *P_SpawnGhostMobj(mobj_t *mobj) { mobj_t *ghost = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_GHOST); + if (P_MobjWasRemoved(ghost)) + return NULL; P_SetTarget(&ghost->target, mobj); P_SetTarget(&ghost->dontdrawforviewmobj, mobj); // Hide the ghost in first-person @@ -2063,10 +2079,13 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) if (mobj->player && mobj->player->followmobj) { mobj_t *ghost2 = P_SpawnGhostMobj(mobj->player->followmobj); - P_SetTarget(&ghost2->tracer, ghost); - P_SetTarget(&ghost->tracer, ghost2); + if (!P_MobjWasRemoved(ghost2)) + { + P_SetTarget(&ghost2->tracer, ghost); + P_SetTarget(&ghost->tracer, ghost2); P_SetTarget(&ghost2->dontdrawforviewmobj, mobj); // Hide the follow-ghost for the non-follow target - ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW); + ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW); + } } // Copy interpolation data :) @@ -2116,6 +2135,8 @@ void P_SpawnThokMobj(player_t *player) zheight = player->mo->ceilingz - FixedMul(mobjinfo[type].height, player->mo->scale); mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type); + if (P_MobjWasRemoved(mobj)) + return; // set to player's angle, just in case mobj->angle = player->drawangle; @@ -2139,7 +2160,8 @@ void P_SpawnThokMobj(player_t *player) } } - P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do + if (!P_MobjWasRemoved(mobj)) + P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do G_GhostAddThok(); } @@ -2177,6 +2199,8 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type) zheight = player->mo->ceilingz - FixedMul(mobjinfo[type].height, player->mo->scale); mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type); + if (P_MobjWasRemoved(mobj)) + return; // set to player's angle, just in case mobj->angle = player->drawangle; @@ -2201,7 +2225,8 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type) } } - P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do + if (!P_MobjWasRemoved(mobj)) + P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do } /** Called when \p player finishes the level. @@ -2265,12 +2290,12 @@ void P_DoPlayerExit(player_t *player) { player->climbing = 0; player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } else if (player->pflags & PF_STARTDASH) { player->pflags &= ~PF_STARTDASH; - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } player->powers[pw_underwater] = 0; player->powers[pw_spacetime] = 0; @@ -2359,7 +2384,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) if (!(player->pflags & PF_STARTDASH) && player->panim != PA_ROLL && player->panim != PA_ETC && player->panim != PA_ABILITY && player->panim != PA_ABILITY2) { - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_spin); } } @@ -2368,7 +2393,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) if (dorollstuff) { player->skidtime = TICRATE; - P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE); + P_SetMobjState(player->mo, S_PLAY_GLIDE); P_SpawnSkidDust(player, player->mo->radius, true); // make sure the player knows they landed player->mo->tics = -1; } @@ -2381,7 +2406,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) if (player->mo->state-states != S_PLAY_GLIDE_LANDING) { P_ResetPlayer(player); - P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE_LANDING); + P_SetMobjState(player->mo, S_PLAY_GLIDE_LANDING); player->pflags |= PF_STASIS; if (player->speed > FixedMul(player->runspeed, player->mo->scale)) player->skidtime += player->mo->tics; @@ -2402,7 +2427,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) if (player->mo->state-states != S_PLAY_MELEE_LANDING) { mobjtype_t type = player->revitem; - P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING); + P_SetMobjState(player->mo, S_PLAY_MELEE_LANDING); player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; S_StartSound(player->mo, sfx_s3k8b); player->pflags |= PF_FULLSTASIS; @@ -2426,15 +2451,18 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) while (i < 5) { missile = P_SpawnMobjFromMobj(player->mo, xo, yo, zo, type); - P_SetTarget(&missile->target, player->mo); - missile->angle = throwang + player->drawangle; - P_Thrust(missile, player->drawangle + ANGLE_90, - P_ReturnThrustY(missile, throwang, mu)); // side to side component - P_Thrust(missile, player->drawangle, mu2); // forward component - P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true); - missile->momz += player->mo->pmomz; - missile->fuse = TICRATE/2; - missile->extravalue2 = ev; + if (!P_MobjWasRemoved(missile)) + { + P_SetTarget(&missile->target, player->mo); + missile->angle = throwang + player->drawangle; + P_Thrust(missile, player->drawangle + ANGLE_90, + P_ReturnThrustY(missile, throwang, mu)); // side to side component + P_Thrust(missile, player->drawangle, mu2); // forward component + P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true); + missile->momz += player->mo->pmomz; + missile->fuse = TICRATE/2; + missile->extravalue2 = ev; + } i++; throwang += ANG30; @@ -2465,28 +2493,28 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) if (player->cmomx || player->cmomy) { if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH) - P_SetPlayerMobjState(player->mo, S_PLAY_DASH); + P_SetMobjState(player->mo, S_PLAY_DASH); else if (player->speed >= runspd && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + P_SetMobjState(player->mo, S_PLAY_RUN); else if ((player->rmomx || player->rmomy) && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } else { if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH) - P_SetPlayerMobjState(player->mo, S_PLAY_DASH); + P_SetMobjState(player->mo, S_PLAY_DASH); else if (player->speed >= runspd && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + P_SetMobjState(player->mo, S_PLAY_RUN); else if ((player->mo->momx || player->mo->momy) && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } } @@ -2516,7 +2544,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) ? 6*FRACUNIT/5 : 5*FRACUNIT/2, false); - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); player->mo->momx = player->mo->momy = 0; clipmomz = false; } @@ -2989,24 +3017,29 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player) : player->mo->z + player->mo->height + FixedMul(8*FRACUNIT, FixedMul(player->mo->scale, player->shieldscale)); mobj_t *numbermobj = P_SpawnMobj(player->mo->x, player->mo->y, height, MT_DROWNNUMBERS); - - timeleft /= (2*TICRATE); // To be strictly accurate it'd need to be ((timeleft/TICRATE) - 1)/2, but integer division rounds down for us - - if (player->charflags & SF_MACHINE) + if (!P_MobjWasRemoved(numbermobj)) { - S_StartSound(player->mo, sfx_buzz1); - timeleft += 6; + timeleft /= (2*TICRATE); // To be strictly accurate it'd need to be ((timeleft/TICRATE) - 1)/2, but integer division rounds down for us + + if (player->charflags & SF_MACHINE) + { + S_StartSound(player->mo, sfx_buzz1); + timeleft += 6; + } + else + S_StartSound(player->mo, sfx_dwnind); + + if (!P_MobjWasRemoved(numbermobj)) + { + if (timeleft) // Don't waste time setting the state if the time is 0. + P_SetMobjState(numbermobj, numbermobj->info->spawnstate+timeleft); + + P_SetTarget(&numbermobj->target, player->mo); + numbermobj->threshold = 40; + numbermobj->destscale = player->mo->scale; + P_SetScale(numbermobj, player->mo->scale); + } } - else - S_StartSound(player->mo, sfx_dwnind); - - if (timeleft) // Don't waste time setting the state if the time is 0. - P_SetMobjState(numbermobj, numbermobj->info->spawnstate+timeleft); - - P_SetTarget(&numbermobj->target, player->mo); - numbermobj->threshold = 40; - numbermobj->destscale = player->mo->scale; - P_SetScale(numbermobj, player->mo->scale); } // Underwater timer runs out else if (timeleft == 1) @@ -3065,8 +3098,11 @@ static void P_CheckInvincibilityTimer(player_t *player) else if (leveltime % (TICRATE/7) == 0) { mobj_t *sparkle = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_IVSP); - sparkle->destscale = player->mo->scale; - P_SetScale(sparkle, player->mo->scale); + if (!P_MobjWasRemoved(sparkle)) + { + sparkle->destscale = player->mo->scale; + P_SetScale(sparkle, player->mo->scale); + } } // Resume normal music stuff. @@ -3121,7 +3157,8 @@ static void P_DoBubbleBreath(player_t *player) y += (P_RandomRange(r, -r)<mo->height>>FRACBITS)<mo->x + stirwaterx, player->mo->y + stirwatery, stirwaterz, MT_SMALLBUBBLE); - bubble->destscale = player->mo->scale; - P_SetScale(bubble,bubble->destscale); + if (!P_MobjWasRemoved(bubble)) + { + bubble->destscale = player->mo->scale; + P_SetScale(bubble,bubble->destscale); + } bubble = P_SpawnMobj( player->mo->x - stirwaterx, player->mo->y - stirwatery, stirwaterz, MT_SMALLBUBBLE); - bubble->destscale = player->mo->scale; - P_SetScale(bubble,bubble->destscale); + if (!P_MobjWasRemoved(bubble)) + { + bubble->destscale = player->mo->scale; + P_SetScale(bubble,bubble->destscale); + } } } @@ -3190,22 +3233,25 @@ static void P_DoPlayerHeadSigns(player_t *player) if (player->pflags & PF_TAGIT && (!P_IsLocalPlayer(player) || consoleplayer != displayplayer || splitscreen)) { sign = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TAG); - sign->x = player->mo->x; - sign->y = player->mo->y; - sign->z = player->mo->z; - sign->old_x = player->mo->old_x; - sign->old_y = player->mo->old_y; - sign->old_z = player->mo->old_z; + if (!P_MobjWasRemoved(sign)) + { + sign->x = player->mo->x; + sign->y = player->mo->y; + sign->z = player->mo->z; + sign->old_x = player->mo->old_x; + sign->old_y = player->mo->old_y; + sign->old_z = player->mo->old_z; - if (!(player->mo->eflags & MFE_VERTICALFLIP)) - { - sign->z += player->mo->height; - sign->old_z += player->mo->height; - } - else - { - sign->z -= mobjinfo[MT_TAG].height; - sign->old_z -= mobjinfo[MT_TAG].height; + if (!(player->mo->eflags & MFE_VERTICALFLIP)) + { + sign->z += player->mo->height; + sign->old_z += player->mo->height; + } + else + { + sign->z -= mobjinfo[MT_TAG].height; + sign->old_z -= mobjinfo[MT_TAG].height; + } } } } @@ -3229,24 +3275,26 @@ static void P_DoPlayerHeadSigns(player_t *player) } sign = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_GOTFLAG); - sign->x = player->mo->x; - sign->y = player->mo->y; - sign->z = player->mo->z + zofs; - sign->old_x = player->mo->old_x; - sign->old_y = player->mo->old_y; - sign->old_z = player->mo->old_z + zofs; - - if (player_is_flipped) + if (!P_MobjWasRemoved(sign)) { - sign->eflags |= MFE_VERTICALFLIP; - } + sign->x = player->mo->x; + sign->y = player->mo->y; + sign->z = player->mo->z + zofs; + sign->old_x = player->mo->old_x; + sign->old_y = player->mo->old_y; + sign->old_z = player->mo->old_z + zofs; - if (player->gotflag & GF_REDFLAG) - sign->frame = 1|FF_FULLBRIGHT; - else //if (player->gotflag & GF_BLUEFLAG) - sign->frame = 2|FF_FULLBRIGHT; + if (player_is_flipped) + { + sign->eflags |= MFE_VERTICALFLIP; + } + + if (player->gotflag & GF_REDFLAG) + sign->frame = 1|FF_FULLBRIGHT; + else //if (player->gotflag & GF_BLUEFLAG) + sign->frame = 2|FF_FULLBRIGHT; + } } - } if (!P_MobjWasRemoved(sign) && splitscreen) // Hide the sign from yourself in splitscreen - In single-screen, it wouldn't get spawned if it shouldn't be visible { @@ -3284,6 +3332,7 @@ static void P_DoPlayerHeadSigns(player_t *player) } #endif } + } } // @@ -3648,9 +3697,9 @@ static void P_DoClimbing(player_t *player) if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz) && player->mo->state-states != S_PLAY_CLIMB) - P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB); + P_SetMobjState(player->mo, S_PLAY_CLIMB); else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING) - P_SetPlayerMobjState(player->mo, S_PLAY_CLING); + P_SetMobjState(player->mo, S_PLAY_CLING); if (!floorclimb) { @@ -3665,14 +3714,14 @@ static void P_DoClimbing(player_t *player) player->climbing = 0; player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } if (skyclimber) { player->climbing = 0; player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } } @@ -3683,15 +3732,15 @@ static void P_DoClimbing(player_t *player) if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz) && player->mo->state-states != S_PLAY_CLIMB) - P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB); + P_SetMobjState(player->mo, S_PLAY_CLIMB); else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING) - P_SetPlayerMobjState(player->mo, S_PLAY_CLING); + P_SetMobjState(player->mo, S_PLAY_CLING); if (cmd->buttons & BT_SPIN && !(player->pflags & PF_JUMPSTASIS)) { player->climbing = 0; player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); P_SetObjectMomZ(player->mo, 4*FRACUNIT, false); P_Thrust(player->mo, player->mo->angle, FixedMul(-4*FRACUNIT, player->mo->scale)); } @@ -3707,12 +3756,12 @@ static void P_DoClimbing(player_t *player) } if (player->climbing == 0) - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); if (player->climbing && P_IsObjectOnGround(player->mo)) { P_ResetPlayer(player); - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } } @@ -4074,10 +4123,10 @@ teeterdone: if (teeter) { if (player->panim == PA_IDLE) - P_SetPlayerMobjState(player->mo, S_PLAY_EDGE); + P_SetMobjState(player->mo, S_PLAY_EDGE); } else if (player->panim == PA_EDGE) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } // @@ -4356,7 +4405,7 @@ static void P_DoSuperStuff(player_t *player) player->mo->color = (player->pflags & PF_GODMODE && cv_debug == 0) ? (SKINCOLOR_SUPERSILVER1 + 5*(((signed)leveltime >> 1) % 7)) // A wholesome easter egg. - : skins[player->skin].supercolor + abs( ( (player->powers[pw_super] >> 1) % 9) - 4); // This is where super flashing is handled. + : skins[player->skin]->supercolor + abs( ( (player->powers[pw_super] >> 1) % 9) - 4); // This is where super flashing is handled. G_GhostAddColor(GHC_SUPER); @@ -4371,8 +4420,11 @@ static void P_DoSuperStuff(player_t *player) && !(leveltime % TICRATE) && (player->mo->momx || player->mo->momy)) { spark = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SUPERSPARK); - spark->destscale = player->mo->scale; - P_SetScale(spark, player->mo->scale); + if (!P_MobjWasRemoved(spark)) + { + spark->destscale = player->mo->scale; + P_SetScale(spark, player->mo->scale); + } } // Ran out of rings while super! @@ -4418,7 +4470,7 @@ boolean P_SuperReady(player_t *player, boolean transform) // // Jump routine for the player // -void P_DoJump(player_t *player, boolean soundandstate) +void P_DoJump(player_t *player, boolean soundandstate, boolean allowflip) { fixed_t factor; const fixed_t dist6 = FixedMul(FixedDiv(player->speed, player->mo->scale), player->actionspd)/20; @@ -4591,7 +4643,7 @@ void P_DoJump(player_t *player, boolean soundandstate) if (player->charflags & SF_NOJUMPDAMAGE) player->pflags &= ~PF_SPINNING; - if (P_InJumpFlipSector(player->mo)) // Flip gravity on jump? + if (allowflip && P_InJumpFlipSector(player->mo)) // Flip gravity on jump? { player->mo->flags2 ^= MF2_OBJECTFLIP; S_StartSound(player->mo, sfx_s3k73); // Play gravity flip sound @@ -4602,7 +4654,7 @@ void P_DoJump(player_t *player, boolean soundandstate) if (!player->spectator) S_StartSound(player->mo, sfx_jump); // Play jump sound! - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } } @@ -4613,6 +4665,8 @@ void P_DoSpinDashDust(player_t *player) INT32 prandom[3]; for (i = 0; i <= (leveltime%7)/2; i++) { // 1, 2, 3 or 4 particles particle = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SPINDUST); + if (P_MobjWasRemoved(particle)) + return; if (player->mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)) // overrides fire version P_SetMobjState(particle, S_SPINDUST_BUBBLE1); @@ -4672,7 +4726,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) player->mo->momy = player->cmomy; player->pflags |= (PF_SPINDOWN|PF_STARTDASH|PF_SPINNING); player->dashspeed = player->mindash; - P_SetPlayerMobjState(player->mo, S_PLAY_SPINDASH); + P_SetMobjState(player->mo, S_PLAY_SPINDASH); if (!player->spectator) S_StartSound(player->mo, sfx_spndsh); // Make the rev sound! } @@ -4682,7 +4736,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) if (player->speed > 5*player->mo->scale) { player->pflags &= ~PF_STARTDASH; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_spin); break; } @@ -4715,7 +4769,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) || !canstand) && !(player->pflags & (PF_SPINDOWN|PF_SPINNING))) { player->pflags |= (PF_SPINDOWN|PF_SPINNING); - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); if (!player->spectator) S_StartSound(player->mo, sfx_spin); } @@ -4731,12 +4785,12 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) { if (player->dashspeed) { - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); P_InstaThrust(player->mo, player->mo->angle, (player->speed = FixedMul(player->dashspeed, player->mo->scale))); // catapult forward ho!! } else { - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); player->pflags &= ~PF_SPINNING; } @@ -4760,7 +4814,8 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators { mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker - P_SetTarget(&visual->target, lockon); + if (!P_MobjWasRemoved(visual)) + P_SetTarget(&visual->target, lockon); visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating } } @@ -4768,7 +4823,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) { mobj_t *bullet; - P_SetPlayerMobjState(player->mo, S_PLAY_FIRE); + P_SetMobjState(player->mo, S_PLAY_FIRE); #define zpos(posmo) (posmo->z + (posmo->height - mobjinfo[player->revitem].height)/2) if (lockon) @@ -4809,10 +4864,10 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) #if 0 if ((player->charability == CA_TWINSPIN) && (player->speed > FixedMul(player->runspeed, player->mo->scale))) { - P_DoJump(player, false); + P_DoJump(player, false, false); player->pflags &= ~PF_STARTJUMP; player->mo->momz = FixedMul(player->mo->momz, 3*FRACUNIT/2); // NOT 1.5 times the jump height, but 2.25 times. - P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN); + P_SetMobjState(player->mo, S_PLAY_TWINSPIN); S_StartSound(player->mo, sfx_s3k8b); } else @@ -4838,7 +4893,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) } player->mo->momx += player->cmomx; player->mo->momy += player->cmomy; - P_SetPlayerMobjState(player->mo, S_PLAY_MELEE); + P_SetMobjState(player->mo, S_PLAY_MELEE); player->powers[pw_strong] = STR_MELEE; S_StartSound(player->mo, sfx_s3k42); } @@ -4862,7 +4917,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) { player->skidtime = 0; player->pflags &= ~PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); player->mo->momx = player->cmomx; player->mo->momy = player->cmomy; } @@ -4889,7 +4944,7 @@ void P_DoJumpShield(player_t *player) return; player->pflags &= ~PF_JUMPED; - P_DoJump(player, false); + P_DoJump(player, false, true); player->secondjump = 0; player->pflags |= PF_THOKKED|PF_SHIELDABILITY; player->pflags &= ~(PF_STARTJUMP|PF_SPINNING|PF_BOUNCING); @@ -4903,21 +4958,24 @@ void P_DoJumpShield(player_t *player) for (i = 0; i < numangles; i++) { spark = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_THUNDERCOIN_SPARK); - P_InstaThrust(spark, travelangle + i*(ANGLE_MAX/numangles), FixedMul(4*FRACUNIT, spark->scale)); - if (i % 2) - P_SetObjectMomZ(spark, -4*FRACUNIT, false); - spark->fuse = 18; + if (!P_MobjWasRemoved(spark)) + { + P_InstaThrust(spark, travelangle + i*(ANGLE_MAX/numangles), FixedMul(4*FRACUNIT, spark->scale)); + if (i % 2) + P_SetObjectMomZ(spark, -4*FRACUNIT, false); + spark->fuse = 18; + } } #undef limitangle #undef numangles player->pflags &= ~PF_NOJUMPDAMAGE; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_s3k45); } else { player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE); - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); S_StartSound(player->mo, sfx_wdjump); } } @@ -4932,11 +4990,11 @@ void P_DoBubbleBounce(player_t *player) player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SHIELDABILITY); S_StartSound(player->mo, sfx_s3k44); P_MobjCheckWater(player->mo); - P_DoJump(player, false); + P_DoJump(player, false, false); if (player->charflags & SF_NOJUMPSPIN) - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); else - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); player->pflags |= PF_THOKKED; player->pflags &= ~PF_STARTJUMP; player->secondjump = UINT8_MAX; @@ -4962,7 +5020,7 @@ void P_DoAbilityBounce(player_t *player, boolean changemomz) else if (player->mo->eflags & MFE_UNDERWATER) prevmomz /= 2; - P_DoJump(player, false); + P_DoJump(player, false, false); player->pflags &= ~(PF_STARTJUMP|PF_JUMPED); minmomz = FixedMul(player->mo->momz, 3*FRACUNIT/2); @@ -4973,7 +5031,7 @@ void P_DoAbilityBounce(player_t *player, boolean changemomz) } S_StartSound(player->mo, sfx_boingf); - P_SetPlayerMobjState(player->mo, S_PLAY_BOUNCE_LANDING); + P_SetMobjState(player->mo, S_PLAY_BOUNCE_LANDING); player->pflags |= PF_BOUNCING|PF_THOKKED; } @@ -5013,14 +5071,17 @@ void P_TwinSpinRejuvenate(player_t *player, mobjtype_t type) yo, player->mo->height/2 + zo, type); - P_SetTarget(&missile->target, player->mo); - P_SetScale(missile, (missile->destscale >>= 1)); - missile->angle = ang + movang; - missile->fuse = TICRATE/2; - missile->extravalue2 = (99*FRACUNIT)/100; - missile->momx = xo; - missile->momy = yo; - missile->momz = zo; + if (!P_MobjWasRemoved(missile)) + { + P_SetTarget(&missile->target, player->mo); + P_SetScale(missile, (missile->destscale >>= 1)); + missile->angle = ang + movang; + missile->fuse = TICRATE/2; + missile->extravalue2 = (99*FRACUNIT)/100; + missile->momx = xo; + missile->momy = yo; + missile->momz = zo; + } ang += ANGLE_45; } @@ -5086,7 +5147,7 @@ static void P_DoTwinSpin(player_t *player) player->pflags |= P_GetJumpFlags(player) | PF_THOKKED; S_StartSound(player->mo, sfx_s3k42); player->mo->frame = 0; - P_SetPlayerMobjState(player->mo, S_PLAY_TWINSPIN); + P_SetMobjState(player->mo, S_PLAY_TWINSPIN); player->powers[pw_strong] = STR_TWINSPIN; } @@ -5118,9 +5179,12 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock if (dovis) { visual = P_SpawnMobj(lockonshield->x, lockonshield->y, lockonshield->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker - P_SetTarget(&visual->target, lockonshield); + if (!P_MobjWasRemoved(visual)) + { + P_SetTarget(&visual->target, lockonshield); visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating - P_SetMobjStateNF(visual, visual->info->spawnstate+1); + P_SetMobjStateNF(visual, visual->info->spawnstate+1); + } } } } @@ -5160,7 +5224,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock { player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonshield->x, lockonshield->y); player->pflags &= ~PF_NOJUMPDAMAGE; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_s3k40); player->homing = 3*TICRATE; } @@ -5184,7 +5248,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock player->mo->momx -= (player->mo->momx/3); player->mo->momy -= (player->mo->momy/3); player->pflags &= ~PF_NOJUMPDAMAGE; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_s3k44); } player->secondjump = 0; @@ -5197,7 +5261,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale)); player->drawangle = player->mo->angle; player->pflags &= ~(PF_NOJUMPDAMAGE|PF_SPINNING); - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_s3k43); default: break; @@ -5226,7 +5290,8 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators { visual = P_SpawnMobj(lockonthok->x, lockonthok->y, lockonthok->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker - P_SetTarget(&visual->target, lockonthok); + if (!P_MobjWasRemoved(visual)) + P_SetTarget(&visual->target, lockonthok); visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating } } @@ -5259,9 +5324,9 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) if (player->panim != PA_RUN && player->panim != PA_WALK) { if (player->speed >= FixedMul(player->runspeed, player->mo->scale)) - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN); + P_SetMobjState(player->mo, S_PLAY_FLOAT_RUN); else - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT); + P_SetMobjState(player->mo, S_PLAY_FLOAT); } player->mo->momz = 0; @@ -5328,7 +5393,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) // Jump S3&K style while in quicksand. else if (P_InQuicksand(player->mo)) { - P_DoJump(player, true); + P_DoJump(player, true, false); player->secondjump = 0; player->pflags &= ~PF_THOKKED; } @@ -5341,7 +5406,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) // can't jump while in air, can't jump while jumping else if (onground || player->climbing || player->powers[pw_carry]) { - P_DoJump(player, true); + P_DoJump(player, true, true); player->secondjump = 0; player->pflags &= ~PF_THOKKED; } @@ -5371,7 +5436,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) if ((player->charability == CA_JUMPTHOK) && !(player->pflags & PF_THOKKED)) { player->pflags &= ~PF_JUMPED; - P_DoJump(player, false); + P_DoJump(player, false, true); } P_InstaThrust(player->mo, player->mo->angle, FixedMul(actionspd, player->mo->scale)); @@ -5392,13 +5457,13 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockonthok)); if (lockonthok) { - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockonthok->x, lockonthok->y); player->homing = 3*TICRATE; } else { - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); player->pflags &= ~PF_JUMPED; player->mo->height = P_GetPlayerHeight(player); } @@ -5435,7 +5500,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) ; // Can't do anything if you're a fish out of water! else if (!(player->pflags & PF_THOKKED) && !(player->powers[pw_tailsfly])) { - P_SetPlayerMobjState(player->mo, S_PLAY_FLY); // Change to the flying animation + P_SetMobjState(player->mo, S_PLAY_FLY); // Change to the flying animation player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer @@ -5468,7 +5533,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) player->pflags |= PF_GLIDING|PF_THOKKED; player->glidetime = 0; - P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE); + P_SetMobjState(player->mo, S_PLAY_GLIDE); if (playerspeed < glidespeed) P_Thrust(player->mo, player->mo->angle, glidespeed - playerspeed); player->pflags &= ~(PF_JUMPED|PF_SPINNING|PF_STARTDASH); @@ -5480,7 +5545,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) { player->pflags |= PF_THOKKED; player->pflags &= ~(PF_JUMPED|PF_SPINNING); - P_DoJump(player, true); + P_DoJump(player, true, true); player->secondjump++; } break; @@ -5489,11 +5554,11 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY) { if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD) - P_SetPlayerMobjState(player->mo, S_PLAY_DASH); + P_SetMobjState(player->mo, S_PLAY_DASH); else if (player->speed >= FixedMul(player->runspeed, player->mo->scale)) - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN); + P_SetMobjState(player->mo, S_PLAY_FLOAT_RUN); else - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT); + P_SetMobjState(player->mo, S_PLAY_FLOAT); player->pflags |= PF_THOKKED; player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING); player->secondjump = 1; @@ -5529,7 +5594,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) case CA_BOUNCE: if (!(player->pflags & PF_THOKKED) || player->charflags & SF_MULTIABILITY) { - P_SetPlayerMobjState(player->mo, S_PLAY_BOUNCE); + P_SetMobjState(player->mo, S_PLAY_BOUNCE); player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING); player->pflags |= PF_THOKKED|PF_BOUNCING; player->powers[pw_strong] = STR_BOUNCE; @@ -5619,7 +5684,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) if (!(player->mo->tracer->flags & MF_BOSS)) player->pflags &= ~PF_THOKKED; - P_SetPlayerMobjState(player->mo, S_PLAY_SPRING); + P_SetMobjState(player->mo, S_PLAY_SPRING); player->pflags |= PF_NOJUMPDAMAGE; } } @@ -5667,7 +5732,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) else player->secondjump = 2; - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); } // If letting go of the jump button while still on ascent, cut the jump height. @@ -5787,7 +5852,7 @@ static void P_2dMovement(player_t *player) else if (player->exiting) { player->pflags &= ~PF_GLIDING; - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); player->skidtime = 0; } } @@ -5796,7 +5861,7 @@ static void P_2dMovement(player_t *player) if (player->pflags & PF_SPINNING && !player->exiting) { player->pflags &= ~PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } } @@ -5923,6 +5988,7 @@ static void P_2dMovement(player_t *player) else if (player->rmomx > -topspeed && cmd->sidemove < 0) P_Thrust(player->mo, movepushangle, movepushforward); } + player->mo->friction = ORIG_FRICTION; //katsy: reset player friction AFTER movement code } //#define OLD_MOVEMENT_CODE 1 @@ -5960,7 +6026,7 @@ static void P_3dMovement(player_t *player) else if (player->exiting) { player->pflags &= ~PF_GLIDING; - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); player->skidtime = 0; } } @@ -5969,7 +6035,7 @@ static void P_3dMovement(player_t *player) if (player->pflags & PF_SPINNING && !player->exiting) { player->pflags &= ~PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } } @@ -5995,7 +6061,7 @@ static void P_3dMovement(player_t *player) // Monster Iestyn - 04-11-13 // Quadrants are stupid, excessive and broken, let's do this a much simpler way! // Get delta angle from rmom angle and player angle first - dangle = R_PointToAngle2(0,0, player->rmomx, player->rmomy) - player->mo->angle; + dangle = R_PointToAngle2(0,0, player->rmomx, player->rmomy) - (cmd->angleturn<<16); if (dangle > ANGLE_180) //flip to keep to one side dangle = InvAngle(dangle); @@ -6011,7 +6077,7 @@ static void P_3dMovement(player_t *player) if (player->pflags & PF_SLIDING) cmd->forwardmove = 0; else if (onground && player->mo->state == states+S_PLAY_PAIN) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); player->aiming = cmd->aiming<mo->momy = tempmomy + player->cmomy; } } + player->mo->friction = ORIG_FRICTION; //katsy: reset player friction AFTER movement code } // @@ -6816,12 +6883,12 @@ static void P_DoNiGHTSCapsule(player_t *player) if (player->mo->momx || player->mo->momy || player->mo->momz) { if (player->mo->state != &states[S_PLAY_NIGHTS_PULL]) - P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_PULL); + P_SetMobjState(player->mo, S_PLAY_NIGHTS_PULL); } else if (player->mo->state != &states[S_PLAY_NIGHTS_ATTACK]) { S_StartSound(player->mo, sfx_spin); - P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_ATTACK); + P_SetMobjState(player->mo, S_PLAY_NIGHTS_ATTACK); } } else @@ -6829,7 +6896,7 @@ static void P_DoNiGHTSCapsule(player_t *player) if (!(player->pflags & PF_JUMPED) && !(player->pflags & PF_SPINNING)) player->pflags |= PF_JUMPED; if (player->panim != PA_ROLL) - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); } if (!(player->charflags & SF_NONIGHTSROTATION)) @@ -6912,10 +6979,14 @@ static void P_DoNiGHTSCapsule(player_t *player) // Spawn a 'pop' for every 2 tics if (!((tictimer - firstpoptic) % 2)) - S_StartSound(P_SpawnMobj(player->capsule->x + ((P_SignedRandom()/2)<capsule->y + ((P_SignedRandom()/2)<capsule->z + (player->capsule->height/2) + ((P_SignedRandom()/2)<capsule->x + ((P_SignedRandom()/2)<capsule->y + ((P_SignedRandom()/2)<capsule->z + (player->capsule->height/2) + ((P_SignedRandom()/2)<mo, 0, 0, player->mo->height, MT_GOTEMERALD); - emmo->health = em; // for identification - P_SetTarget(&emmo->target, player->mo); - P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); - P_SetTarget(&player->mo->tracer, emmo); + if (!P_MobjWasRemoved(emmo)) + { + emmo->health = em; // for identification + P_SetTarget(&emmo->target, player->mo); + P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); + P_SetTarget(&player->mo->tracer, emmo); + } } // Okay, we're doing this down here because we're handling time weirdly for co-op special stages @@ -6998,19 +7072,22 @@ static void P_DoNiGHTSCapsule(player_t *player) P_InstaThrust(flicky, flicky->angle, 8*FRACUNIT); }*/ mobj_t *idya = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height, MT_GOTEMERALD); - idya->extravalue2 = player->mare/5; - idya->health = player->mare + 1; // for identification - P_SetTarget(&idya->target, player->mo); - P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate + ((player->mare + 1) % 5)); - - if (player->mo->tracer) + if (!P_MobjWasRemoved(idya)) { - P_SetTarget(&idya->hnext, player->mo->tracer); - idya->extravalue1 = (angle_t)(player->mo->tracer->extravalue1 - 72*ANG1); - if (idya->extravalue1 > player->mo->tracer->extravalue1) - idya->extravalue1 -= (72*ANG1)/idya->extravalue1; + idya->extravalue2 = player->mare/5; + idya->health = player->mare + 1; // for identification + P_SetTarget(&idya->target, player->mo); + P_SetMobjState(idya, mobjinfo[MT_GOTEMERALD].missilestate + ((player->mare + 1) % 5)); + + if (player->mo->tracer) + { + P_SetTarget(&idya->hnext, player->mo->tracer); + idya->extravalue1 = (angle_t)(player->mo->tracer->extravalue1 - 72*ANG1); + if (idya->extravalue1 > player->mo->tracer->extravalue1) + idya->extravalue1 -= (72*ANG1)/idya->extravalue1; + } + P_SetTarget(&player->mo->tracer, idya); } - P_SetTarget(&player->mo->tracer, idya); } for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mare == player->mare) @@ -7308,14 +7385,14 @@ static void P_NiGHTSMovement(player_t *player) if (!(player->charflags & SF_NONIGHTSROTATION) && player->mo->momz) { if (player->mo->state != &states[S_PLAY_NIGHTS_DRILL]) - P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_DRILL); + P_SetMobjState(player->mo, S_PLAY_NIGHTS_DRILL); player->mo->spriteroll = ANGLE_90; } else #endif { if (player->mo->state != &states[S_PLAY_NIGHTS_FLOAT]) - P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_FLOAT); + P_SetMobjState(player->mo, S_PLAY_NIGHTS_FLOAT); player->drawangle += ANGLE_22h; } @@ -7335,19 +7412,25 @@ static void P_NiGHTSMovement(player_t *player) z -= FixedMul(mobjinfo[MT_NIGHTSPARKLE].height, player->mo->scale); firstmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle+ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle+ANGLE_90, spawndist), z, MT_NIGHTSPARKLE); - secondmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle-ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle-ANGLE_90, spawndist), z, MT_NIGHTSPARKLE); - - firstmobj->destscale = secondmobj->destscale = player->mo->scale; - P_SetTarget(&firstmobj->target, player->mo); - P_SetScale(firstmobj, player->mo->scale); - P_SetTarget(&secondmobj->target, player->mo); - P_SetScale(secondmobj, player->mo->scale); - - // Superloop turns sparkles red - if (player->powers[pw_nights_superloop]) + if (!P_MobjWasRemoved(firstmobj)) { - P_SetMobjState(firstmobj, mobjinfo[MT_NIGHTSPARKLE].seestate); - P_SetMobjState(secondmobj, mobjinfo[MT_NIGHTSPARKLE].seestate); + firstmobj->destscale = player->mo->scale; + P_SetTarget(&firstmobj->target, player->mo); + P_SetScale(firstmobj, player->mo->scale); + // Superloop turns sparkles red + if (player->powers[pw_nights_superloop]) + P_SetMobjState(firstmobj, mobjinfo[MT_NIGHTSPARKLE].seestate); + } + secondmobj = P_SpawnMobj(player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle-ANGLE_90, spawndist), player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle-ANGLE_90, spawndist), z, MT_NIGHTSPARKLE); + if (!P_MobjWasRemoved(secondmobj)) + { + secondmobj->destscale = player->mo->scale; + P_SetTarget(&secondmobj->target, player->mo); + P_SetScale(secondmobj, player->mo->scale); + + // Superloop turns sparkles red + if (player->powers[pw_nights_superloop]) + P_SetMobjState(secondmobj, mobjinfo[MT_NIGHTSPARKLE].seestate); } } @@ -7355,9 +7438,12 @@ static void P_NiGHTSMovement(player_t *player) // It also spawns every tic to avoid failed paraloops { mobj_t *helpermobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_NIGHTSLOOPHELPER); - helpermobj->fuse = player->mo->fuse = leveltime; - P_SetTarget(&helpermobj->target, player->mo); - P_SetScale(helpermobj, player->mo->scale); + if (!P_MobjWasRemoved(helpermobj)) + { + helpermobj->fuse = player->mo->fuse = leveltime; + P_SetTarget(&helpermobj->target, player->mo); + P_SetScale(helpermobj, player->mo->scale); + } } if (player->bumpertime) @@ -7546,19 +7632,22 @@ static void P_NiGHTSMovement(player_t *player) mobjtype_t splishtype = (player->mo->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; mobj_t *water = P_SpawnMobj(player->mo->x, player->mo->y, ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[splishtype].height, player->mo->scale) : player->mo->watertop), splishtype); - if (player->mo->eflags & MFE_GOOWATER) - S_StartSound(water, sfx_ghit); - else if (player->mo->eflags & MFE_TOUCHLAVA) - S_StartSound(water, sfx_splash); - else - S_StartSound(water, sfx_wslap); - if (player->mo->eflags & MFE_VERTICALFLIP) + if (!P_MobjWasRemoved(water)) { - water->flags2 |= MF2_OBJECTFLIP; - water->eflags |= MFE_VERTICALFLIP; + if (player->mo->eflags & MFE_GOOWATER) + S_StartSound(water, sfx_ghit); + else if (player->mo->eflags & MFE_TOUCHLAVA) + S_StartSound(water, sfx_splash); + else + S_StartSound(water, sfx_wslap); + if (player->mo->eflags & MFE_VERTICALFLIP) + { + water->flags2 |= MF2_OBJECTFLIP; + water->eflags |= MFE_VERTICALFLIP; + } + water->destscale = player->mo->scale; + P_SetScale(water, player->mo->scale); } - water->destscale = player->mo->scale; - P_SetScale(water, player->mo->scale); } if (player->mo->momx || player->mo->momy) @@ -7639,7 +7728,7 @@ static void P_NiGHTSMovement(player_t *player) } if (player->mo->state != &states[flystate]) - P_SetPlayerMobjState(player->mo, flystate); + P_SetMobjState(player->mo, flystate); if (player->charflags & SF_NONIGHTSROTATION) player->mo->spriteroll = 0; @@ -7791,6 +7880,8 @@ void P_ElementalFire(player_t *player, boolean cropcircle) for (i = 0; i < numangles; i++) { flame = P_SpawnMobj(player->mo->x, player->mo->y, ground, MT_SPINFIRE); + if (P_MobjWasRemoved(flame)) + continue; flame->flags &= ~MF_NOGRAVITY; P_SetTarget(&flame->target, player->mo); flame->angle = travelangle + i*(ANGLE_MAX/numangles); @@ -7828,6 +7919,8 @@ void P_ElementalFire(player_t *player, boolean cropcircle) } flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE); + if (P_MobjWasRemoved(flame)) + continue; P_SetTarget(&flame->target, player->mo); flame->angle = travelangle; flame->fuse = TICRATE*6; @@ -7869,6 +7962,8 @@ void P_SpawnSkidDust(player_t *player, fixed_t radius, boolean sound) mobj_t *particle; particle = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_SPINDUST); + if (P_MobjWasRemoved(particle)) + return; if (radius >>= FRACBITS) { P_UnsetThingPosition(particle); @@ -7905,13 +8000,13 @@ static void P_SkidStuff(player_t *player) player->skidtime = 0; player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE); player->pflags |= PF_THOKKED; - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); } // Get up and brush yourself off, idiot. else if (player->glidetime > 15 || !(player->cmd.buttons & BT_JUMP)) { P_ResetPlayer(player); - P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE_LANDING); + P_SetMobjState(player->mo, S_PLAY_GLIDE_LANDING); player->pflags |= PF_STASIS; if (player->speed > FixedMul(player->runspeed, player->mo->scale)) player->skidtime += player->mo->tics; @@ -7956,7 +8051,7 @@ static void P_SkidStuff(player_t *player) if (dang > ANGLE_157h) { if (player->mo->state-states != S_PLAY_SKID) - P_SetPlayerMobjState(player->mo, S_PLAY_SKID); + P_SetMobjState(player->mo, S_PLAY_SKID); player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; S_StartSound(player->mo, sfx_skid); } @@ -8188,63 +8283,63 @@ void P_MovePlayer(player_t *player) { // If the player is in dashmode, here's their peelout. if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim == PA_RUN && !player->skidtime && (onground || ((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super])) - P_SetPlayerMobjState (player->mo, S_PLAY_DASH); + P_SetMobjState (player->mo, S_PLAY_DASH); // If the player is moving fast enough, // break into a run! else if (player->speed >= runspd && player->panim == PA_WALK && !player->skidtime && (onground || ((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super])) { if (!onground) - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT_RUN); + P_SetMobjState(player->mo, S_PLAY_FLOAT_RUN); else - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + P_SetMobjState(player->mo, S_PLAY_RUN); } // Floating at slow speeds has its own special animation. else if ((((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super]) && player->panim == PA_IDLE && !onground) - P_SetPlayerMobjState (player->mo, S_PLAY_FLOAT); + P_SetMobjState (player->mo, S_PLAY_FLOAT); // Otherwise, just walk. else if ((player->rmomx || player->rmomy) && player->panim == PA_IDLE) - P_SetPlayerMobjState (player->mo, S_PLAY_WALK); + P_SetMobjState (player->mo, S_PLAY_WALK); } // If your peelout animation is playing, and you're // going too slow, switch back to the run. if (player->charflags & SF_DASHMODE && player->panim == PA_DASH && player->dashmode < DASHMODE_THRESHOLD) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + P_SetMobjState(player->mo, S_PLAY_RUN); // If your running animation is playing, and you're // going too slow, switch back to the walking frames. if (player->panim == PA_RUN && player->speed < runspd) { if (!onground && (((player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) && player->secondjump == 1) || player->powers[pw_super])) - P_SetPlayerMobjState(player->mo, S_PLAY_FLOAT); + P_SetMobjState(player->mo, S_PLAY_FLOAT); else - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); } // Correct floating when ending up on the ground. if (onground) { if (player->mo->state-states == S_PLAY_FLOAT) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else if (player->mo->state-states == S_PLAY_FLOAT_RUN) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + P_SetMobjState(player->mo, S_PLAY_RUN); } // If Springing (or nojumpspinning), but travelling DOWNWARD, change back! if ((player->panim == PA_SPRING && P_MobjFlip(player->mo)*player->mo->momz < 0) || ((((player->charflags & SF_NOJUMPSPIN) && (player->pflags & PF_JUMPED) && player->panim == PA_JUMP)) && (P_MobjFlip(player->mo)*player->mo->momz < 0))) - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); // If doing an air animation but on the ground, change back! else if (onground && (player->panim == PA_SPRING || player->panim == PA_FALL || player->panim == PA_RIDE || player->panim == PA_JUMP) && !player->mo->momz) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); // If you are stopped and are still walking, stand still! if (!player->mo->momx && !player->mo->momy && !player->mo->momz && player->panim == PA_WALK) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); ////////////////// //GAMEPLAY STUFF// @@ -8256,18 +8351,18 @@ void P_MovePlayer(player_t *player) { player->pflags &= ~(PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY); player->secondjump = 0; - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); } if ((!(player->charability == CA_GLIDEANDCLIMB) || player->gotflag) // If you can't glide, then why the heck would you be gliding? && (player->pflags & PF_GLIDING || player->climbing)) { if (onground) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else { player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } player->pflags &= ~PF_GLIDING; player->glidetime = 0; @@ -8278,11 +8373,11 @@ void P_MovePlayer(player_t *player) && (player->pflags & PF_BOUNCING)) { if (onground) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else { player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } player->pflags &= ~PF_BOUNCING; } @@ -8399,18 +8494,18 @@ void P_MovePlayer(player_t *player) { P_ResetPlayer(player); // down, stop gliding. if (onground) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else if (player->charflags & SF_MULTIABILITY) { player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } else { player->pflags |= PF_THOKKED; player->mo->momx >>= 1; player->mo->momy >>= 1; - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); } } } @@ -8427,23 +8522,23 @@ void P_MovePlayer(player_t *player) P_ResetPlayer(player); // down, stop bouncing. player->pflags |= PF_THOKKED; if (onground) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else if (player->charflags & SF_MULTIABILITY) { player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } else { player->mo->momx >>= 1; player->mo->momy >>= 1; player->mo->momz >>= 1; - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); } } } else if (player->mo->state-states == S_PLAY_BOUNCE) - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); // If you're running fast enough, you can create splashes as you run in shallow water. if (!player->climbing @@ -8455,19 +8550,22 @@ void P_MovePlayer(player_t *player) mobjtype_t splishtype = (player->mo->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; mobj_t *water = P_SpawnMobj(player->mo->x - P_ReturnThrustX(NULL, player->mo->angle, player->mo->radius), player->mo->y - P_ReturnThrustY(NULL, player->mo->angle, player->mo->radius), ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[splishtype].height, player->mo->scale) : player->mo->watertop), splishtype); - if (player->mo->eflags & MFE_GOOWATER) - S_StartSound(water, sfx_ghit); - else if (player->mo->eflags & MFE_TOUCHLAVA) - S_StartSound(water, sfx_splash); - else - S_StartSound(water, sfx_wslap); - if (player->mo->eflags & MFE_VERTICALFLIP) + if (!P_MobjWasRemoved(water)) { - water->flags2 |= MF2_OBJECTFLIP; - water->eflags |= MFE_VERTICALFLIP; + if (player->mo->eflags & MFE_GOOWATER) + S_StartSound(water, sfx_ghit); + else if (player->mo->eflags & MFE_TOUCHLAVA) + S_StartSound(water, sfx_splash); + else + S_StartSound(water, sfx_wslap); + if (player->mo->eflags & MFE_VERTICALFLIP) + { + water->flags2 |= MF2_OBJECTFLIP; + water->eflags |= MFE_VERTICALFLIP; + } + water->destscale = player->mo->scale; + P_SetScale(water, player->mo->scale); } - water->destscale = player->mo->scale; - P_SetScale(water, player->mo->scale); } // Little water sound while touching water - just a nicety. @@ -8487,11 +8585,11 @@ void P_MovePlayer(player_t *player) || player->mo->state-states == S_PLAY_FLY_TIRED) { if (onground) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); else { player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } } player->powers[pw_tailsfly] = 0; @@ -8525,7 +8623,7 @@ void P_MovePlayer(player_t *player) if (P_MobjFlip(player->mo)*player->mo->momz < FixedMul(5*actionspd, player->mo->scale)) P_SetObjectMomZ(player->mo, actionspd/2, true); - P_SetPlayerMobjState(player->mo, player->mo->state->nextstate); + P_SetMobjState(player->mo, player->mo->state->nextstate); player->fly1--; } @@ -8553,7 +8651,7 @@ void P_MovePlayer(player_t *player) { // Tails-gets-tired Stuff if (player->panim == PA_ABILITY && player->mo->state-states != S_PLAY_FLY_TIRED) - P_SetPlayerMobjState(player->mo, S_PLAY_FLY_TIRED); + P_SetMobjState(player->mo, S_PLAY_FLY_TIRED); if (player->charability == CA_FLY && (leveltime % 10 == 0) && player->mo->state-states == S_PLAY_FLY_TIRED @@ -8670,7 +8768,7 @@ void P_MovePlayer(player_t *player) // Make sure you're not teetering when you shouldn't be. if (player->panim == PA_EDGE && (player->mo->momx || player->mo->momy || player->mo->momz)) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); // Check for teeter! if (!(player->mo->momz || player->mo->momx || player->mo->momy) && !(player->mo->eflags & MFE_GOOWATER) @@ -8736,7 +8834,7 @@ void P_MovePlayer(player_t *player) if (!atspinheight) { player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); } else if (player->mo->ceilingz - player->mo->floorz < player->mo->height) { @@ -8917,7 +9015,7 @@ static void P_DoRopeHang(player_t *player) if (player->cmd.buttons & BT_SPIN && !(player->pflags & PF_STASIS)) // Drop off of the rope { player->pflags |= (P_GetJumpFlags(player)|PF_SPINDOWN); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); P_SetTarget(&player->mo->tracer, NULL); player->powers[pw_carry] = CR_NONE; @@ -8926,7 +9024,7 @@ static void P_DoRopeHang(player_t *player) } if (player->mo->state-states != S_PLAY_RIDE) - P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); + P_SetMobjState(player->mo, S_PLAY_RIDE); // If not allowed to move, we're done here. if (!speed) @@ -8982,7 +9080,7 @@ static void P_DoRopeHang(player_t *player) if (player->mo->tracer->flags & MF_SLIDEME) { player->pflags |= P_GetJumpFlags(player); - P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); + P_SetMobjState(player->mo, S_PLAY_JUMP); } P_SetTarget(&player->mo->tracer, NULL); @@ -10893,6 +10991,8 @@ static void P_SpawnSparks(mobj_t *mo, angle_t maindir) fixed_t fm = (maindir >> ANGLETOFINESHIFT) & FINEMASK; spark = P_SpawnMobj(mo->x - b2*s + b1*c, mo->y + b2*c + b1*s, mo->z, MT_MINECARTSPARK); + if (P_MobjWasRemoved(spark)) + return; spark->momx = mo->momx + r1 + 8*FINECOSINE(fm); spark->momy = mo->momy + r2 + 8*FINESINE(fm); spark->momz = mo->momz + r3; @@ -11180,7 +11280,7 @@ static void P_MinecartThink(player_t *player) if (player->mo->state-states != S_PLAY_STND) { - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); player->mo->tics = -1; } @@ -11531,7 +11631,7 @@ void P_DoMetalJetFume(player_t *player, mobj_t *fume) if (resetinterp) R_ResetMobjInterpolationState(fume); // If dashmode is high enough, spawn a trail - if (player->normalspeed >= skins[player->skin].normalspeed*2) + if (player->normalspeed >= skins[player->skin]->normalspeed*2) { mobj_t *ghost = P_SpawnGhostMobj(fume); if (!P_MobjWasRemoved(ghost)) @@ -11792,7 +11892,7 @@ void P_PlayerThink(player_t *player) if (!total || ((4*exiting)/total) >= numneeded) { if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } else player->exiting = 3; @@ -11800,7 +11900,7 @@ void P_PlayerThink(player_t *player) else { if (server) - D_SendExitLevel(false); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } } @@ -11958,7 +12058,7 @@ void P_PlayerThink(player_t *player) { P_DoZoomTube(player); if (!(player->panim == PA_ROLL)) - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); } player->rmomx = player->rmomy = 0; // no actual momentum from your controls P_ResetScore(player); @@ -12117,7 +12217,7 @@ void P_PlayerThink(player_t *player) { statenum_t stat = player->mo->state-states; if (stat == S_PLAY_WAIT) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); else if (stat == S_PLAY_STND && player->mo->tics != -1) player->mo->tics++; } @@ -12144,7 +12244,7 @@ void P_PlayerThink(player_t *player) else if (!player->skidtime && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID) && P_AproxDistance(player->mo->momx, player->mo->momy) >= FixedMul(player->runspeed, player->mo->scale)) // modified from player->runspeed/2 'cuz the skid was just TOO frequent ngl { if (player->mo->state-states != S_PLAY_SKID) - P_SetPlayerMobjState(player->mo, S_PLAY_SKID); + P_SetMobjState(player->mo, S_PLAY_SKID); player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; if (P_IsLocalPlayer(player)) // the sound happens way more frequently now, so give co-op players' ears a brake... @@ -12194,17 +12294,20 @@ void P_PlayerThink(player_t *player) if ((player->powers[pw_super] || player->powers[pw_sneakers]) && (player->speed + abs(player->mo->momz)) > FixedMul(20*FRACUNIT,player->mo->scale)) { mobj_t *gmobj = P_SpawnGhostMobj(player->mo); - gmobj->fuse = 2; - if (gmobj->tracer) - gmobj->tracer->fuse = 2; - if (leveltime & 1) + if (!P_MobjWasRemoved(gmobj)) { - gmobj->frame &= ~FF_TRANSMASK; - gmobj->frame |= tr_trans70<fuse = 2; if (gmobj->tracer) + gmobj->tracer->fuse = 2; + if (leveltime & 1) { - gmobj->tracer->frame &= ~FF_TRANSMASK; - gmobj->tracer->frame |= tr_trans70<frame &= ~FF_TRANSMASK; + gmobj->frame |= tr_trans70<tracer) + { + gmobj->tracer->frame &= ~FF_TRANSMASK; + gmobj->tracer->frame |= tr_trans70<powers[pw_flashing]) { if (player->mo->state != &states[S_PLAY_STND]) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); + P_SetMobjState(player->mo, S_PLAY_STND); else player->mo->tics = 2; } @@ -12402,38 +12505,41 @@ void P_PlayerThink(player_t *player) { if (prevdashmode >= DASHMODE_THRESHOLD) { - player->normalspeed = skins[player->skin].normalspeed; // Reset to default if not capable of entering dash mode. - player->jumpfactor = skins[player->skin].jumpfactor; + player->normalspeed = skins[player->skin]->normalspeed; // Reset to default if not capable of entering dash mode. + player->jumpfactor = skins[player->skin]->jumpfactor; if (player->powers[pw_strong] & STR_DASH) player->powers[pw_strong] = STR_NONE; } } else if (P_IsObjectOnGround(player->mo)) // Activate dash mode if we're on the ground. { - if (player->normalspeed < skins[player->skin].normalspeed*2) // If the player normalspeed is not currently at normalspeed*2 in dash mode, add speed each tic + if (player->normalspeed < skins[player->skin]->normalspeed*2) // If the player normalspeed is not currently at normalspeed*2 in dash mode, add speed each tic player->normalspeed += FRACUNIT/5; // Enter Dash Mode smoothly. - if (player->jumpfactor < FixedMul(skins[player->skin].jumpfactor, 5*FRACUNIT/4)) // Boost jump height. + if (player->jumpfactor < FixedMul(skins[player->skin]->jumpfactor, 5*FRACUNIT/4)) // Boost jump height. player->jumpfactor += FRACUNIT/300; if ((player->charflags & SF_MACHINE) && (!(player->powers[pw_strong] == STR_METAL))) player->powers[pw_strong] = STR_METAL; } - if (player->normalspeed >= skins[player->skin].normalspeed*2) + if (player->normalspeed >= skins[player->skin]->normalspeed*2) { mobj_t *ghost = P_SpawnGhostMobj(player->mo); // Spawns afterimages - ghost->fuse = 2; // Makes the images fade quickly - if (ghost->tracer && !P_MobjWasRemoved(ghost->tracer)) - ghost->tracer->fuse = ghost->fuse; + if (!P_MobjWasRemoved(ghost)) + { + ghost->fuse = 2; // Makes the images fade quickly + if (ghost->tracer && !P_MobjWasRemoved(ghost->tracer)) + ghost->tracer->fuse = ghost->fuse; + } } } else if (dashmode) { if (dashmode >= DASHMODE_THRESHOLD) // catch getting the flag! { - player->normalspeed = skins[player->skin].normalspeed; - player->jumpfactor = skins[player->skin].jumpfactor; + player->normalspeed = skins[player->skin]->normalspeed; + player->jumpfactor = skins[player->skin]->jumpfactor; S_StartSound(player->mo, sfx_kc65); if (player->powers[pw_strong] & STR_DASH) player->powers[pw_strong] = STR_NONE; @@ -12682,7 +12788,7 @@ void P_PlayerAfterThink(player_t *player) S_StartSound(NULL, sfx_wepchg); if ((player->pflags & PF_SLIDING) && ((player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE)) != PF_JUMPED)) - P_SetPlayerMobjState(player->mo, player->mo->info->painstate); + P_SetMobjState(player->mo, player->mo->info->painstate); /* if (player->powers[pw_carry] == CR_NONE && player->mo->tracer && !player->homing) P_SetTarget(&player->mo->tracer, NULL); @@ -12743,7 +12849,7 @@ void P_PlayerAfterThink(player_t *player) if (player->powers[pw_carry] == CR_PLAYER) { if (player->mo->state-states != S_PLAY_RIDE) - P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); + P_SetMobjState(player->mo, S_PLAY_RIDE); if (tails->player && (tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) tails->player->powers[pw_tailsfly] = 0; } @@ -12768,7 +12874,7 @@ void P_PlayerAfterThink(player_t *player) player->mo->momx = player->mo->momy = player->mo->momz = 0; P_SetThingPosition(player->mo); if (player->mo->state-states != S_PLAY_RIDE) - P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); + P_SetMobjState(player->mo, S_PLAY_RIDE); // Controllable missile if (item->type == MT_BLACKEGGMAN_MISSILE) @@ -12889,7 +12995,7 @@ void P_PlayerAfterThink(player_t *player) if (player->panim == PA_IDLE && (mo->momx || mo->momy)) { - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + P_SetMobjState(player->mo, S_PLAY_WALK); } if (player->panim == PA_WALK && mo->tics > walktics) @@ -12945,7 +13051,7 @@ void P_PlayerAfterThink(player_t *player) P_KillMobj(ptera, player->mo, player->mo, 0); P_SetObjectMomZ(player->mo, 12*FRACUNIT, false); player->pflags |= PF_APPLYAUTOBRAKE|PF_JUMPED|PF_THOKKED; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + P_SetMobjState(player->mo, S_PLAY_ROLL); break; } @@ -12966,7 +13072,7 @@ void P_PlayerAfterThink(player_t *player) ptera->cvmem >>= 1; if (player->mo->state-states != S_PLAY_FALL) - P_SetPlayerMobjState(player->mo, S_PLAY_FALL); + P_SetMobjState(player->mo, S_PLAY_FALL); break; dropoff: @@ -13026,15 +13132,18 @@ void P_PlayerAfterThink(player_t *player) if (!player->followmobj || P_MobjWasRemoved(player->followmobj)) { P_SetTarget(&player->followmobj, P_SpawnMobjFromMobj(player->mo, 0, 0, 0, player->followitem)); - P_SetTarget(&player->followmobj->tracer, player->mo); - switch (player->followmobj->type) + if (!P_MobjWasRemoved(player->followmobj)) { - case MT_METALJETFUME: - player->followmobj->colorized = true; - break; - default: - player->followmobj->flags2 |= MF2_LINKDRAW; - break; + P_SetTarget(&player->followmobj->tracer, player->mo); + switch (player->followmobj->type) + { + case MT_METALJETFUME: + player->followmobj->colorized = true; + break; + default: + player->followmobj->flags2 |= MF2_LINKDRAW; + break; + } } } diff --git a/src/r_bsp.c b/src/r_bsp.c index 3eeff5e9a..573a871d6 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -36,6 +36,11 @@ drawseg_t *curdrawsegs = NULL; /**< This is used to handle multiple lists for ma drawseg_t *drawsegs = NULL; drawseg_t *ds_p = NULL; +boolean bothceilingssky = false; // turned on if both back and front ceilings are sky +boolean bothfloorssky = false; // likewise, but for floors + +boolean horizonline = false; + // indicates doors closed wrt automap bugfix: INT32 doorclosed; @@ -188,6 +193,8 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, tempsec->floorpic = s->floorpic; tempsec->floorxoffset = s->floorxoffset; tempsec->flooryoffset = s->flooryoffset; + tempsec->floorxscale = s->floorxscale; + tempsec->flooryscale = s->flooryscale; tempsec->floorangle = s->floorangle; if (underwater) @@ -198,6 +205,8 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, tempsec->ceilingpic = tempsec->floorpic; tempsec->ceilingxoffset = tempsec->floorxoffset; tempsec->ceilingyoffset = tempsec->flooryoffset; + tempsec->ceilingxscale = tempsec->floorxscale; + tempsec->ceilingyscale = tempsec->flooryscale; tempsec->ceilingangle = tempsec->floorangle; } else @@ -205,6 +214,8 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, tempsec->ceilingpic = s->ceilingpic; tempsec->ceilingxoffset = s->ceilingxoffset; tempsec->ceilingyoffset = s->ceilingyoffset; + tempsec->ceilingxscale = s->ceilingxscale; + tempsec->ceilingyscale = s->ceilingyscale; tempsec->ceilingangle = s->ceilingangle; } } @@ -228,6 +239,8 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, tempsec->floorpic = tempsec->ceilingpic = s->ceilingpic; tempsec->floorxoffset = tempsec->ceilingxoffset = s->ceilingxoffset; tempsec->flooryoffset = tempsec->ceilingyoffset = s->ceilingyoffset; + tempsec->floorxscale = tempsec->ceilingxscale = s->ceilingxscale; + tempsec->flooryscale = tempsec->ceilingyscale = s->ceilingyscale; tempsec->floorangle = tempsec->ceilingangle = s->ceilingangle; if (s->floorpic == skyflatnum) // SKYFIX? @@ -236,6 +249,8 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, tempsec->floorpic = tempsec->ceilingpic; tempsec->floorxoffset = tempsec->ceilingxoffset; tempsec->flooryoffset = tempsec->ceilingyoffset; + tempsec->floorxscale = tempsec->ceilingxscale; + tempsec->flooryscale = tempsec->ceilingyscale; tempsec->floorangle = tempsec->ceilingangle; } else @@ -244,6 +259,8 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, tempsec->floorpic = s->floorpic; tempsec->floorxoffset = s->floorxoffset; tempsec->flooryoffset = s->flooryoffset; + tempsec->floorxscale = s->floorxscale; + tempsec->flooryscale = s->flooryscale; tempsec->floorangle = s->floorangle; } @@ -265,6 +282,11 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back) { + if (P_SectorHasPortal(front) && !P_SectorHasPortal(back)) + return false; + else if (!P_SectorHasPortal(front) && P_SectorHasPortal(back)) + return false; + return ( !line->polyseg && back->ceilingpic == front->ceilingpic @@ -273,12 +295,16 @@ boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back) && back->c_slope == front->c_slope && back->lightlevel == front->lightlevel && !line->sidedef->midtexture - // Check offsets too! + // Check offsets and scale too! && back->floorxoffset == front->floorxoffset && back->flooryoffset == front->flooryoffset + && back->floorxscale == front->floorxscale + && back->flooryscale == front->flooryscale && back->floorangle == front->floorangle && back->ceilingxoffset == front->ceilingxoffset && back->ceilingyoffset == front->ceilingyoffset + && back->ceilingxscale == front->ceilingxscale + && back->ceilingyscale == front->ceilingyscale && back->ceilingangle == front->ceilingangle // Consider altered lighting. && back->floorlightlevel == front->floorlightlevel @@ -302,7 +328,6 @@ static void R_AddLine(seg_t *line) INT32 x1, x2; angle_t angle1, angle2, span, tspan; static sector_t tempsec; - boolean bothceilingssky = false, bothfloorssky = false; portalline = false; @@ -360,6 +385,8 @@ static void R_AddLine(seg_t *line) return; backsector = line->backsector; + horizonline = line->linedef->special == SPECIAL_HORIZON_LINE; + bothceilingssky = bothfloorssky = false; // Portal line if (line->linedef->special == 40 && line->side == 0) @@ -384,6 +411,15 @@ static void R_AddLine(seg_t *line) } } } + // Transferred portal + else if (line->linedef->secportal != UINT32_MAX && line->side == 0) + { + if (portalrender < cv_maxportals.value) + { + Portal_AddTransferred(line->linedef->secportal, x1, x2); + goto clipsolid; + } + } // Single sided line? if (!backsector) @@ -393,9 +429,15 @@ static void R_AddLine(seg_t *line) doorclosed = 0; - if (backsector->ceilingpic == skyflatnum && frontsector->ceilingpic == skyflatnum) + // hack to allow height changes in outdoor areas + // This is what gets rid of the upper textures if there should be sky + if (backsector->ceilingpic == skyflatnum && frontsector->ceilingpic == skyflatnum + && !(P_SectorHasCeilingPortal(backsector) || P_SectorHasCeilingPortal(frontsector))) bothceilingssky = true; - if (backsector->floorpic == skyflatnum && frontsector->floorpic == skyflatnum) + + // likewise, but for floors and upper textures + if (backsector->floorpic == skyflatnum && frontsector->floorpic == skyflatnum + && !(P_SectorHasFloorPortal(backsector) || P_SectorHasFloorPortal(frontsector))) bothfloorssky = true; if (bothceilingssky && bothfloorssky) // everything's sky? let's save us a bit of time then @@ -773,63 +815,32 @@ static void R_Subsector(size_t num) floorcenterz = P_GetSectorFloorZAt (frontsector, frontsector->soundorg.x, frontsector->soundorg.y); ceilingcenterz = P_GetSectorCeilingZAt(frontsector, frontsector->soundorg.x, frontsector->soundorg.y); - // Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps. - if (frontsector->ffloors) - { - boolean anyMoved = frontsector->moved; - - if (anyMoved == false) - { - for (rover = frontsector->ffloors; rover; rover = rover->next) - { - sector_t *controlSec = §ors[rover->secnum]; - - if (controlSec->moved == true) - { - anyMoved = true; - break; - } - } - } - - if (anyMoved == true) - { - frontsector->numlights = sub->sector->numlights = 0; - R_Prep3DFloors(frontsector); - sub->sector->lightlist = frontsector->lightlist; - sub->sector->numlights = frontsector->numlights; - sub->sector->moved = frontsector->moved = false; - } - - light = R_GetPlaneLight(frontsector, floorcenterz, false); - if (frontsector->floorlightsec == -1 && !frontsector->floorlightabsolute) - floorlightlevel = max(0, min(255, *frontsector->lightlist[light].lightlevel + frontsector->floorlightlevel)); - floorcolormap = *frontsector->lightlist[light].extra_colormap; - light = R_GetPlaneLight(frontsector, ceilingcenterz, false); - if (frontsector->ceilinglightsec == -1 && !frontsector->ceilinglightabsolute) - ceilinglightlevel = max(0, min(255, *frontsector->lightlist[light].lightlevel + frontsector->ceilinglightlevel)); - ceilingcolormap = *frontsector->lightlist[light].extra_colormap; - } + R_CheckSectorLightLists(sub->sector, frontsector, &floorlightlevel, &ceilinglightlevel, &floorcolormap, &ceilingcolormap); sub->sector->extra_colormap = frontsector->extra_colormap; if (P_GetSectorFloorZAt(frontsector, viewx, viewy) < viewz || frontsector->floorpic == skyflatnum + || P_SectorHasFloorPortal(frontsector) || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum)) { - floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel, - frontsector->floorxoffset, frontsector->flooryoffset, frontsector->floorangle, floorcolormap, NULL, NULL, frontsector->f_slope); + floorplane = R_FindPlane(frontsector, frontsector->floorheight, frontsector->floorpic, floorlightlevel, + frontsector->floorxoffset, frontsector->flooryoffset, + frontsector->floorxscale, frontsector->flooryscale, frontsector->floorangle, + floorcolormap, NULL, NULL, frontsector->f_slope, P_SectorGetFloorPortal(frontsector)); } else floorplane = NULL; if (P_GetSectorCeilingZAt(frontsector, viewx, viewy) > viewz || frontsector->ceilingpic == skyflatnum + || P_SectorHasCeilingPortal(frontsector) || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum)) { - ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic, - ceilinglightlevel, frontsector->ceilingxoffset, frontsector->ceilingyoffset, frontsector->ceilingangle, - ceilingcolormap, NULL, NULL, frontsector->c_slope); + ceilingplane = R_FindPlane(frontsector, frontsector->ceilingheight, frontsector->ceilingpic, ceilinglightlevel, + frontsector->ceilingxoffset, frontsector->ceilingyoffset, + frontsector->ceilingxscale, frontsector->ceilingyscale, frontsector->ceilingangle, + ceilingcolormap, NULL, NULL, frontsector->c_slope, P_SectorGetCeilingPortal(frontsector)); } else ceilingplane = NULL; @@ -870,9 +881,10 @@ static void R_Subsector(size_t num) light = R_GetPlaneLight(frontsector, planecenterz, viewz < heightcheck); - ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic, - *frontsector->lightlist[light].lightlevel, *rover->bottomxoffs, - *rover->bottomyoffs, *rover->bottomangle, *frontsector->lightlist[light].extra_colormap, rover, NULL, *rover->b_slope); + ffloor[numffloors].plane = R_FindPlane(rover->master->frontsector, *rover->bottomheight, *rover->bottompic, + *frontsector->lightlist[light].lightlevel, *rover->bottomxoffs, *rover->bottomyoffs, + *rover->bottomxscale, *rover->bottomyscale, *rover->bottomangle, + *frontsector->lightlist[light].extra_colormap, rover, NULL, *rover->b_slope, NULL); ffloor[numffloors].slope = *rover->b_slope; @@ -899,9 +911,10 @@ static void R_Subsector(size_t num) { light = R_GetPlaneLight(frontsector, planecenterz, viewz < heightcheck); - ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic, - *frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle, - *frontsector->lightlist[light].extra_colormap, rover, NULL, *rover->t_slope); + ffloor[numffloors].plane = R_FindPlane(rover->master->frontsector, *rover->topheight, *rover->toppic, + *frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, + *rover->topxscale, *rover->topyscale, *rover->topangle, + *frontsector->lightlist[light].extra_colormap, rover, NULL, *rover->t_slope, NULL); ffloor[numffloors].slope = *rover->t_slope; @@ -941,16 +954,17 @@ static void R_Subsector(size_t num) && (viewz < polysec->floorheight)) { light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight); - ffloor[numffloors].plane = R_FindPlane(polysec->floorheight, polysec->floorpic, - (light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->floorxoffset, polysec->flooryoffset, + ffloor[numffloors].plane = R_FindPlane(polysec, polysec->floorheight, polysec->floorpic, + (light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), + polysec->floorxoffset, polysec->flooryoffset, + polysec->floorxscale, polysec->flooryscale, polysec->floorangle-po->angle, (light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po, - NULL); // will ffloors be slopable eventually? + NULL, NULL); ffloor[numffloors].height = polysec->floorheight; ffloor[numffloors].polyobj = po; ffloor[numffloors].slope = NULL; - //ffloor[numffloors].ffloor = rover; po->visplane = ffloor[numffloors].plane; numffloors++; } @@ -965,15 +979,17 @@ static void R_Subsector(size_t num) && (viewz > polysec->ceilingheight)) { light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight); - ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic, - (light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), polysec->ceilingxoffset, polysec->ceilingyoffset, polysec->ceilingangle-po->angle, + ffloor[numffloors].plane = R_FindPlane(polysec, polysec->ceilingheight, polysec->ceilingpic, + (light == -1 ? frontsector->lightlevel : *frontsector->lightlist[light].lightlevel), + polysec->ceilingxoffset, polysec->ceilingyoffset, + polysec->ceilingxscale, polysec->ceilingyscale, + polysec->ceilingangle-po->angle, (light == -1 ? frontsector->extra_colormap : *frontsector->lightlist[light].extra_colormap), NULL, po, - NULL); // will ffloors be slopable eventually? + NULL, NULL); ffloor[numffloors].polyobj = po; ffloor[numffloors].height = polysec->ceilingheight; ffloor[numffloors].slope = NULL; - //ffloor[numffloors].ffloor = rover; po->visplane = ffloor[numffloors].plane; numffloors++; } @@ -982,18 +998,18 @@ static void R_Subsector(size_t num) } } - // killough 9/18/98: Fix underwater slowdown, by passing real sector - // instead of fake one. Improve sprite lighting by basing sprite - // lightlevels on floor & ceiling lightlevels in the surrounding area. - // - // 10/98 killough: - // - // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!! - // That is part of the 242 effect!!! If you simply pass sub->sector to - // the old code you will not get correct lighting for underwater sprites!!! - // Either you must pass the fake sector and handle validcount here, on the - // real sector, or you must account for the lighting in some other way, - // like passing it as an argument. + // killough 9/18/98: Fix underwater slowdown, by passing real sector + // instead of fake one. Improve sprite lighting by basing sprite + // lightlevels on floor & ceiling lightlevels in the surrounding area. + // + // 10/98 killough: + // + // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!! + // That is part of the 242 effect!!! If you simply pass sub->sector to + // the old code you will not get correct lighting for underwater sprites!!! + // Either you must pass the fake sector and handle validcount here, on the + // real sector, or you must account for the lighting in some other way, + // like passing it as an argument. R_AddSprites(sub->sector, (floorlightlevel+ceilinglightlevel)/2); firstseg = NULL; @@ -1004,7 +1020,6 @@ static void R_Subsector(size_t num) while (count--) { -// CONS_Debug(DBG_GAMELOGIC, "Adding normal line %d...(%d)\n", line->linedef-lines, leveltime); if (!line->glseg && !line->polyseg) // ignore segs that belong to polyobjects R_AddLine(line); line++; @@ -1012,6 +1027,51 @@ static void R_Subsector(size_t num) } } +void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap) +{ + // Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps. + if (fakeflat->ffloors) + { + fixed_t floorcenterz = P_GetSectorFloorZAt (fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y); + fixed_t ceilingcenterz = P_GetSectorCeilingZAt(fakeflat, fakeflat->soundorg.x, fakeflat->soundorg.y); + + boolean anyMoved = fakeflat->moved; + + if (anyMoved == false) + { + for (ffloor_t *rover = fakeflat->ffloors; rover; rover = rover->next) + { + sector_t *controlSec = §ors[rover->secnum]; + + if (controlSec->moved == true) + { + anyMoved = true; + break; + } + } + } + + if (anyMoved == true) + { + fakeflat->numlights = sector->numlights = 0; + R_Prep3DFloors(fakeflat); + sector->lightlist = fakeflat->lightlist; + sector->numlights = fakeflat->numlights; + sector->moved = fakeflat->moved = false; + } + + INT32 light = R_GetPlaneLight(fakeflat, floorcenterz, false); + if (fakeflat->floorlightsec == -1 && !fakeflat->floorlightabsolute) + *floorlightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->floorlightlevel)); + *floorcolormap = *fakeflat->lightlist[light].extra_colormap; + + light = R_GetPlaneLight(fakeflat, ceilingcenterz, false); + if (fakeflat->ceilinglightsec == -1 && !fakeflat->ceilinglightabsolute) + *ceilinglightlevel = max(0, min(255, *fakeflat->lightlist[light].lightlevel + fakeflat->ceilinglightlevel)); + *ceilingcolormap = *fakeflat->lightlist[light].extra_colormap; + } +} + // // R_Prep3DFloors // @@ -1197,3 +1257,59 @@ void R_RenderBSPNode(INT32 bspnum) R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); } + +void R_RenderPortalHorizonLine(sector_t *sector) +{ + INT32 floorlightlevel, ceilinglightlevel; + static sector_t tempsec; // Deep water hack + extracolormap_t *floorcolormap; + extracolormap_t *ceilingcolormap; + + frontsector = sector; + backsector = NULL; + + // Deep water/fake ceiling effect. + frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, false); + + floorcolormap = ceilingcolormap = frontsector->extra_colormap; + + R_CheckSectorLightLists(sector, frontsector, &floorlightlevel, &ceilinglightlevel, &floorcolormap, &ceilingcolormap); + + sector->extra_colormap = frontsector->extra_colormap; + + if (P_GetSectorFloorZAt(frontsector, viewx, viewy) < viewz + || frontsector->floorpic == skyflatnum + || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].ceilingpic == skyflatnum)) + { + floorplane = R_FindPlane(frontsector, frontsector->floorheight, frontsector->floorpic, floorlightlevel, + frontsector->floorxoffset, frontsector->flooryoffset, frontsector->floorxscale, frontsector->flooryscale, + frontsector->floorangle, floorcolormap, NULL, NULL, NULL, NULL); + } + else + floorplane = NULL; + + if (P_GetSectorCeilingZAt(frontsector, viewx, viewy) > viewz + || frontsector->ceilingpic == skyflatnum + || (frontsector->heightsec != -1 && sectors[frontsector->heightsec].floorpic == skyflatnum)) + { + ceilingplane = R_FindPlane(frontsector, frontsector->ceilingheight, frontsector->ceilingpic, + ceilinglightlevel, frontsector->ceilingxoffset, frontsector->ceilingxscale, frontsector->ceilingyscale, + frontsector->ceilingyoffset, frontsector->ceilingangle, + ceilingcolormap, NULL, NULL, NULL, NULL); + } + else + ceilingplane = NULL; + + numffloors = 0; + portalline = false; + doorclosed = 0; + bothceilingssky = bothfloorssky = false; + horizonline = true; + + firstseg = NULL; + curline = &segs[0]; + + R_ClipWallSegment(portalclipstart, portalclipend, true); + + curline = NULL; +} diff --git a/src/r_bsp.h b/src/r_bsp.h index adb0fc67d..a310cc408 100644 --- a/src/r_bsp.h +++ b/src/r_bsp.h @@ -25,14 +25,17 @@ extern sector_t *frontsector; extern sector_t *backsector; extern boolean portalline; // is curline a portal seg? -// drawsegs are allocated on the fly... see r_segs.c - extern INT32 checkcoord[12][4]; extern drawseg_t *curdrawsegs; extern drawseg_t *drawsegs; extern drawseg_t *ds_p; +extern boolean bothceilingssky; +extern boolean bothfloorssky; + +extern boolean horizonline; + extern INT32 doorclosed; extern UINT8 *solidcol; @@ -43,6 +46,7 @@ void R_PortalClearClipSegs(INT32 start, INT32 end); void R_AllocClipSegMemory(void); void R_ClearDrawSegs(void); void R_RenderBSPNode(INT32 bspnum); +void R_RenderPortalHorizonLine(sector_t *sector); void R_SortPolyObjects(subsector_t *sub); @@ -56,4 +60,5 @@ boolean R_IsEmptyLine(seg_t *line, sector_t *front, sector_t *back); INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside); void R_Prep3DFloors(sector_t *sector); +void R_CheckSectorLightLists(sector_t *sector, sector_t *fakeflat, INT32 *floorlightlevel, INT32 *ceilinglightlevel, extracolormap_t **floorcolormap, extracolormap_t **ceilingcolormap); #endif diff --git a/src/r_defs.h b/src/r_defs.h index a8f3d9cb1..7aa546b1e 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -200,6 +200,34 @@ typedef enum BT_STRONG, } busttype_e; +typedef enum +{ + SECPORTAL_LINE, // Works similar to a line portal + SECPORTAL_SKYBOX, // Uses the skybox object as the reference view + SECPORTAL_PLANE, // Eternity Engine's plane portal type + SECPORTAL_HORIZON, // Eternity Engine's horizon portal type + SECPORTAL_OBJECT, // Uses an object as the reference view + SECPORTAL_FLOOR, // Uses a sector as the reference view; the view height is aligned with the sector's floor + SECPORTAL_CEILING, // Uses a sector as the reference view; the view height is aligned with the sector's ceiling + SECPORTAL_NONE = 0xFF +} secportaltype_e; + +typedef struct sectorportal_s +{ + secportaltype_e type; + union { + struct { + struct line_s *start; + struct line_s *dest; + } line; + struct sector_s *sector; + struct mobj_s *mobj; + }; + struct { + fixed_t x, y; + } origin; +} sectorportal_t; + typedef struct ffloor_s { fixed_t *topheight; @@ -207,12 +235,16 @@ typedef struct ffloor_s INT16 *toplightlevel; fixed_t *topxoffs; fixed_t *topyoffs; + fixed_t *topxscale; + fixed_t *topyscale; angle_t *topangle; fixed_t *bottomheight; INT32 *bottompic; fixed_t *bottomxoffs; fixed_t *bottomyoffs; + fixed_t *bottomxscale; + fixed_t *bottomyscale; angle_t *bottomangle; // Pointers to pointers. Yup. @@ -418,6 +450,10 @@ typedef struct sector_s fixed_t floorxoffset, flooryoffset; fixed_t ceilingxoffset, ceilingyoffset; + // floor and ceiling texture scale + fixed_t floorxscale, flooryscale; + fixed_t ceilingxscale, ceilingyscale; + // flat angle angle_t floorangle; angle_t ceilingangle; @@ -486,6 +522,10 @@ typedef struct sector_s // colormap structure extracolormap_t *spawn_extra_colormap; + + // portals + UINT32 portal_floor; + UINT32 portal_ceiling; } sector_t; // @@ -499,7 +539,9 @@ typedef enum ST_NEGATIVE } slopetype_t; -#define HORIZONSPECIAL 41 +#define SPECIAL_HORIZON_LINE 41 + +#define SPECIAL_SECTOR_SETPORTAL 6 #define NUMLINEARGS 10 #define NUMLINESTRINGARGS 2 @@ -542,6 +584,8 @@ typedef struct line_s polyobj_t *polyobj; // Belongs to a polyobject? INT16 callcount; // no. of calls left before triggering, for the "X calls" linedef specials, defaults to 0 + + UINT32 secportal; // transferred sector portal } line_t; typedef struct @@ -553,8 +597,11 @@ typedef struct fixed_t rowoffset; // per-texture offsets for UDMF - fixed_t offsetx_top, offsetx_mid, offsetx_bot; - fixed_t offsety_top, offsety_mid, offsety_bot; + fixed_t offsetx_top, offsetx_mid, offsetx_bottom; + fixed_t offsety_top, offsety_mid, offsety_bottom; + + fixed_t scalex_top, scalex_mid, scalex_bottom; + fixed_t scaley_top, scaley_mid, scaley_bottom; // Texture indices. // We do not maintain names here. @@ -748,10 +795,13 @@ typedef struct drawseg_s fixed_t bsilheight; // do not clip sprites above this fixed_t tsilheight; // do not clip sprites below this + fixed_t offsetx; + // Pointers to lists for sprite clipping, all three adjusted so [x1] is first value. INT16 *sprtopclip; INT16 *sprbottomclip; fixed_t *maskedtexturecol; + fixed_t *invscale; struct visplane_s *ffloorplanes[MAXFFLOORS]; INT32 numffloorplanes; diff --git a/src/r_draw.c b/src/r_draw.c index 324727681..0e100735c 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -88,14 +88,13 @@ fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; INT32 ds_waterofs, ds_bgofs; UINT16 ds_flatwidth, ds_flatheight; -boolean ds_powersoftwo, ds_solidcolor; +boolean ds_powersoftwo, ds_solidcolor, ds_fog; UINT8 *ds_source; // points to the start of a flat UINT8 *ds_transmap; // one of the translucency tables // Vectors for Software's tilted slope drawers -floatv3_t *ds_su, *ds_sv, *ds_sz; -floatv3_t *ds_sup, *ds_svp, *ds_szp; +floatv3_t ds_su, ds_sv, ds_sz, ds_slopelight; float focallengthf, zeroheight; /** \brief Variable flat sizes @@ -107,52 +106,53 @@ UINT32 nflatxshift, nflatyshift, nflatshiftup, nflatmask; static INT32 *tiltlighting; // ========================================================================= -// TRANSLATION COLORMAP CODE +// TRANSLATION COLORMAP CODE // ========================================================================= -#define DEFAULT_TT_CACHE_INDEX MAXSKINS -#define BOSS_TT_CACHE_INDEX (MAXSKINS + 1) -#define METALSONIC_TT_CACHE_INDEX (MAXSKINS + 2) -#define ALLWHITE_TT_CACHE_INDEX (MAXSKINS + 3) -#define RAINBOW_TT_CACHE_INDEX (MAXSKINS + 4) -#define BLINK_TT_CACHE_INDEX (MAXSKINS + 5) -#define DASHMODE_TT_CACHE_INDEX (MAXSKINS + 6) - -static UINT8 **translationtablecache[MAXSKINS + 7] = {NULL}; -UINT8 skincolor_modified[MAXSKINCOLORS]; - -static INT32 SkinToCacheIndex(INT32 skinnum) +enum { - switch (skinnum) + DEFAULT_TT_CACHE_INDEX, + BOSS_TT_CACHE_INDEX, + METALSONIC_TT_CACHE_INDEX, + ALLWHITE_TT_CACHE_INDEX, + RAINBOW_TT_CACHE_INDEX, + BLINK_TT_CACHE_INDEX, + DASHMODE_TT_CACHE_INDEX, + + TT_CACHE_SIZE +}; + +static UINT8 **translationtablecache[TT_CACHE_SIZE] = {NULL}; +static UINT8 **skintranslationcache[NUM_PALETTE_ENTRIES] = {NULL}; + +boolean skincolor_modified[MAXSKINCOLORS]; + +static INT32 TranslationToCacheIndex(INT32 translation) +{ + switch (translation) { - case TC_DEFAULT: return DEFAULT_TT_CACHE_INDEX; case TC_BOSS: return BOSS_TT_CACHE_INDEX; case TC_METALSONIC: return METALSONIC_TT_CACHE_INDEX; case TC_ALLWHITE: return ALLWHITE_TT_CACHE_INDEX; case TC_RAINBOW: return RAINBOW_TT_CACHE_INDEX; case TC_BLINK: return BLINK_TT_CACHE_INDEX; case TC_DASHMODE: return DASHMODE_TT_CACHE_INDEX; - default: break; + default: return DEFAULT_TT_CACHE_INDEX; } - - return skinnum; } -static INT32 CacheIndexToSkin(INT32 ttc) +static INT32 CacheIndexToTranslation(INT32 index) { - switch (ttc) + switch (index) { - case DEFAULT_TT_CACHE_INDEX: return TC_DEFAULT; case BOSS_TT_CACHE_INDEX: return TC_BOSS; case METALSONIC_TT_CACHE_INDEX: return TC_METALSONIC; case ALLWHITE_TT_CACHE_INDEX: return TC_ALLWHITE; case RAINBOW_TT_CACHE_INDEX: return TC_RAINBOW; case BLINK_TT_CACHE_INDEX: return TC_BLINK; case DASHMODE_TT_CACHE_INDEX: return TC_DASHMODE; - default: break; + default: return TC_DEFAULT; } - - return ttc; } CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; @@ -382,18 +382,18 @@ static void R_RainbowColormap(UINT8 *dest_colormap, UINT16 skincolor) RGBA_t color; UINT8 brightness; INT32 j; - UINT8 colorbrightnesses[16]; + UINT8 colorbrightnesses[COLORRAMPSIZE]; UINT16 brightdif; INT32 temp; // first generate the brightness of all the colours of that skincolour - for (i = 0; i < 16; i++) + for (i = 0; i < COLORRAMPSIZE; i++) { color = V_GetColor(skincolors[skincolor].ramp[i]); SETBRIGHTNESS(colorbrightnesses[i], color.s.red, color.s.green, color.s.blue); } - // next, for every colour in the palette, choose the transcolor that has the closest brightness + // next, for every colour in the palette, choose the translated colour that has the closest brightness for (i = 0; i < NUM_PALETTE_ENTRIES; i++) { if (i == 0 || i == 31) // pure black and pure white don't change @@ -404,7 +404,7 @@ static void R_RainbowColormap(UINT8 *dest_colormap, UINT16 skincolor) color = V_GetColor(i); SETBRIGHTNESS(brightness, color.s.red, color.s.green, color.s.blue); brightdif = 256; - for (j = 0; j < 16; j++) + for (j = 0; j < COLORRAMPSIZE; j++) { temp = abs((INT16)brightness - (INT16)colorbrightnesses[j]); if (temp < brightdif) @@ -420,28 +420,29 @@ static void R_RainbowColormap(UINT8 *dest_colormap, UINT16 skincolor) /** \brief Generates a translation colormap. - \param dest_colormap colormap to populate - \param skinnum skin number, or a translation mode - \param color translation color + \param dest_colormap colormap to populate + \param translation translation mode + \param color translation color + \param starttranscolor starting point of the translation \return void */ -static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, UINT16 color) +static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 translation, UINT16 color, INT32 starttranscolor) { - INT32 i, starttranscolor, skinramplength; + INT32 i, skinramplength; // Handle a couple of simple special cases - if (skinnum < TC_DEFAULT) + if (translation < TC_DEFAULT) { - switch (skinnum) + switch (translation) { case TC_ALLWHITE: memset(dest_colormap, 0, NUM_PALETTE_ENTRIES * sizeof(UINT8)); return; case TC_RAINBOW: if (color >= numskincolors) - I_Error("Invalid skin color #%hu.", (UINT16)color); - if (color != SKINCOLOR_NONE) + I_Error("Invalid skin color #%hu", (UINT16)color); + else if (color != SKINCOLOR_NONE) { R_RainbowColormap(dest_colormap, color); return; @@ -449,8 +450,8 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U break; case TC_BLINK: if (color >= numskincolors) - I_Error("Invalid skin color #%hu.", (UINT16)color); - if (color != SKINCOLOR_NONE) + I_Error("Invalid skin color #%hu", (UINT16)color); + else if (color != SKINCOLOR_NONE) { memset(dest_colormap, skincolors[color].ramp[3], NUM_PALETTE_ENTRIES * sizeof(UINT8)); return; @@ -464,26 +465,28 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U dest_colormap[i] = (UINT8)i; // White! - if (skinnum == TC_BOSS) + if (translation == TC_BOSS) { UINT8 *originalColormap = R_GetTranslationColormap(TC_DEFAULT, (skincolornum_t)color, GTC_CACHE); - for (i = 0; i < 16; i++) + if (starttranscolor >= NUM_PALETTE_ENTRIES) + I_Error("Invalid startcolor #%d", starttranscolor); + for (i = 0; i < COLORRAMPSIZE; i++) { - dest_colormap[DEFAULT_STARTTRANSCOLOR + i] = originalColormap[DEFAULT_STARTTRANSCOLOR + i]; + dest_colormap[starttranscolor + i] = originalColormap[starttranscolor + i]; dest_colormap[31-i] = i; } } - else if (skinnum == TC_METALSONIC) + else if (translation == TC_METALSONIC) { for (i = 0; i < 6; i++) { dest_colormap[skincolors[SKINCOLOR_BLUE].ramp[12-i]] = skincolors[SKINCOLOR_BLUE].ramp[i]; } dest_colormap[159] = dest_colormap[253] = dest_colormap[254] = 0; - for (i = 0; i < 16; i++) + for (i = 0; i < COLORRAMPSIZE; i++) dest_colormap[96+i] = dest_colormap[skincolors[SKINCOLOR_COBALT].ramp[i]]; } - else if (skinnum == TC_DASHMODE) // This is a long one, because MotorRoach basically hand-picked the indices + else if (translation == TC_DASHMODE) // This is a long one, because MotorRoach basically hand-picked the indices { // greens -> ketchups dest_colormap[96] = dest_colormap[97] = 48; @@ -527,26 +530,21 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U } if (color >= numskincolors) - I_Error("Invalid skin color #%hu.", (UINT16)color); - - if (skinnum < 0 && skinnum > TC_DEFAULT) - I_Error("Invalid translation colormap index %d.", skinnum); - - starttranscolor = (skinnum != TC_DEFAULT) ? skins[skinnum].starttranscolor : DEFAULT_STARTTRANSCOLOR; + I_Error("Invalid skin color #%hu", (UINT16)color); if (starttranscolor >= NUM_PALETTE_ENTRIES) - I_Error("Invalid startcolor #%d.", starttranscolor); + I_Error("Invalid startcolor #%d", starttranscolor); // Fill in the entries of the palette that are fixed for (i = 0; i < starttranscolor; i++) dest_colormap[i] = (UINT8)i; - i = starttranscolor + 16; + i = starttranscolor + COLORRAMPSIZE; if (i < NUM_PALETTE_ENTRIES) { for (i = (UINT8)i; i < NUM_PALETTE_ENTRIES; i++) dest_colormap[i] = (UINT8)i; - skinramplength = 16; + skinramplength = COLORRAMPSIZE; } else skinramplength = i - NUM_PALETTE_ENTRIES; // shouldn't this be NUM_PALETTE_ENTRIES - starttranscolor? @@ -559,7 +557,7 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U /** \brief Retrieves a translation colormap from the cache. - \param skinnum number of skin, TC_DEFAULT or TC_BOSS + \param skinnum number of skin, or translation modes \param color translation color \param flags set GTC_CACHE to use the cache @@ -567,25 +565,47 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U */ UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags) { - UINT8* ret; - INT32 skintableindex = SkinToCacheIndex(skinnum); // Adjust if we want the default colormap - INT32 i; + UINT8 ***cache = NULL; + INT32 index, starttranscolor; + UINT8 *ret; + + // Adjust if we want the default colormap + if (skinnum >= numskins) + I_Error("Invalid skin number %d", skinnum); + else if (skinnum >= 0) + { + cache = skintranslationcache; + starttranscolor = index = skins[skinnum]->starttranscolor; + } + else if (skinnum <= TC_DEFAULT) + { + cache = translationtablecache; + starttranscolor = DEFAULT_STARTTRANSCOLOR; + index = TranslationToCacheIndex(skinnum); + } + else + I_Error("Invalid translation %d", skinnum); if (flags & GTC_CACHE) { // Allocate table for skin if necessary - if (!translationtablecache[skintableindex]) - translationtablecache[skintableindex] = Z_Calloc(MAXSKINCOLORS * sizeof(UINT8**), PU_STATIC, NULL); + if (!cache[index]) + cache[index] = Z_Calloc(MAXSKINCOLORS * sizeof(UINT8**), PU_STATIC, NULL); // Get colormap - ret = translationtablecache[skintableindex][color]; + ret = cache[index][color]; // Rebuild the cache if necessary if (skincolor_modified[color]) { - for (i = 0; i < (INT32)(sizeof(translationtablecache) / sizeof(translationtablecache[0])); i++) + INT32 i; + + for (i = 0; i < TT_CACHE_SIZE; i++) if (translationtablecache[i] && translationtablecache[i][color]) - R_GenerateTranslationColormap(translationtablecache[i][color], CacheIndexToSkin(i), color); + R_GenerateTranslationColormap(translationtablecache[i][color], CacheIndexToTranslation(i), color, starttranscolor); + for (i = 0; i < NUM_PALETTE_ENTRIES; i++) + if (skintranslationcache[i] && skintranslationcache[i][color]) + R_GenerateTranslationColormap(skintranslationcache[i][color], 0, color, i); skincolor_modified[color] = false; } @@ -596,11 +616,11 @@ UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags if (!ret) { ret = Z_MallocAlign(NUM_PALETTE_ENTRIES, (flags & GTC_CACHE) ? PU_LEVEL : PU_STATIC, NULL, 8); - R_GenerateTranslationColormap(ret, skinnum, color); + R_GenerateTranslationColormap(ret, skinnum, color, starttranscolor); // Cache the colormap if desired if (flags & GTC_CACHE) - translationtablecache[skintableindex][color] = ret; + cache[index][color] = ret; } return ret; @@ -618,9 +638,12 @@ void R_FlushTranslationColormapCache(void) { INT32 i; - for (i = 0; i < (INT32)(sizeof(translationtablecache) / sizeof(translationtablecache[0])); i++) + for (i = 0; i < TT_CACHE_SIZE; i++) if (translationtablecache[i]) memset(translationtablecache[i], 0, MAXSKINCOLORS * sizeof(UINT8**)); + for (i = 0; i < NUM_PALETTE_ENTRIES; i++) + if (skintranslationcache[i]) + memset(skintranslationcache[i], 0, MAXSKINCOLORS * sizeof(UINT8**)); } UINT16 R_GetColorByName(const char *name) @@ -748,13 +771,15 @@ static void R_CalcTiltedLighting(fixed_t start, fixed_t end) } } +#define PLANELIGHTFLOAT (BASEVIDWIDTH * BASEVIDWIDTH / vid.width / zeroheight / 21.0f * FIXED_TO_FLOAT(fovtan)) + // Lighting is simple. It's just linear interpolation from start to end -#define CALC_SLOPE_LIGHT { \ - float planelightfloat = PLANELIGHTFLOAT; \ - float lightstart, lightend; \ - lightend = (iz + ds_szp->x*width) * planelightfloat; \ - lightstart = iz * planelightfloat; \ - R_CalcTiltedLighting(FloatToFixed(lightstart), FloatToFixed(lightend)); \ +static void R_CalcSlopeLight(void) +{ + float iz = ds_slopelight.z + ds_slopelight.y * (centery - ds_y) + ds_slopelight.x * (ds_x1 - centerx); + float lightstart = iz * PLANELIGHTFLOAT; + float lightend = (iz + ds_slopelight.x * (ds_x2 - ds_x1)) * PLANELIGHTFLOAT; + R_CalcTiltedLighting(FloatToFixed(lightstart), FloatToFixed(lightend)); } // ========================================================================== diff --git a/src/r_draw.h b/src/r_draw.h index 55cdf254d..80b91567c 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -56,7 +56,7 @@ extern fixed_t ds_xfrac, ds_yfrac, ds_xstep, ds_ystep; extern INT32 ds_waterofs, ds_bgofs; extern UINT16 ds_flatwidth, ds_flatheight; -extern boolean ds_powersoftwo, ds_solidcolor; +extern boolean ds_powersoftwo, ds_solidcolor, ds_fog; extern UINT8 *ds_source; extern UINT8 *ds_transmap; @@ -66,8 +66,7 @@ typedef struct { } floatv3_t; // Vectors for Software's tilted slope drawers -extern floatv3_t *ds_su, *ds_sv, *ds_sz; -extern floatv3_t *ds_sup, *ds_svp, *ds_szp; +extern floatv3_t ds_su, ds_sv, ds_sz, ds_slopelight; extern float focallengthf, zeroheight; // Variable flat sizes @@ -123,7 +122,7 @@ UINT8 *R_GetBlendTable(int style, INT32 alphalevel); boolean R_BlendLevelVisible(INT32 blendmode, INT32 alphalevel); // Color ramp modification should force a recache -extern UINT8 skincolor_modified[]; +extern boolean skincolor_modified[]; void R_InitViewBuffer(INT32 width, INT32 height); void R_VideoErase(size_t ofs, INT32 count); @@ -145,8 +144,6 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void); void R_DrawFogColumn_8(void); void R_DrawColumnShadowed_8(void); -#define PLANELIGHTFLOAT (BASEVIDWIDTH * BASEVIDWIDTH / vid.width / zeroheight / 21.0f * FIXED_TO_FLOAT(fovtan)) - void R_DrawSpan_8(void); void R_DrawTranslucentSpan_8(void); void R_DrawTiltedSpan_8(void); diff --git a/src/r_draw8.c b/src/r_draw8.c index f3f3b04c7..463e4716f 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -649,12 +649,11 @@ void R_DrawTiltedSpan_8(void) double endz, endu, endv; UINT32 stepu, stepv; - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; source = ds_source; @@ -672,18 +671,18 @@ void R_DrawTiltedSpan_8(void) *dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]]; dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -725,9 +724,9 @@ void R_DrawTiltedSpan_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -771,12 +770,11 @@ void R_DrawTiltedTranslucentSpan_8(void) double endz, endu, endv; UINT32 stepu, stepv; - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; source = ds_source; @@ -793,18 +791,18 @@ void R_DrawTiltedTranslucentSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -846,9 +844,9 @@ void R_DrawTiltedTranslucentSpan_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -893,12 +891,11 @@ void R_DrawTiltedWaterSpan_8(void) double endz, endu, endv; UINT32 stepu, stepv; - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1; @@ -916,18 +913,18 @@ void R_DrawTiltedWaterSpan_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dsrc++); dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -969,9 +966,9 @@ void R_DrawTiltedWaterSpan_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -1014,12 +1011,11 @@ void R_DrawTiltedSplat_8(void) double endz, endu, endv; UINT32 stepu, stepv; - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; source = ds_source; @@ -1040,18 +1036,18 @@ void R_DrawTiltedSplat_8(void) *dest = colormap[val]; dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -1097,9 +1093,9 @@ void R_DrawTiltedSplat_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -1582,9 +1578,9 @@ void R_DrawTiltedFloorSprite_8(void) double endz, endu, endv; UINT32 stepu, stepv; - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); dest = &topleft[ds_y*vid.width + ds_x1]; source = (UINT16 *)ds_source; @@ -1595,9 +1591,9 @@ void R_DrawTiltedFloorSprite_8(void) startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -1642,9 +1638,9 @@ void R_DrawTiltedFloorSprite_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -1691,9 +1687,9 @@ void R_DrawTiltedTranslucentFloorSprite_8(void) double endz, endu, endv; UINT32 stepu, stepv; - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); dest = &topleft[ds_y*vid.width + ds_x1]; source = (UINT16 *)ds_source; @@ -1704,9 +1700,9 @@ void R_DrawTiltedTranslucentFloorSprite_8(void) startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -1751,9 +1747,9 @@ void R_DrawTiltedTranslucentFloorSprite_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -1981,9 +1977,7 @@ void R_DrawTiltedFogSpan_8(void) UINT8 *dest = &topleft[ds_y*vid.width + ds_x1]; - double iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - - CALC_SLOPE_LIGHT + R_CalcSlopeLight(); do { @@ -2035,9 +2029,7 @@ void R_DrawTiltedSolidColorSpan_8(void) UINT8 source = ds_source[0]; UINT8 *dest = &topleft[ds_y*vid.width + ds_x1]; - double iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - - CALC_SLOPE_LIGHT + R_CalcSlopeLight(); do { @@ -2056,9 +2048,7 @@ void R_DrawTiltedTransSolidColorSpan_8(void) UINT8 source = ds_source[0]; UINT8 *dest = &topleft[ds_y*vid.width + ds_x1]; - double iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - - CALC_SLOPE_LIGHT + R_CalcSlopeLight(); do { @@ -2099,9 +2089,7 @@ void R_DrawTiltedWaterSolidColorSpan_8(void) UINT8 *dest = &topleft[ds_y*vid.width + ds_x1]; UINT8 *dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1; - double iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - - CALC_SLOPE_LIGHT + R_CalcSlopeLight(); do { diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c index b96325871..0d8229200 100644 --- a/src/r_draw8_npo2.c +++ b/src/r_draw8_npo2.c @@ -114,12 +114,11 @@ void R_DrawTiltedSpan_NPO2_8(void) struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; source = ds_source; @@ -153,18 +152,18 @@ void R_DrawTiltedSpan_NPO2_8(void) *dest = colormap[source[((y * ds_flatwidth) + x)]]; } dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -238,9 +237,9 @@ void R_DrawTiltedSpan_NPO2_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -303,12 +302,11 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; source = ds_source; @@ -341,18 +339,18 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); } dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -426,9 +424,9 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -490,12 +488,11 @@ void R_DrawTiltedSplat_NPO2_8(void) struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; source = ds_source; @@ -533,18 +530,18 @@ void R_DrawTiltedSplat_NPO2_8(void) *dest = colormap[val]; dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -622,9 +619,9 @@ void R_DrawTiltedSplat_NPO2_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -967,9 +964,9 @@ void R_DrawTiltedFloorSprite_NPO2_8(void) struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); dest = &topleft[ds_y*vid.width + ds_x1]; source = (UINT16 *)ds_source; @@ -980,9 +977,9 @@ void R_DrawTiltedFloorSprite_NPO2_8(void) startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -1057,9 +1054,9 @@ void R_DrawTiltedFloorSprite_NPO2_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -1123,9 +1120,9 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void) struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); dest = &topleft[ds_y*vid.width + ds_x1]; source = (UINT16 *)ds_source; @@ -1136,9 +1133,9 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void) startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -1213,9 +1210,9 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; @@ -1408,12 +1405,11 @@ void R_DrawTiltedWaterSpan_NPO2_8(void) struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); - iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); + iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx); + uz = ds_su.z + ds_su.y*(centery-ds_y) + ds_su.x*(ds_x1-centerx); + vz = ds_sv.z + ds_sv.y*(centery-ds_y) + ds_sv.x*(ds_x1-centerx); - CALC_SLOPE_LIGHT - - uz = ds_sup->z + ds_sup->y*(centery-ds_y) + ds_sup->x*(ds_x1-centerx); - vz = ds_svp->z + ds_svp->y*(centery-ds_y) + ds_svp->x*(ds_x1-centerx); + R_CalcSlopeLight(); dest = &topleft[ds_y*vid.width + ds_x1]; dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1; @@ -1447,18 +1443,18 @@ void R_DrawTiltedWaterSpan_NPO2_8(void) *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++); } dest++; - iz += ds_szp->x; - uz += ds_sup->x; - vz += ds_svp->x; + iz += ds_sz.x; + uz += ds_su.x; + vz += ds_sv.x; } while (--width >= 0); #else startz = 1.f/iz; startu = uz*startz; startv = vz*startz; - izstep = ds_szp->x * SPANSIZE; - uzstep = ds_sup->x * SPANSIZE; - vzstep = ds_svp->x * SPANSIZE; + izstep = ds_sz.x * SPANSIZE; + uzstep = ds_su.x * SPANSIZE; + vzstep = ds_sv.x * SPANSIZE; //x1 = 0; width++; @@ -1532,9 +1528,9 @@ void R_DrawTiltedWaterSpan_NPO2_8(void) else { double left = width; - iz += ds_szp->x * left; - uz += ds_sup->x * left; - vz += ds_svp->x * left; + iz += ds_sz.x * left; + uz += ds_su.x * left; + vz += ds_sv.x * left; endz = 1.f/iz; endu = uz*endz; diff --git a/src/r_main.c b/src/r_main.c index fa4b89431..b2595254f 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -353,7 +353,7 @@ angle_t R_PointToAngle2(fixed_t pviewx, fixed_t pviewy, fixed_t x, fixed_t y) fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1) { angle_t angle; - fixed_t dx, dy, dist; + ufixed_t dx, dy, dist; dx = abs(px1 - px2); dy = abs(py1 - py2); @@ -966,16 +966,6 @@ void R_ExecuteSetViewSize(void) dy = FixedMul(abs(dy), fovtan); yslopetab[i] = FixedDiv(centerx*FRACUNIT, dy); } - - if (ds_su) - Z_Free(ds_su); - if (ds_sv) - Z_Free(ds_sv); - if (ds_sz) - Z_Free(ds_sz); - - ds_su = ds_sv = ds_sz = NULL; - ds_sup = ds_svp = ds_szp = NULL; } memset(scalelight, 0xFF, sizeof(scalelight)); @@ -1050,6 +1040,34 @@ fixed_t R_AdjustFOV(fixed_t ftan) return ftan; } +// +// R_IsPointInSector +// +boolean R_IsPointInSector(sector_t *sector, fixed_t x, fixed_t y) +{ + size_t i; + line_t *closest = NULL; + fixed_t closestdist = INT32_MAX; + + for (i = 0; i < sector->linecount; i++) + { + vertex_t v; + fixed_t dist; + + // find the line closest to the point we're looking for. + P_ClosestPointOnLine(x, y, sector->lines[i], &v); + dist = R_PointToDist2(0, 0, v.x - x, v.y - y); + if (dist < closestdist) + { + closest = sector->lines[i]; + closestdist = dist; + } + } + + // if the side of the closest line is in this sector, we're inside of it. + return P_PointOnLineSide(x, y, closest) == 0 ? closest->frontsector == sector : closest->backsector == sector; +} + // // R_PointInSubsector // @@ -1542,9 +1560,8 @@ void R_RenderPlayerView(player_t *player) ps_numsprites.value.i = numvisiblesprites; - // Add skybox portals caused by sky visplanes. - if (cv_skybox.value && skyboxmo[0]) - Portal_AddSkyboxPortals(); + // Add portals caused by visplanes. + Portal_AddPlanePortals(cv_skybox.value); // Portal rendering. Hijacks the BSP traversal. PS_START_TIMING(ps_sw_portaltime); @@ -1577,9 +1594,21 @@ void R_RenderPlayerView(player_t *player) Mask_Pre(&masks[nummasks - 1]); curdrawsegs = ds_p; - // Render the BSP from the new viewpoint, and clip - // any sprites with the new clipsegs and window. - R_RenderBSPNode((INT32)numnodes - 1); + if (portal->is_horizon) + { + // If the portal is a plane or a horizon portal, then we just render a horizon line + R_RenderPortalHorizonLine(portal->horizon_sector); + } + else + { + // Render the BSP from the new viewpoint, and clip + // any sprites with the new clipsegs and window. + R_RenderBSPNode((INT32)numnodes - 1); + } + + // Don't add skybox portals while already rendering a skybox view, because that'll cause an infinite loop + Portal_AddPlanePortals(cv_skybox.value && !portal->is_skybox); + Mask_Post(&masks[nummasks - 1]); R_ClipSprites(ds_p - (masks[nummasks - 1].drawsegs[1] - masks[nummasks - 1].drawsegs[0]), portal); diff --git a/src/r_main.h b/src/r_main.h index 3cbe07029..f6ecd760a 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -79,6 +79,7 @@ fixed_t R_PointToDist(fixed_t x, fixed_t y); fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1); fixed_t R_ScaleFromGlobalAngle(angle_t visangle); +boolean R_IsPointInSector(sector_t *sector, fixed_t x, fixed_t y); subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); subsector_t *R_PointInSubsectorOrNull(fixed_t x, fixed_t y); diff --git a/src/r_picformats.c b/src/r_picformats.c index 3e817f4a0..0de15b427 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1489,7 +1489,7 @@ static void R_ParseSpriteInfo(boolean spr2) spritenum_t sprnum = NUMSPRITES; playersprite_t spr2num = NUMPLAYERSPRITES; INT32 i; - INT32 skinnumbers[MAXSKINS]; + UINT8 *skinnumbers = NULL; INT32 foundskins = 0; // Sprite name @@ -1587,7 +1587,9 @@ static void R_ParseSpriteInfo(boolean spr2) if (skinnum == -1) I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName); - skinnumbers[foundskins] = skinnum; + if (skinnumbers == NULL) + skinnumbers = Z_Malloc(sizeof(UINT8) * numskins, PU_STATIC, NULL); + skinnumbers[foundskins] = (UINT8)skinnum; foundskins++; } else if (stricmp(sprinfoToken, "FRAME")==0) @@ -1600,8 +1602,7 @@ static void R_ParseSpriteInfo(boolean spr2) I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition"); for (i = 0; i < foundskins; i++) { - size_t skinnum = skinnumbers[i]; - skin_t *skin = &skins[skinnum]; + skin_t *skin = skins[skinnumbers[i]]; spriteinfo_t *sprinfo = skin->sprinfo; M_Memcpy(&sprinfo[spr2num], info, sizeof(spriteinfo_t)); } @@ -1625,8 +1626,11 @@ static void R_ParseSpriteInfo(boolean spr2) { I_Error("Error parsing SPRTINFO lump: Expected \"{\" for sprite \"%s\", got \"%s\"",newSpriteName,sprinfoToken); } + Z_Free(sprinfoToken); Z_Free(info); + if (skinnumbers) + Z_Free(skinnumbers); } // diff --git a/src/r_plane.c b/src/r_plane.c index fb1c50d12..43a18e83a 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -85,7 +85,8 @@ fixed_t *yslopetab; fixed_t *yslope; static fixed_t xoffs, yoffs; -static floatv3_t ds_slope_origin, ds_slope_u, ds_slope_v; +static floatv3_t slope_origin, slope_u, slope_v; +static floatv3_t slope_lightu, slope_lightv; static INT16 *ffloor_f_clip; static INT16 *ffloor_c_clip; @@ -137,6 +138,12 @@ void R_AllocPlaneMemory(void) spanstart = Z_Realloc(spanstart, sizeof(*spanstart) * viewheight, PU_STATIC, NULL); } +static void CalcSlopePlaneVectors(visplane_t *pl, fixed_t xoff, fixed_t yoff); +static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t height, float ang, angle_t plangle); + +static void DoSlopeCrossProducts(void); +static void DoSlopeLightCrossProduct(void); + // // Water ripple effect // Needs the height of the plane, and the vertical position of the span. @@ -205,6 +212,8 @@ static void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { ds_xstep = FixedMul(planesin, planeheight) / span; ds_ystep = FixedMul(planecos, planeheight) / span; + ds_xstep = FixedMul(currentplane->xscale, ds_xstep); + ds_ystep = FixedMul(currentplane->yscale, ds_ystep); } else ds_xstep = ds_ystep = FRACUNIT; @@ -214,8 +223,8 @@ static void R_MapPlane(INT32 y, INT32 x1, INT32 x2) // to step from those to the proper texture coordinate to start drawing at. // That way, the texture coordinate is always calculated by its position // on the screen and not by its position relative to the edge of the visplane. - ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep; - ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep; + ds_xfrac = xoffs + FixedMul(currentplane->xscale, FixedMul(planecos, distance)) + (x1 - centerx) * ds_xstep; + ds_yfrac = yoffs - FixedMul(currentplane->yscale, FixedMul(planesin, distance)) + (x1 - centerx) * ds_ystep; // Water ripple effect if (planeripple.active) @@ -264,9 +273,9 @@ static void R_MapTiltedPlane(INT32 y, INT32 x1, INT32 x2) { ds_bgofs = R_CalculateRippleOffset(y); - ds_sup = &ds_su[y]; - ds_svp = &ds_sv[y]; - ds_szp = &ds_sz[y]; + R_CalculatePlaneRipple(currentplane->viewangle + currentplane->plangle); + + CalcSlopePlaneVectors(currentplane, (xoffs + planeripple.xfrac), (yoffs + planeripple.yfrac)); ds_bgofs >>= FRACBITS; @@ -414,17 +423,19 @@ static visplane_t *new_visplane(unsigned hash) // Same height, same flattexture, same lightlevel. // If not, allocates another of them. // -visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, - fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap, - ffloor_t *pfloor, polyobj_t *polyobj, pslope_t *slope) +visplane_t *R_FindPlane(sector_t *sector, fixed_t height, INT32 picnum, INT32 lightlevel, + fixed_t xoff, fixed_t yoff, fixed_t xscale, fixed_t yscale, + angle_t plangle, extracolormap_t *planecolormap, + ffloor_t *pfloor, polyobj_t *polyobj, pslope_t *slope, sectorportal_t *portalsector) { visplane_t *check; unsigned hash; if (!slope) // Don't mess with this right now if a slope is involved { - xoff += viewx; - yoff -= viewy; + xoff += FixedMul(viewx, xscale); + yoff -= FixedMul(viewy, yscale); + if (plangle != 0) { // Add the view offset, rotated by the plane angle. @@ -465,16 +476,17 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, hash = visplane_hash(picnum, lightlevel, height); for (check = visplanes[hash]; check; check = check->next) { - if (polyobj != check->polyobj) - continue; if (height == check->height && picnum == check->picnum && lightlevel == check->lightlevel && xoff == check->xoffs && yoff == check->yoffs + && xscale == check->xscale && yscale == check->yscale && planecolormap == check->extra_colormap && check->viewx == viewx && check->viewy == viewy && check->viewz == viewz && check->viewangle == viewangle && check->plangle == plangle - && check->slope == slope) + && check->slope == slope + && check->polyobj == polyobj + && P_CompareSectorPortals(check->portalsector, portalsector)) { return check; } @@ -494,6 +506,8 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, check->maxx = -1; check->xoffs = xoff; check->yoffs = yoff; + check->xscale = xscale; + check->yscale = yscale; check->extra_colormap = planecolormap; check->ffloor = pfloor; check->viewx = viewx; @@ -501,6 +515,8 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, check->viewz = viewz; check->viewangle = viewangle; check->plangle = plangle; + check->sector = sector; + check->portalsector = portalsector; check->polyobj = polyobj; check->slope = slope; @@ -569,6 +585,8 @@ visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop) new_pl->lightlevel = pl->lightlevel; new_pl->xoffs = pl->xoffs; new_pl->yoffs = pl->yoffs; + new_pl->xscale = pl->xscale; + new_pl->yscale = pl->yscale; new_pl->extra_colormap = pl->extra_colormap; new_pl->ffloor = pl->ffloor; new_pl->viewx = pl->viewx; @@ -576,8 +594,10 @@ visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop) new_pl->viewz = pl->viewz; new_pl->viewangle = pl->viewangle; new_pl->plangle = pl->plangle; + new_pl->sector = pl->sector; new_pl->polyobj = pl->polyobj; new_pl->slope = pl->slope; + new_pl->portalsector = pl->portalsector; pl = new_pl; pl->minx = start; pl->maxx = stop; @@ -709,8 +729,6 @@ static INT64 R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y) // Sets the texture origin vector of the sloped plane. static void R_SetSlopePlaneOrigin(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, fixed_t angle) { - floatv3_t *p = &ds_slope_origin; - INT64 vx = (INT64)xpos + (INT64)xoff; INT64 vy = (INT64)ypos - (INT64)yoff; @@ -718,125 +736,164 @@ static void R_SetSlopePlaneOrigin(pslope_t *slope, fixed_t xpos, fixed_t ypos, f float vyf = vy / (float)FRACUNIT; float ang = ANG2RAD(ANGLE_270 - angle); - // p is the texture origin in view space + // slope_origin is the texture origin in view space // Don't add in the offsets at this stage, because doing so can result in // errors if the flat is rotated. - p->x = vxf * cos(ang) - vyf * sin(ang); - p->z = vxf * sin(ang) + vyf * cos(ang); - p->y = (R_GetSlopeZAt(slope, -xoff, yoff) - zpos) / (float)FRACUNIT; + slope_origin.x = vxf * cos(ang) - vyf * sin(ang); + slope_origin.z = vxf * sin(ang) + vyf * cos(ang); + slope_origin.y = (R_GetSlopeZAt(slope, -xoff, yoff) - zpos) / (float)FRACUNIT; } // This function calculates all of the vectors necessary for drawing a sloped plane. void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle) { - // Potentially override other stuff for now cus we're mean. :< But draw a slope plane! // I copied ZDoom's code and adapted it to SRB2... -Red - floatv3_t *m = &ds_slope_v, *n = &ds_slope_u; - fixed_t height, temp; + fixed_t height, z_at_xy; float ang; R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle); height = P_GetSlopeZAt(slope, xpos, ypos); zeroheight = FixedToFloat(height - zpos); - // m is the v direction vector in view space ang = ANG2RAD(ANGLE_180 - (angle + plangle)); - m->x = cos(ang); - m->z = sin(ang); - // n is the u direction vector in view space - n->x = sin(ang); - n->z = -cos(ang); + CalcSlopeLightVectors(slope, xpos, ypos, height, ang, plangle); + + if (ds_solidcolor || ds_fog) + { + DoSlopeLightCrossProduct(); + return; + } + + // slope_v is the v direction vector in view space + slope_v.x = cos(ang); + slope_v.z = sin(ang); + + // slope_u is the u direction vector in view space + slope_u.x = sin(ang); + slope_u.z = -cos(ang); plangle >>= ANGLETOFINESHIFT; - temp = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); - m->y = FixedToFloat(temp - height); - temp = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); - n->y = FixedToFloat(temp - height); + z_at_xy = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); + slope_v.y = FixedToFloat(z_at_xy - height); + z_at_xy = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); + slope_u.y = FixedToFloat(z_at_xy - height); + + DoSlopeCrossProducts(); + DoSlopeLightCrossProduct(); } // This function calculates all of the vectors necessary for drawing a sloped and scaled plane. void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle) { - floatv3_t *m = &ds_slope_v, *n = &ds_slope_u; - fixed_t height, temp; + fixed_t height, z_at_xy; - float xscale = FixedToFloat(xs); - float yscale = FixedToFloat(ys); float ang; R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle); height = P_GetSlopeZAt(slope, xpos, ypos); zeroheight = FixedToFloat(height - zpos); - // m is the v direction vector in view space ang = ANG2RAD(ANGLE_180 - (angle + plangle)); - m->x = yscale * cos(ang); - m->z = yscale * sin(ang); + + CalcSlopeLightVectors(slope, xpos, ypos, height, ang, plangle); + + if (ds_solidcolor || ds_fog) + { + DoSlopeLightCrossProduct(); + return; + } + + float xscale = FixedToFloat(xs); + float yscale = FixedToFloat(ys); + + // m is the v direction vector in view space + slope_v.x = yscale * cos(ang); + slope_v.z = yscale * sin(ang); // n is the u direction vector in view space - n->x = xscale * sin(ang); - n->z = -xscale * cos(ang); + slope_u.x = xscale * sin(ang); + slope_u.z = -xscale * cos(ang); ang = ANG2RAD(plangle); - temp = P_GetSlopeZAt(slope, xpos + FloatToFixed(yscale * sin(ang)), ypos + FloatToFixed(yscale * cos(ang))); - m->y = FixedToFloat(temp - height); - temp = P_GetSlopeZAt(slope, xpos + FloatToFixed(xscale * cos(ang)), ypos - FloatToFixed(xscale * sin(ang))); - n->y = FixedToFloat(temp - height); + z_at_xy = P_GetSlopeZAt(slope, xpos + FloatToFixed(yscale * sin(ang)), ypos + FloatToFixed(yscale * cos(ang))); + slope_v.y = FixedToFloat(z_at_xy - height); + z_at_xy = P_GetSlopeZAt(slope, xpos + FloatToFixed(xscale * cos(ang)), ypos - FloatToFixed(xscale * sin(ang))); + slope_u.y = FixedToFloat(z_at_xy - height); + + DoSlopeCrossProducts(); + DoSlopeLightCrossProduct(); } -void R_CalculateSlopeVectors(void) +static void CalcSlopeLightVectors(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t height, float ang, angle_t plangle) +{ + fixed_t z_at_xy; + + slope_lightv.x = cos(ang); + slope_lightv.z = sin(ang); + + slope_lightu.x = sin(ang); + slope_lightu.z = -cos(ang); + + plangle >>= ANGLETOFINESHIFT; + z_at_xy = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); + slope_lightv.y = FixedToFloat(z_at_xy - height); + z_at_xy = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); + slope_lightu.y = FixedToFloat(z_at_xy - height); +} + +// Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. +#define CROSS(d, v1, v2) \ +d.x = (v1.y * v2.z) - (v1.z * v2.y);\ +d.y = (v1.z * v2.x) - (v1.x * v2.z);\ +d.z = (v1.x * v2.y) - (v1.y * v2.x) + +static void DoSlopeCrossProducts(void) { float sfmult = 65536.f; - // Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. -#define CROSS(d, v1, v2) \ -d->x = (v1.y * v2.z) - (v1.z * v2.y);\ -d->y = (v1.z * v2.x) - (v1.x * v2.z);\ -d->z = (v1.x * v2.y) - (v1.y * v2.x) - CROSS(ds_sup, ds_slope_origin, ds_slope_v); - CROSS(ds_svp, ds_slope_origin, ds_slope_u); - CROSS(ds_szp, ds_slope_v, ds_slope_u); -#undef CROSS + CROSS(ds_su, slope_origin, slope_v); + CROSS(ds_sv, slope_origin, slope_u); + CROSS(ds_sz, slope_v, slope_u); - ds_sup->z *= focallengthf; - ds_svp->z *= focallengthf; - ds_szp->z *= focallengthf; + ds_su.z *= focallengthf; + ds_sv.z *= focallengthf; + ds_sz.z *= focallengthf; if (ds_solidcolor) return; // Premultiply the texture vectors with the scale factors if (ds_powersoftwo) - sfmult *= (1 << nflatshiftup); + sfmult *= 1 << nflatshiftup; - ds_sup->x *= sfmult; - ds_sup->y *= sfmult; - ds_sup->z *= sfmult; - ds_svp->x *= sfmult; - ds_svp->y *= sfmult; - ds_svp->z *= sfmult; + ds_su.x *= sfmult; + ds_su.y *= sfmult; + ds_su.z *= sfmult; + ds_sv.x *= sfmult; + ds_sv.y *= sfmult; + ds_sv.z *= sfmult; } -void R_SetTiltedSpan(INT32 span) +static void DoSlopeLightCrossProduct(void) { - if (ds_su == NULL) - ds_su = Z_Malloc(sizeof(*ds_su) * vid.height, PU_STATIC, NULL); - if (ds_sv == NULL) - ds_sv = Z_Malloc(sizeof(*ds_sv) * vid.height, PU_STATIC, NULL); - if (ds_sz == NULL) - ds_sz = Z_Malloc(sizeof(*ds_sz) * vid.height, PU_STATIC, NULL); + CROSS(ds_slopelight, slope_lightv, slope_lightu); - ds_sup = &ds_su[span]; - ds_svp = &ds_sv[span]; - ds_szp = &ds_sz[span]; + ds_slopelight.z *= focallengthf; } -static void R_SetSlopePlaneVectors(visplane_t *pl, INT32 y, fixed_t xoff, fixed_t yoff) +#undef CROSS + +static void CalcSlopePlaneVectors(visplane_t *pl, fixed_t xoff, fixed_t yoff) { - R_SetTiltedSpan(y); - R_SetSlopePlane(pl->slope, pl->viewx, pl->viewy, pl->viewz, xoff, yoff, pl->viewangle, pl->plangle); - R_CalculateSlopeVectors(); + if (!ds_fog && (pl->xscale != FRACUNIT || pl->yscale != FRACUNIT)) + { + R_SetScaledSlopePlane(pl->slope, pl->viewx, pl->viewy, pl->viewz, + FixedDiv(FRACUNIT, pl->xscale), FixedDiv(FRACUNIT, pl->yscale), + FixedDiv(xoff, pl->xscale), FixedDiv(yoff, pl->yscale), pl->viewangle, pl->plangle); + } + else + R_SetSlopePlane(pl->slope, pl->viewx, pl->viewy, pl->viewz, xoff, yoff, pl->viewangle, pl->plangle); } static inline void R_AdjustSlopeCoordinates(vector3_t *origin) @@ -873,7 +930,6 @@ void R_DrawSinglePlane(visplane_t *pl) INT32 light = 0; INT32 x, stop; ffloor_t *rover; - boolean fog = false; INT32 spanfunctype = BASEDRAWFUNC; void (*mapfunc)(INT32, INT32, INT32); @@ -887,6 +943,8 @@ void R_DrawSinglePlane(visplane_t *pl) return; } + ds_powersoftwo = ds_solidcolor = ds_fog = false; + planeripple.active = false; if (pl->polyobj) @@ -951,13 +1009,13 @@ void R_DrawSinglePlane(visplane_t *pl) } else if (pl->ffloor->fofflags & FOF_FOG) { - fog = true; + ds_fog = true; spanfunctype = SPANDRAWFUNC_FOG; light = (pl->lightlevel >> LIGHTSEGSHIFT); } else light = (pl->lightlevel >> LIGHTSEGSHIFT); - if (pl->ffloor->fofflags & FOF_RIPPLE && !fog) + if (pl->ffloor->fofflags & FOF_RIPPLE && !ds_fog) { planeripple.active = true; @@ -985,9 +1043,7 @@ void R_DrawSinglePlane(visplane_t *pl) light = (pl->lightlevel >> LIGHTSEGSHIFT); } - ds_powersoftwo = ds_solidcolor = false; - - if (fog) + if (ds_fog) { // Since all fog planes do is apply a colormap, it's not required // to know any information about their textures. @@ -1053,13 +1109,13 @@ void R_DrawSinglePlane(visplane_t *pl) if (pl->slope) { - if (fog) + if (ds_fog) mapfunc = R_MapTiltedFogPlane; else { mapfunc = R_MapTiltedPlane; - if (!pl->plangle && !ds_solidcolor) + if (!pl->plangle && !ds_solidcolor && pl->xscale == FRACUNIT && pl->yscale == FRACUNIT) { if (ds_powersoftwo) R_AdjustSlopeCoordinates(&pl->slope->o); @@ -1068,21 +1124,10 @@ void R_DrawSinglePlane(visplane_t *pl) } } - if (planeripple.active) - { + if (!ds_fog && planeripple.active) planeheight = abs(P_GetSlopeZAt(pl->slope, pl->viewx, pl->viewy) - pl->viewz); - - R_PlaneBounds(pl); - - for (x = pl->high; x < pl->low; x++) - { - ds_bgofs = R_CalculateRippleOffset(x); - R_CalculatePlaneRipple(pl->viewangle + pl->plangle); - R_SetSlopePlaneVectors(pl, x, (xoffs + planeripple.xfrac), (yoffs + planeripple.yfrac)); - } - } else - R_SetSlopePlaneVectors(pl, 0, xoffs, yoffs); + CalcSlopePlaneVectors(pl, xoffs, yoffs); switch (spanfunctype) { diff --git a/src/r_plane.h b/src/r_plane.h index b137cd57e..af27355cc 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -49,10 +49,13 @@ typedef struct visplane_s INT32 high, low; // R_PlaneBounds should set these. fixed_t xoffs, yoffs; // Scrolling flats. + fixed_t xscale, yscale; + sector_t *sector; struct ffloor_s *ffloor; polyobj_t *polyobj; pslope_t *slope; + sectorportal_t *portalsector; } visplane_t; extern visplane_t *visplanes[MAXVISPLANES]; @@ -71,8 +74,9 @@ void R_ClearPlanes(void); void R_ClearFFloorClips (void); void R_DrawPlanes(void); -visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle, - extracolormap_t *planecolormap, ffloor_t *ffloor, polyobj_t *polyobj, pslope_t *slope); +visplane_t *R_FindPlane(sector_t *sector, fixed_t height, INT32 picnum, INT32 lightlevel, + fixed_t xoff, fixed_t yoff, fixed_t xscale, fixed_t yscale, angle_t plangle, + extracolormap_t *planecolormap, ffloor_t *ffloor, polyobj_t *polyobj, pslope_t *slope, sectorportal_t *portalsector); visplane_t *R_CheckPlane(visplane_t *pl, INT32 start, INT32 stop); void R_ExpandPlane(visplane_t *pl, INT32 start, INT32 stop); void R_PlaneBounds(visplane_t *plane); @@ -83,10 +87,6 @@ void R_DrawSinglePlane(visplane_t *pl); // Calculates the slope vectors needed for tilted span drawing. void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle); void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle); -void R_CalculateSlopeVectors(void); - -// Sets the slope vector pointers for the current tilted span. -void R_SetTiltedSpan(INT32 span); typedef struct planemgr_s { diff --git a/src/r_portal.c b/src/r_portal.c index e594f960a..4d042cae3 100644 --- a/src/r_portal.c +++ b/src/r_portal.c @@ -16,6 +16,8 @@ #include "r_main.h" #include "doomstat.h" #include "p_spec.h" // Skybox viewpoints +#include "p_slopes.h" // P_GetSectorFloorZAt and P_GetSectorCeilingZAt +#include "p_local.h" #include "z_zone.h" #include "r_things.h" #include "r_sky.h" @@ -140,30 +142,17 @@ void Portal_Remove (portal_t* portal) Z_Free(portal); } -/** Creates a portal out of two lines and a determined screen range. - * - * line1 determines the entrance, and line2 the exit. - * x1 and x2 determine the screen's column bounds. - - * The view's offset from the entry line center is obtained, - * and then rotated&translated to the exit line's center. - * When the portal renders, it will create the illusion of - * the two lines being seamed together. - */ -void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2) +static void Portal_GetViewpointForLine(portal_t *portal, line_t *start, line_t *dest) { - portal_t* portal = Portal_Add(x1, x2); - // Offset the portal view by the linedef centers - line_t* start = &lines[line1]; - line_t* dest = &lines[line2]; - angle_t dangle = R_PointToAngle2(0,0,dest->dx,dest->dy) - R_PointToAngle2(start->dx,start->dy,0,0); fixed_t disttopoint; angle_t angtopoint; - vertex_t dest_c, start_c; + struct { + fixed_t x, y; + } dest_c, start_c; // looking glass center start_c.x = (start->v1->x + start->v2->x) / 2; @@ -181,8 +170,31 @@ void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, con portal->viewy = dest_c.y + FixedMul(FINESINE(angtopoint>>ANGLETOFINESHIFT), disttopoint); portal->viewz = viewz + dest->frontsector->floorheight - start->frontsector->floorheight; portal->viewangle = viewangle + dangle; +} + +/** Creates a portal out of two lines and a determined screen range. + * + * line1 determines the entrance, and line2 the exit. + * x1 and x2 determine the screen's column bounds. + + * The view's offset from the entry line center is obtained, + * and then rotated&translated to the exit line's center. + * When the portal renders, it will create the illusion of + * the two lines being seamed together. + */ +void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2) +{ + portal_t* portal = Portal_Add(x1, x2); + + line_t* start = &lines[line1]; + line_t* dest = &lines[line2]; + + Portal_GetViewpointForLine(portal, start, dest); portal->clipline = line2; + portal->is_skybox = false; + portal->is_horizon = false; + portal->horizon_sector = NULL; Portal_ClipRange(portal); @@ -252,30 +264,14 @@ static boolean TrimVisplaneBounds (const visplane_t* plane, INT16* start, INT16* return false; } -/** Creates a skybox portal out of a visplane. - * - * Applies the necessary offsets and rotation to give - * a depth illusion to the skybox. - */ -void Portal_AddSkybox (const visplane_t* plane) +static void Portal_GetViewpointForSkybox(portal_t *portal) { - INT16 start, end; - mapheader_t *mh; - portal_t* portal; - - if (TrimVisplaneBounds(plane, &start, &end)) - return; - - portal = Portal_Add(start, end); - - Portal_ClipVisplane(plane, portal); - portal->viewx = skyboxmo[0]->x; portal->viewy = skyboxmo[0]->y; portal->viewz = skyboxmo[0]->z; portal->viewangle = viewangle + skyboxmo[0]->angle; - mh = mapheaderinfo[gamemap-1]; + mapheader_t *mh = mapheaderinfo[gamemap-1]; // If a relative viewpoint exists, offset the viewpoint. if (skyboxmo[1]) @@ -302,34 +298,192 @@ void Portal_AddSkybox (const visplane_t* plane) portal->viewz += viewz / mh->skybox_scalez; else if (mh->skybox_scalez < 0) portal->viewz += viewz * -mh->skybox_scalez; - - portal->clipline = -1; } -/** Creates portals for the currently existing sky visplanes. +/** Creates a skybox portal out of a visplane. + * + * Applies the necessary offsets and rotation to give + * a depth illusion to the skybox. + */ +static boolean Portal_AddSkybox (const visplane_t* plane) +{ + INT16 start, end; + portal_t* portal; + + if (TrimVisplaneBounds(plane, &start, &end)) + return false; + + portal = Portal_Add(start, end); + + Portal_ClipVisplane(plane, portal); + + portal->clipline = -1; + portal->is_skybox = true; + portal->is_horizon = false; + portal->horizon_sector = NULL; + + Portal_GetViewpointForSkybox(portal); + + return true; +} + +static void Portal_GetViewpointForSecPortal(portal_t *portal, sectorportal_t *secportal) +{ + fixed_t x, y, z; + angle_t angle; + + switch (secportal->type) + { + case SECPORTAL_LINE: + Portal_GetViewpointForLine(portal, secportal->line.start, secportal->line.dest); + return; + case SECPORTAL_OBJECT: + if (!secportal->mobj || P_MobjWasRemoved(secportal->mobj)) + return; + x = secportal->mobj->x; + y = secportal->mobj->y; + z = secportal->mobj->z; + angle = secportal->mobj->angle; + break; + case SECPORTAL_FLOOR: + x = secportal->sector->soundorg.x; + y = secportal->sector->soundorg.y; + z = P_GetSectorFloorZAt(secportal->sector, x, y); + angle = 0; + break; + case SECPORTAL_CEILING: + x = secportal->sector->soundorg.x; + y = secportal->sector->soundorg.y; + z = P_GetSectorCeilingZAt(secportal->sector, x, y); + angle = 0; + break; + case SECPORTAL_PLANE: + case SECPORTAL_HORIZON: + portal->is_horizon = true; + portal->horizon_sector = secportal->sector; + x = secportal->sector->soundorg.x; + y = secportal->sector->soundorg.y; + if (secportal->type == SECPORTAL_PLANE) + z = -viewz; + else + z = 0; + angle = 0; + break; + default: + return; + } + + fixed_t refx = secportal->origin.x - viewx; + fixed_t refy = secportal->origin.y - viewy; + + // Rotate the X/Y to match the target angle + if (angle != 0) + { + fixed_t tr_x = refx, tr_y = refy; + angle_t ang = angle >> ANGLETOFINESHIFT; + refx = FixedMul(tr_x, FINECOSINE(ang)) - FixedMul(tr_y, FINESINE(ang)); + refy = FixedMul(tr_x, FINESINE(ang)) + FixedMul(tr_y, FINECOSINE(ang)); + } + + portal->viewx = x - refx; + portal->viewy = y - refy; + portal->viewz = z + viewz; + portal->viewangle = angle + viewangle; +} + +/** Creates a sector portal out of a visplane. + */ +static boolean Portal_AddSectorPortal (const visplane_t* plane) +{ + INT16 start, end; + sectorportal_t *secportal = plane->portalsector; + + // Shortcut + if (secportal->type == SECPORTAL_SKYBOX) + { + if (cv_skybox.value && skyboxmo[0]) + return Portal_AddSkybox(plane); + return false; + } + + if (TrimVisplaneBounds(plane, &start, &end)) + return false; + + portal_t* portal = Portal_Add(start, end); + + Portal_ClipVisplane(plane, portal); + + portal->clipline = -1; + portal->is_horizon = false; + portal->is_skybox = false; + portal->horizon_sector = NULL; + + Portal_GetViewpointForSecPortal(portal, secportal); + + return true; +} + +/** Creates a transferred sector portal. + */ +void Portal_AddTransferred (UINT32 secportalnum, const INT32 x1, const INT32 x2) +{ + if (secportalnum >= secportalcount) + return; + + sectorportal_t *secportal = &secportals[secportalnum]; + if (!P_IsSectorPortalValid(secportal)) + return; + + portal_t* portal = Portal_Add(x1, x2); + portal->is_skybox = false; + portal->is_horizon = false; + portal->horizon_sector = NULL; + + if (secportal->type == SECPORTAL_SKYBOX) + Portal_GetViewpointForSkybox(portal); + else + Portal_GetViewpointForSecPortal(portal, secportal); + + if (secportal->type == SECPORTAL_LINE) + portal->clipline = secportal->line.dest - lines; + else + portal->clipline = -1; + + Portal_ClipRange(portal); + + portalline = true; +} + +/** Creates portals for the currently existing portal visplanes. * The visplanes are also removed and cleared from the list. */ -void Portal_AddSkyboxPortals (void) +void Portal_AddPlanePortals (boolean add_skyboxes) { visplane_t *pl; - INT32 i; - UINT16 count = 0; - for (i = 0; i < MAXVISPLANES; i++, pl++) + for (INT32 i = 0; i < MAXVISPLANES; i++, pl++) { for (pl = visplanes[i]; pl; pl = pl->next) { - if (pl->picnum == skyflatnum) - { - Portal_AddSkybox(pl); + if (pl->minx >= pl->maxx) + continue; + boolean added_portal = false; + + // Render sector portal if recursiveness limit hasn't been reached + if (pl->portalsector && portalrender < cv_maxportals.value) + added_portal = Portal_AddSectorPortal(pl); + + // Render skybox portal + if (!added_portal && pl->picnum == skyflatnum && add_skyboxes && skyboxmo[0]) + added_portal = Portal_AddSkybox(pl); + + // don't render this visplane anymore + if (added_portal) + { pl->minx = 0; pl->maxx = -1; - - count++; } } } - - CONS_Debug(DBG_RENDER, "Skybox portals: %d\n", count); } diff --git a/src/r_portal.h b/src/r_portal.h index f90f05fbc..2485e45a7 100644 --- a/src/r_portal.h +++ b/src/r_portal.h @@ -30,6 +30,12 @@ typedef struct portal_s fixed_t viewz; angle_t viewangle; + // For horizon portals + boolean is_horizon; + sector_t *horizon_sector; + + boolean is_skybox; + UINT8 pass; /**< Keeps track of the portal's recursion depth. */ INT32 clipline; /**< Optional clipline for line-based portals. */ @@ -49,13 +55,13 @@ extern line_t *portalclipline; extern sector_t *portalcullsector; extern INT32 portalclipstart, portalclipend; -void Portal_InitList (void); -void Portal_Remove (portal_t* portal); -void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2); -void Portal_AddSkybox (const visplane_t* plane); +void Portal_InitList (void); +void Portal_Remove (portal_t* portal); +void Portal_Add2Lines (const INT32 line1, const INT32 line2, const INT32 x1, const INT32 x2); +void Portal_AddTransferred (UINT32 secportalnum, const INT32 x1, const INT32 x2); void Portal_ClipRange (portal_t* portal); void Portal_ClipApply (const portal_t* portal); -void Portal_AddSkyboxPortals (void); +void Portal_AddPlanePortals (boolean add_skyboxes); #endif diff --git a/src/r_segs.c b/src/r_segs.c index b1d6d97b1..738a4cf34 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -48,15 +48,18 @@ fixed_t rw_distance; // static INT32 rw_x, rw_stopx; static angle_t rw_centerangle; -static fixed_t rw_offset; -static fixed_t rw_offset_top, rw_offset_mid, rw_offset_bot; -static fixed_t rw_offset2; // for splats +static fixed_t rw_offset, rw_offsetx; +static fixed_t rw_offset_top, rw_offset_mid, rw_offset_bottom; static fixed_t rw_scale, rw_scalestep; static fixed_t rw_midtexturemid, rw_toptexturemid, rw_bottomtexturemid; static INT32 worldtop, worldbottom, worldhigh, worldlow; static INT32 worldtopslope, worldbottomslope, worldhighslope, worldlowslope; // worldtop/bottom at end of slope static fixed_t rw_toptextureslide, rw_midtextureslide, rw_bottomtextureslide; // Defines how to adjust Y offsets along the wall for slopes static fixed_t rw_midtextureback, rw_midtexturebackslide; // Values for masked midtexture height calculation +static fixed_t rw_midtexturescalex, rw_midtexturescaley; +static fixed_t rw_toptexturescalex, rw_toptexturescaley; +static fixed_t rw_bottomtexturescalex, rw_bottomtexturescaley; +static fixed_t rw_invmidtexturescalex, rw_invtoptexturescalex, rw_invbottomtexturescalex; // Lactozilla: 3D floor clipping static boolean rw_floormarked = false; @@ -71,8 +74,10 @@ static fixed_t topfrac, topstep; static fixed_t bottomfrac, bottomstep; static lighttable_t **walllights; -static fixed_t *maskedtexturecol; +static fixed_t *maskedtexturecol = NULL; static fixed_t *maskedtextureheight = NULL; +static fixed_t *thicksidecol = NULL; +static fixed_t *invscale = NULL; //SoM: 3/23/2000: Use boom opening limit removal static size_t numopenings; @@ -162,7 +167,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) frontsector = curline->frontsector; backsector = curline->backsector; - texnum = R_GetTextureNum(curline->sidedef->midtexture); + sidedef = curline->sidedef; + texnum = R_GetTextureNum(sidedef->midtexture); windowbottom = windowtop = sprbotscreen = INT32_MAX; ldef = curline->linedef; @@ -200,9 +206,13 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) colfunc = colfuncs[COLDRAWFUNC_FUZZY]; } + fixed_t wall_scaley = sidedef->scaley_mid; + fixed_t scalestep = FixedDiv(ds->scalestep, wall_scaley); + fixed_t scale1 = FixedDiv(ds->scale1, wall_scaley); + range = max(ds->x2-ds->x1, 1); - rw_scalestep = ds->scalestep; - spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; + rw_scalestep = scalestep; + spryscale = scale1 + (x1 - ds->x1)*rw_scalestep; // Texture must be cached before setting colfunc_2s, // otherwise texture[texnum]->holes may be false when it shouldn't be @@ -313,8 +323,8 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) else back = backsector; - if (ds->curline->sidedef->repeatcnt) - repeats = 1 + ds->curline->sidedef->repeatcnt; + if (sidedef->repeatcnt) + repeats = 1 + sidedef->repeatcnt; else if (ldef->flags & ML_WRAPMIDTEX) { fixed_t high, low; @@ -340,15 +350,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) { if (times > 0) { - rw_scalestep = ds->scalestep; - spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; - if (dc_numlights) - { // reset all lights to their starting heights - for (i = 0; i < dc_numlights; i++) - { - rlight = &dc_lightlist[i]; - rlight->height = rlight->startheight; - } + rw_scalestep = scalestep; + spryscale = scale1 + (x1 - ds->x1)*rw_scalestep; + + // reset all lights to their starting heights + for (i = 0; i < dc_numlights; i++) + { + rlight = &dc_lightlist[i]; + rlight->height = rlight->startheight; } } @@ -390,8 +399,10 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) sprbotscreen = INT32_MAX; sprtopscreen = windowtop = (centeryfrac - FixedMul(dc_texturemid, spryscale)); - realbot = windowbottom = FixedMul(textureheight[texnum], spryscale) + sprtopscreen; - dc_iscale = 0xffffffffu / (unsigned)spryscale; + realbot = FixedMul(textureheight[texnum], spryscale) + sprtopscreen; + dc_iscale = FixedMul(ds->invscale[dc_x], wall_scaley); + + windowbottom = realbot; // draw the texture col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3); @@ -466,7 +477,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2) dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps); sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale); - dc_iscale = 0xffffffffu / (unsigned)spryscale; + dc_iscale = FixedMul(ds->invscale[dc_x], wall_scaley); // draw the texture col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3); @@ -514,6 +525,9 @@ static boolean R_IsFFloorTranslucent(visffloor_t *pfloor) // // R_RenderThickSideRange // Renders all the thick sides in the given range. + +static fixed_t ffloortexturecolumn[MAXVIDWIDTH]; + void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) { size_t pindex; @@ -525,7 +539,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) INT32 i, p; fixed_t bottombounds = viewheight << FRACBITS; fixed_t topbounds = (con_clipviewtop - 1) << FRACBITS; - fixed_t offsetvalue = 0; + fixed_t offsetvalue; lightlist_t *light; r_lightlist_t *rlight; INT32 range; @@ -534,11 +548,13 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) // NOTE: INT64 instead of fixed_t because overflow concerns INT64 top_frac, top_step, bottom_frac, bottom_step; // skew FOF walls with slopes? - boolean slopeskew = false; fixed_t ffloortextureslide = 0; INT32 oldx = -1; fixed_t left_top, left_bottom; // needed here for slope skewing pslope_t *skewslope = NULL; + boolean do_texture_skew; + INT16 lineflags; + fixed_t wall_scalex, wall_scaley; void (*colfunc_2s) (column_t *); @@ -550,7 +566,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) curline = ds->curline; backsector = pfloor->target; frontsector = curline->frontsector == pfloor->target ? curline->backsector : curline->frontsector; - texnum = R_GetTextureNum(sides[pfloor->master->sidenum[0]].midtexture); + sidedef = &sides[pfloor->master->sidenum[0]]; colfunc = colfuncs[BASEDRAWFUNC]; @@ -558,8 +574,13 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) { size_t linenum = curline->linedef-backsector->lines[0]; newline = pfloor->master->frontsector->lines[0] + linenum; - texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture); + sidedef = &sides[newline->sidenum[0]]; + lineflags = newline->flags; } + else + lineflags = curline->linedef->flags; + + texnum = R_GetTextureNum(sidedef->midtexture); if (pfloor->fofflags & FOF_TRANSLUCENT) { @@ -711,7 +732,21 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) walllights = scalelight[lightnum]; } - maskedtexturecol = ds->thicksidecol; + wall_scalex = FixedDiv(FRACUNIT, sidedef->scalex_mid); + wall_scaley = sidedef->scaley_mid; + + thicksidecol = ffloortexturecolumn; + + if (wall_scalex == FRACUNIT) + { + for (INT32 x = x1; x <= x2; x++) + thicksidecol[x] = ds->thicksidecol[x] + ds->offsetx; + } + else + { + for (INT32 x = x1; x <= x2; x++) + thicksidecol[x] = FixedDiv(ds->thicksidecol[x], wall_scalex) + ds->offsetx; + } mfloorclip = ds->sprbottomclip; mceilingclip = ds->sprtopclip; @@ -721,51 +756,33 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) left_top = P_GetFFloorTopZAt (pfloor, ds->leftpos.x, ds->leftpos.y) - viewz; left_bottom = P_GetFFloorBottomZAt(pfloor, ds->leftpos.x, ds->leftpos.y) - viewz; - skewslope = *pfloor->t_slope; // skew using top slope by default - if (newline) - { - if (newline->flags & ML_SKEWTD) - slopeskew = true; - } - else if (pfloor->master->flags & ML_SKEWTD) - slopeskew = true; + do_texture_skew = lineflags & ML_SKEWTD; - if (slopeskew) - dc_texturemid = left_top; - else - dc_texturemid = *pfloor->topheight - viewz; - - if (newline) + if (do_texture_skew) { - offsetvalue = sides[newline->sidenum[0]].rowoffset + sides[newline->sidenum[0]].offsety_mid; - if (newline->flags & ML_DONTPEGBOTTOM) - { - skewslope = *pfloor->b_slope; // skew using bottom slope - if (slopeskew) - dc_texturemid = left_bottom; - else - offsetvalue -= *pfloor->topheight - *pfloor->bottomheight; - } + skewslope = *pfloor->t_slope; // skew using top slope by default + dc_texturemid = FixedMul(left_top, wall_scaley); } else + dc_texturemid = FixedMul(*pfloor->topheight - viewz, wall_scaley); + + offsetvalue = sidedef->rowoffset + sidedef->offsety_mid; + + if (lineflags & ML_DONTPEGBOTTOM) { - offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset + sides[pfloor->master->sidenum[0]].offsety_mid; - if (curline->linedef->flags & ML_DONTPEGBOTTOM) + if (do_texture_skew) { skewslope = *pfloor->b_slope; // skew using bottom slope - if (slopeskew) - dc_texturemid = left_bottom; - else - offsetvalue -= *pfloor->topheight - *pfloor->bottomheight; + dc_texturemid = FixedMul(left_bottom, wall_scaley); } + else + offsetvalue -= FixedMul(*pfloor->topheight - *pfloor->bottomheight, wall_scaley); } - if (slopeskew) + if (skewslope) { angle_t lineangle = R_PointToAngle2(curline->v1->x, curline->v1->y, curline->v2->x, curline->v2->y); - - if (skewslope) - ffloortextureslide = FixedMul(skewslope->zdelta, FINECOSINE((lineangle-skewslope->xydirection)>>ANGLETOFINESHIFT)); + ffloortextureslide = FixedMul(skewslope->zdelta, FINECOSINE((lineangle-skewslope->xydirection)>>ANGLETOFINESHIFT)); } dc_texturemid += offsetvalue; @@ -819,7 +836,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (ffloortextureslide) { if (oldx != -1) - dc_texturemid += FixedMul(ffloortextureslide, maskedtexturecol[oldx]-maskedtexturecol[dc_x]); + dc_texturemid += FixedMul(ffloortextureslide, thicksidecol[oldx]-thicksidecol[dc_x]); oldx = dc_x; } @@ -852,10 +869,10 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) continue; } - dc_iscale = 0xffffffffu / (unsigned)spryscale; + dc_iscale = FixedMul(0xffffffffu / (unsigned)spryscale, wall_scaley); // Get data for the column - col = (column_t *)((UINT8 *)R_GetColumn(texnum, (maskedtexturecol[dc_x] >> FRACBITS)) - 3); + col = (column_t *)((UINT8 *)R_GetColumn(texnum, (thicksidecol[dc_x] >> FRACBITS)) - 3); // SoM: New code does not rely on R_DrawColumnShadowed_8 which // will (hopefully) put less strain on the stack. @@ -1034,13 +1051,18 @@ static boolean didsolidcol; // True if at least one column was marked solid static void R_RenderSegLoop (void) { angle_t angle; + fixed_t textureoffset; size_t pindex; INT32 yl; INT32 yh; INT32 mid; fixed_t texturecolumn = 0; + fixed_t toptexturecolumn = 0; + fixed_t bottomtexturecolumn = 0; fixed_t oldtexturecolumn = -1; + fixed_t oldtexturecolumn_top = -1; + fixed_t oldtexturecolumn_bottom = -1; INT32 top; INT32 bottom; INT32 i; @@ -1207,17 +1229,8 @@ static void R_RenderSegLoop (void) //SoM: Calculate offsets for Thick fake floors. // calculate texture offset angle = (rw_centerangle + xtoviewangle[rw_x])>>ANGLETOFINESHIFT; - texturecolumn = rw_offset-FixedMul(FINETANGENT(angle),rw_distance); - - if (oldtexturecolumn != -1) { - rw_bottomtexturemid += FixedMul(rw_bottomtextureslide, oldtexturecolumn-texturecolumn); - rw_midtexturemid += FixedMul(rw_midtextureslide, oldtexturecolumn-texturecolumn); - rw_toptexturemid += FixedMul(rw_toptextureslide, oldtexturecolumn-texturecolumn); - rw_midtextureback += FixedMul(rw_midtexturebackslide, oldtexturecolumn-texturecolumn); - } - oldtexturecolumn = texturecolumn; - - INT32 itexturecolumn = texturecolumn >> FRACBITS; + textureoffset = rw_offset - FixedMul(FINETANGENT(angle), rw_distance); + texturecolumn = FixedDiv(textureoffset, rw_invmidtexturescalex); // texturecolumn and lighting are independent of wall tiers if (segtextured) @@ -1230,7 +1243,6 @@ static void R_RenderSegLoop (void) dc_colormap = walllights[pindex]; dc_x = rw_x; - dc_iscale = 0xffffffffu / (unsigned)rw_scale; if (frontsector->extra_colormap) dc_colormap = frontsector->extra_colormap->colormap + (dc_colormap - colormaps); @@ -1280,10 +1292,13 @@ static void R_RenderSegLoop (void) // single sided line if (yl <= yh && yh >= 0 && yl < viewheight) { + fixed_t offset = texturecolumn + rw_offsetx; + dc_yl = yl; dc_yh = yh; dc_texturemid = rw_midtexturemid; - dc_source = R_GetColumn(midtexture, itexturecolumn + (rw_offset_mid>>FRACBITS)); + dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_midtexturescaley); + dc_source = R_GetColumn(midtexture, offset >> FRACBITS); dc_texheight = textureheight[midtexture]>>FRACBITS; colfunc(); @@ -1319,6 +1334,8 @@ static void R_RenderSegLoop (void) if (mid >= floorclip[rw_x]) mid = floorclip[rw_x]-1; + toptexturecolumn = FixedDiv(textureoffset, rw_invtoptexturescalex); + if (mid >= yl) // back ceiling lower than front ceiling ? { if (yl >= viewheight) // entirely off bottom of screen @@ -1328,10 +1345,16 @@ static void R_RenderSegLoop (void) } else if (mid >= 0) // safe to draw top texture { + fixed_t offset = rw_offset_top; + if (rw_toptexturescalex < 0) + offset = -offset; + offset = toptexturecolumn + offset; + dc_yl = yl; dc_yh = mid; dc_texturemid = rw_toptexturemid; - dc_source = R_GetColumn(toptexture, itexturecolumn + (rw_offset_top>>FRACBITS)); + dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_toptexturescaley); + dc_source = R_GetColumn(toptexture, offset >> FRACBITS); dc_texheight = textureheight[toptexture]>>FRACBITS; colfunc(); ceilingclip[rw_x] = (INT16)mid; @@ -1341,6 +1364,10 @@ static void R_RenderSegLoop (void) } else if (!rw_ceilingmarked) ceilingclip[rw_x] = topclip; + + if (oldtexturecolumn_top != -1) + rw_toptexturemid += FixedMul(rw_toptextureslide, oldtexturecolumn_top-toptexturecolumn); + oldtexturecolumn_top = toptexturecolumn; } else if (markceiling && (!rw_ceilingmarked)) // no top wall ceilingclip[rw_x] = topclip; @@ -1355,6 +1382,8 @@ static void R_RenderSegLoop (void) if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x]+1; + bottomtexturecolumn = FixedDiv(textureoffset, rw_invbottomtexturescalex); + if (mid <= yh) // back floor higher than front floor ? { if (yh < 0) // entirely off top of screen @@ -1364,10 +1393,16 @@ static void R_RenderSegLoop (void) } else if (mid < viewheight) // safe to draw bottom texture { + fixed_t offset = rw_offset_bottom; + if (rw_bottomtexturescalex < 0) + offset = -offset; + offset = bottomtexturecolumn + offset; + dc_yl = mid; dc_yh = yh; dc_texturemid = rw_bottomtexturemid; - dc_source = R_GetColumn(bottomtexture, itexturecolumn + (rw_offset_bot>>FRACBITS)); + dc_iscale = FixedMul(0xffffffffu / (unsigned)rw_scale, rw_bottomtexturescaley); + dc_source = R_GetColumn(bottomtexture, offset >> FRACBITS); dc_texheight = textureheight[bottomtexture]>>FRACBITS; colfunc(); floorclip[rw_x] = (INT16)mid; @@ -1377,6 +1412,10 @@ static void R_RenderSegLoop (void) } else if (!rw_floormarked) floorclip[rw_x] = bottomclip; + + if (oldtexturecolumn_bottom != -1) + rw_bottomtexturemid += FixedMul(rw_bottomtextureslide, oldtexturecolumn_bottom-bottomtexturecolumn); + oldtexturecolumn_bottom = bottomtexturecolumn; } else if (markfloor && (!rw_floormarked)) // no bottom wall floorclip[rw_x] = bottomclip; @@ -1388,18 +1427,34 @@ static void R_RenderSegLoop (void) didsolidcol = true; } - if (maskedtexture || numthicksides) - { - // save texturecol for backdrawing of masked mid texture - maskedtexturecol[rw_x] = texturecolumn + rw_offset_mid; + if (maskedtexturecol) + maskedtexturecol[rw_x] = texturecolumn + rw_offsetx; - if (maskedtextureheight != NULL) { - maskedtextureheight[rw_x] = (curline->linedef->flags & ML_MIDPEG) ? - max(rw_midtexturemid, rw_midtextureback) : - min(rw_midtexturemid, rw_midtextureback); - } + if (thicksidecol) + thicksidecol[rw_x] = textureoffset; + + if (maskedtextureheight) + { + if (curline->linedef->flags & ML_MIDPEG) + maskedtextureheight[rw_x] = max(rw_midtexturemid, rw_midtextureback); + else + maskedtextureheight[rw_x] = min(rw_midtexturemid, rw_midtextureback); } + if (midtexture || maskedtextureheight) + { + if (oldtexturecolumn != -1) + { + rw_midtexturemid += FixedMul(rw_midtextureslide, oldtexturecolumn-texturecolumn); + rw_midtextureback += FixedMul(rw_midtexturebackslide, oldtexturecolumn-texturecolumn); + } + + oldtexturecolumn = texturecolumn; + } + + if (invscale) + invscale[rw_x] = 0xffffffffu / (unsigned)rw_scale; + if (dc_numlights) { for (i = 0; i < dc_numlights; i++) @@ -1517,10 +1572,9 @@ static void R_AllocClippingTables(size_t range) static void R_AllocTextureColumnTables(size_t range) { size_t pos = curtexturecolumntable - texturecolumntable; + size_t need = range * 3; - // For both tables, we reserve exactly an amount of memory that's equivalent to - // how many columns the seg will take on the entire screen (think about it) - if (pos + range < texturecolumntablesize) + if (pos + need < texturecolumntablesize) return; fixed_t *oldtable = texturecolumntable; @@ -1529,7 +1583,7 @@ static void R_AllocTextureColumnTables(size_t range) if (texturecolumntablesize == 0) texturecolumntablesize = 16384; - texturecolumntablesize += range; + texturecolumntablesize += need; texturecolumntable = Z_Realloc(texturecolumntable, texturecolumntablesize * sizeof (*texturecolumntable), PU_STATIC, NULL); curtexturecolumntable = texturecolumntable + pos; @@ -1543,6 +1597,8 @@ static void R_AllocTextureColumnTables(size_t range) ds->maskedtexturecol = (ds->maskedtexturecol - oldtable) + texturecolumntable; if (ds->thicksidecol + ds->x1 >= oldtable && ds->thicksidecol + ds->x1 <= oldlast) ds->thicksidecol = (ds->thicksidecol - oldtable) + texturecolumntable; + if (ds->invscale + ds->x1 >= oldtable && ds->invscale + ds->x1 <= oldlast) + ds->invscale = (ds->invscale - oldtable) + texturecolumntable; } } @@ -1565,7 +1621,11 @@ void R_StoreWallRange(INT32 start, INT32 stop) vertex_t segleft, segright; fixed_t ceilingfrontslide, floorfrontslide, ceilingbackslide, floorbackslide; + maskedtexturecol = NULL; maskedtextureheight = NULL; + thicksidecol = NULL; + invscale = NULL; + //initialize segleft and segright memset(&segleft, 0x00, sizeof(segleft)); memset(&segright, 0x00, sizeof(segright)); @@ -1723,6 +1783,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) ds_p->maskedtexturecol = NULL; ds_p->numthicksides = numthicksides = 0; ds_p->thicksidecol = NULL; + ds_p->invscale = NULL; ds_p->tsilheight = 0; numbackffloors = 0; @@ -1762,32 +1823,67 @@ void R_StoreWallRange(INT32 start, INT32 stop) ceilingbackslide = FixedMul(backsector->c_slope->zdelta, FINECOSINE((lineangle-backsector->c_slope->xydirection)>>ANGLETOFINESHIFT)); } + rw_midtexturescalex = sidedef->scalex_mid; + rw_midtexturescaley = sidedef->scaley_mid; + rw_invmidtexturescalex = FixedDiv(FRACUNIT, rw_midtexturescalex); + if (!backsector) { - fixed_t texheight; - // single sided line midtexture = R_GetTextureNum(sidedef->midtexture); - texheight = textureheight[midtexture]; + // a single sided line is terminal, so it must mark ends markfloor = markceiling = true; - if (linedef->flags & ML_NOSKEW) { - if (linedef->flags & ML_DONTPEGBOTTOM) - rw_midtexturemid = frontsector->floorheight + texheight - viewz; - else - rw_midtexturemid = frontsector->ceilingheight - viewz; - } - else if (linedef->flags & ML_DONTPEGBOTTOM) + + fixed_t rowoffset = sidedef->rowoffset + sidedef->offsety_mid; + fixed_t texheight = textureheight[midtexture]; + + if (rw_midtexturescaley > 0) { - rw_midtexturemid = worldbottom + texheight; - rw_midtextureslide = floorfrontslide; + if (linedef->flags & ML_NOSKEW) + { + if (linedef->flags & ML_DONTPEGBOTTOM) + rw_midtexturemid = FixedMul(frontsector->floorheight - viewz, rw_midtexturescaley) + texheight; + else + rw_midtexturemid = FixedMul(frontsector->ceilingheight - viewz, rw_midtexturescaley); + } + else if (linedef->flags & ML_DONTPEGBOTTOM) + { + rw_midtexturemid = FixedMul(worldbottom, rw_midtexturescaley) + texheight; + rw_midtextureslide = floorfrontslide; + } + else + { + // top of texture at top + rw_midtexturemid = FixedMul(worldtop, rw_midtexturescaley); + rw_midtextureslide = ceilingfrontslide; + } } else { - // top of texture at top - rw_midtexturemid = worldtop; - rw_midtextureslide = ceilingfrontslide; + // Upside down + rowoffset = -rowoffset; + + if (linedef->flags & ML_NOSKEW) + { + if (linedef->flags & ML_DONTPEGBOTTOM) + rw_midtexturemid = FixedMul(frontsector->floorheight - viewz, rw_midtexturescaley); + else + rw_midtexturemid = FixedMul(frontsector->ceilingheight - viewz, rw_midtexturescaley) + texheight; + } + else if (linedef->flags & ML_DONTPEGBOTTOM) + { + rw_midtexturemid = FixedMul(worldbottom, rw_midtexturescaley); + rw_midtextureslide = floorfrontslide; + } + else + { + // top of texture at top + rw_midtexturemid = FixedMul(worldtop, rw_midtexturescaley) + texheight; + rw_midtextureslide = ceilingfrontslide; + } } - rw_midtexturemid += sidedef->rowoffset + sidedef->offsety_mid; + + rw_midtexturemid += rowoffset; ds_p->silhouette = SIL_BOTH; ds_p->sprtopclip = screenheightarray; @@ -1798,9 +1894,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) else { // two sided line - boolean bothceilingssky = false; // turned on if both back and front ceilings are sky - boolean bothfloorssky = false; // likewise, but for floors - SLOPEPARAMS(backsector->c_slope, worldhigh, worldhighslope, backsector->ceilingheight) SLOPEPARAMS(backsector->f_slope, worldlow, worldlowslope, backsector->floorheight) worldhigh -= viewz; @@ -1808,21 +1901,6 @@ void R_StoreWallRange(INT32 start, INT32 stop) worldlow -= viewz; worldlowslope -= viewz; - // hack to allow height changes in outdoor areas - // This is what gets rid of the upper textures if there should be sky - if (frontsector->ceilingpic == skyflatnum - && backsector->ceilingpic == skyflatnum) - { - bothceilingssky = true; - } - - // likewise, but for floors and upper textures - if (frontsector->floorpic == skyflatnum - && backsector->floorpic == skyflatnum) - { - bothfloorssky = true; - } - ds_p->sprtopclip = ds_p->sprbottomclip = NULL; ds_p->silhouette = 0; @@ -1912,6 +1990,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) //SoM: 3/22/2000: Check floor x and y offsets. || backsector->floorxoffset != frontsector->floorxoffset || backsector->flooryoffset != frontsector->flooryoffset + || backsector->floorxscale != frontsector->floorxscale + || backsector->flooryscale != frontsector->flooryscale || backsector->floorangle != frontsector->floorangle //SoM: 3/22/2000: Prevents bleeding. || (frontsector->heightsec != -1 && frontsector->floorpic != skyflatnum) @@ -1920,6 +2000,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) || backsector->floorlightsec != frontsector->floorlightsec //SoM: 4/3/2000: Check for colormaps || frontsector->extra_colormap != backsector->extra_colormap + || !P_CompareSectorPortals(P_SectorGetFloorPortal(frontsector), P_SectorGetFloorPortal(backsector)) || (frontsector->ffloors != backsector->ffloors && !Tag_Compare(&frontsector->tags, &backsector->tags))) { markfloor = true; @@ -1945,6 +2026,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) //SoM: 3/22/2000: Check floor x and y offsets. || backsector->ceilingxoffset != frontsector->ceilingxoffset || backsector->ceilingyoffset != frontsector->ceilingyoffset + || backsector->ceilingxscale != frontsector->ceilingxscale + || backsector->ceilingyscale != frontsector->ceilingyscale || backsector->ceilingangle != frontsector->ceilingangle //SoM: 3/22/2000: Prevents bleeding. || (frontsector->heightsec != -1 && frontsector->ceilingpic != skyflatnum) @@ -1953,9 +2036,10 @@ void R_StoreWallRange(INT32 start, INT32 stop) || backsector->ceilinglightsec != frontsector->ceilinglightsec //SoM: 4/3/2000: Check for colormaps || frontsector->extra_colormap != backsector->extra_colormap + || !P_CompareSectorPortals(P_SectorGetCeilingPortal(frontsector), P_SectorGetCeilingPortal(backsector)) || (frontsector->ffloors != backsector->ffloors && !Tag_Compare(&frontsector->tags, &backsector->tags))) { - markceiling = true; + markceiling = true; } else { @@ -1973,16 +2057,28 @@ void R_StoreWallRange(INT32 start, INT32 stop) } } + fixed_t toprowoffset = sidedef->rowoffset + sidedef->offsety_top; + fixed_t botrowoffset = sidedef->rowoffset + sidedef->offsety_bottom; + // check TOP TEXTURE if (!bothceilingssky // never draw the top texture if on && (worldhigh < worldtop || worldhighslope < worldtopslope)) { - fixed_t texheight; - // top texture toptexture = R_GetTextureNum(sidedef->toptexture); - texheight = textureheight[toptexture]; - if (!(linedef->flags & ML_SKEWTD)) { // Ignore slopes for lower/upper textures unless flag is checked + rw_toptexturescalex = sidedef->scalex_top; + rw_toptexturescaley = sidedef->scaley_top; + + rw_invtoptexturescalex = FixedDiv(FRACUNIT, rw_toptexturescalex); + + if (rw_toptexturescaley < 0) + toprowoffset = -toprowoffset; + + fixed_t texheight = textureheight[toptexture]; + + // Ignore slopes for lower/upper textures unless flag is checked + if (!(linedef->flags & ML_SKEWTD)) + { if (linedef->flags & ML_DONTPEGTOP) rw_toptexturemid = frontsector->ceilingheight - viewz; else @@ -1999,7 +2095,10 @@ void R_StoreWallRange(INT32 start, INT32 stop) rw_toptexturemid = worldhigh + texheight; rw_toptextureslide = ceilingbackslide; } + + rw_toptexturemid = FixedMul(rw_toptexturemid, rw_toptexturescaley); } + // check BOTTOM TEXTURE if (!bothfloorssky // never draw the bottom texture if on && (worldlow > worldbottom || worldlowslope > worldbottomslope)) // Only if VISIBLE!!! @@ -2007,7 +2106,17 @@ void R_StoreWallRange(INT32 start, INT32 stop) // bottom texture bottomtexture = R_GetTextureNum(sidedef->bottomtexture); - if (!(linedef->flags & ML_SKEWTD)) { // Ignore slopes for lower/upper textures unless flag is checked + rw_bottomtexturescalex = sidedef->scalex_bottom; + rw_bottomtexturescaley = sidedef->scaley_bottom; + + rw_invbottomtexturescalex = FixedDiv(FRACUNIT, rw_bottomtexturescalex); + + if (rw_bottomtexturescaley < 0) + botrowoffset = -botrowoffset; + + // Ignore slopes for lower/upper textures unless flag is checked + if (!(linedef->flags & ML_SKEWTD)) + { if (linedef->flags & ML_DONTPEGBOTTOM) rw_bottomtexturemid = frontsector->floorheight - viewz; else @@ -2020,18 +2129,22 @@ void R_StoreWallRange(INT32 start, INT32 stop) rw_bottomtexturemid = worldbottom; rw_bottomtextureslide = floorfrontslide; } - else { // top of texture at top + else + { + // top of texture at top rw_bottomtexturemid = worldlow; rw_bottomtextureslide = floorbackslide; } + + rw_bottomtexturemid = FixedMul(rw_bottomtexturemid, rw_bottomtexturescaley); } - rw_toptexturemid += sidedef->rowoffset + sidedef->offsety_top; - rw_bottomtexturemid += sidedef->rowoffset + sidedef->offsety_bot; - - R_AllocTextureColumnTables(rw_stopx - start); + rw_toptexturemid += toprowoffset; + rw_bottomtexturemid += botrowoffset; // allocate space for masked texture tables + R_AllocTextureColumnTables(rw_stopx - start); + if (frontsector && backsector && !Tag_Compare(&frontsector->tags, &backsector->tags) && (backsector->ffloors || frontsector->ffloors)) { ffloor_t *rover; @@ -2042,10 +2155,9 @@ void R_StoreWallRange(INT32 start, INT32 stop) // Used for height comparisons and etc across FOFs and slopes fixed_t high1, highslope1, low1, lowslope1, high2, highslope2, low2, lowslope2; - //markceiling = markfloor = true; maskedtexture = true; - ds_p->thicksidecol = maskedtexturecol = curtexturecolumntable - rw_x; + ds_p->thicksidecol = thicksidecol = curtexturecolumntable - rw_x; curtexturecolumntable += rw_stopx - rw_x; lowcut = max(worldbottom, worldlow) + viewz; @@ -2224,21 +2336,20 @@ void R_StoreWallRange(INT32 start, INT32 stop) ds_p->numthicksides = numthicksides = i; } + + // masked midtexture if (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) { - // masked midtexture - if (!ds_p->thicksidecol) - { - ds_p->maskedtexturecol = maskedtexturecol = curtexturecolumntable - rw_x; - curtexturecolumntable += rw_stopx - rw_x; - } - else - ds_p->maskedtexturecol = ds_p->thicksidecol; + ds_p->maskedtexturecol = maskedtexturecol = curtexturecolumntable - rw_x; + curtexturecolumntable += rw_stopx - rw_x; maskedtextureheight = ds_p->maskedtextureheight; // note to red, this == &(ds_p->maskedtextureheight[0]) + maskedtexture = true; + if (curline->polyseg) - { // use REAL front and back floors please, so midtexture rendering isn't mucked up + { + // use REAL front and back floors please, so midtexture rendering isn't mucked up rw_midtextureslide = rw_midtexturebackslide = 0; if (linedef->flags & ML_MIDPEG) rw_midtexturemid = rw_midtextureback = max(curline->frontsector->floorheight, curline->backsector->floorheight) - viewz; @@ -2249,7 +2360,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) { // Set midtexture starting height if (linedef->flags & ML_NOSKEW) - { // Ignore slopes when texturing + { + // Ignore slopes when texturing rw_midtextureslide = rw_midtexturebackslide = 0; if (linedef->flags & ML_MIDPEG) rw_midtexturemid = rw_midtextureback = max(frontsector->floorheight, backsector->floorheight) - viewz; @@ -2272,6 +2384,10 @@ void R_StoreWallRange(INT32 start, INT32 stop) rw_midtexturebackslide = ceilingbackslide; } } + + rw_midtexturemid = FixedMul(rw_midtexturemid, rw_midtexturescaley); + rw_midtextureback = FixedMul(rw_midtextureback, rw_midtexturescaley); + rw_midtexturemid += sidedef->rowoffset + sidedef->offsety_mid; rw_midtextureback += sidedef->rowoffset + sidedef->offsety_mid; @@ -2284,6 +2400,8 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (segtextured) { + fixed_t sideoffset = sidedef->textureoffset; + offsetangle = rw_normalangle-rw_angle1; if (offsetangle > ANGLE_180) @@ -2308,14 +2426,20 @@ void R_StoreWallRange(INT32 start, INT32 stop) if (rw_normalangle-rw_angle1 < ANGLE_180) rw_offset = -rw_offset; - /// don't use texture offset for splats - rw_offset2 = rw_offset + curline->offset; - rw_offset += sidedef->textureoffset + curline->offset; - rw_offset_top = sidedef->offsetx_top; - rw_offset_mid = sidedef->offsetx_mid; - rw_offset_bot = sidedef->offsetx_bot; + rw_offset += curline->offset; rw_centerangle = ANGLE_90 + viewangle - rw_normalangle; + rw_offset_top = sideoffset + sidedef->offsetx_top; + rw_offset_mid = sideoffset + sidedef->offsetx_mid; + rw_offset_bottom = sideoffset + sidedef->offsetx_bottom; + + rw_offsetx = rw_offset_mid; + if (rw_midtexturescalex < 0) + rw_offsetx = -rw_offsetx; + + if (numthicksides) + ds_p->offsetx = rw_offsetx; + // calculate light table // use different light tables // for horizontal / vertical / diagonal @@ -2335,6 +2459,12 @@ void R_StoreWallRange(INT32 start, INT32 stop) walllights = scalelight[lightnum]; } + if (maskedtexture) + { + ds_p->invscale = invscale = curtexturecolumntable - rw_x; + curtexturecolumntable += rw_stopx - rw_x; + } + // if a floor / ceiling plane is on the wrong side // of the view plane, it is definitely invisible // and doesn't need to be marked. @@ -2359,7 +2489,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) worldtopslope >>= 4; worldbottomslope >>= 4; - if (linedef->special == HORIZONSPECIAL) { // HORIZON LINES + if (horizonline) { // HORIZON LINES topstep = bottomstep = 0; topfrac = bottomfrac = (centeryfrac>>4); topfrac++; // Prevent 1px HOM @@ -2460,7 +2590,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) { ffloor[i].f_pos >>= 4; ffloor[i].f_pos_slope >>= 4; - if (linedef->special == HORIZONSPECIAL) // Horizon lines extend FOFs in contact with them too. + if (horizonline) // Horizon lines extend FOFs in contact with them too. { ffloor[i].f_step = 0; ffloor[i].f_frac = (centeryfrac>>4); diff --git a/src/r_skins.c b/src/r_skins.c index 308cee8d6..6b4aeefe1 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -18,6 +18,7 @@ #include "st_stuff.h" #include "w_wad.h" #include "z_zone.h" +#include "m_menu.h" #include "m_misc.h" #include "info.h" // spr2names #include "i_video.h" // rendermode @@ -32,13 +33,7 @@ #endif INT32 numskins = 0; -skin_t skins[MAXSKINS]; - -// FIXTHIS: don't work because it must be inistilised before the config load -//#define SKINVALUES -#ifdef SKINVALUES -CV_PossibleValue_t skin_cons_t[MAXSKINS+1]; -#endif +skin_t **skins = NULL; // // P_GetSkinSprite2 @@ -105,7 +100,7 @@ static void Sk_SetDefaultValue(skin_t *skin) // memset(skin, 0, sizeof (skin_t)); snprintf(skin->name, - sizeof skin->name, "skin %u", (UINT32)(skin-skins)); + sizeof skin->name, "skin %u", (UINT32)(skin->skinnum)); skin->name[sizeof skin->name - 1] = '\0'; skin->wadnum = INT16_MAX; @@ -149,6 +144,8 @@ static void Sk_SetDefaultValue(skin_t *skin) skin->contspeed = 17; skin->contangle = 0; + skin->natkcolor = SKINCOLOR_NONE; + for (i = 0; i < sfx_skinsoundslot0; i++) if (S_sfx[i].skinsound != -1) skin->soundsid[S_sfx[i].skinsound] = i; @@ -159,16 +156,6 @@ static void Sk_SetDefaultValue(skin_t *skin) // void R_InitSkins(void) { -#ifdef SKINVALUES - INT32 i; - - for (i = 0; i <= MAXSKINS; i++) - { - skin_cons_t[i].value = 0; - skin_cons_t[i].strvalue = NULL; - } -#endif - // no default skin! numskins = 0; } @@ -291,7 +278,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum) } } -// returns true if the skin name is found (loaded from pwad) +// returns the skin number if the skin name is found (loaded from pwad) // warning return -1 if not found INT32 R_SkinAvailable(const char *name) { @@ -300,7 +287,7 @@ INT32 R_SkinAvailable(const char *name) for (i = 0; i < numskins; i++) { // search in the skin list - if (stricmp(skins[i].name,name)==0) + if (!stricmp(skins[i]->name,name)) return i; } return -1; @@ -324,7 +311,7 @@ INT32 R_GetForcedSkin(INT32 playernum) // Auxillary function that actually sets the skin static void SetSkin(player_t *player, INT32 skinnum) { - skin_t *skin = &skins[skinnum]; + skin_t *skin = skins[skinnum]; UINT16 newcolor = 0; player->skin = skinnum; @@ -381,7 +368,7 @@ static void SetSkin(player_t *player, INT32 skinnum) fixed_t radius = FixedMul(skin->radius, player->mo->scale); if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (skin->sprites[SPR2_NFLY].numframes == 0)) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin. { - skin = &skins[DEFAULTNIGHTSSKIN]; + skin = skins[DEFAULTNIGHTSSKIN]; player->followitem = skin->followitem; if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) newcolor = skin->prefcolor; // will be updated in thinker to flashing @@ -392,7 +379,7 @@ static void SetSkin(player_t *player, INT32 skinnum) P_SetScale(player->mo, player->mo->scale); player->mo->radius = radius; - P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames + P_SetMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames } } @@ -603,7 +590,6 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) UINT16 color = R_GetSuperColorByName(value); skin->supercolor = (color ? color : SKINCOLOR_SUPERGOLD1); } - #define GETFLOAT(field) else if (!stricmp(stoken, #field)) skin->field = FLOAT_TO_FIXED(atof(value)); GETFLOAT(jumpfactor) GETFLOAT(highresscale) @@ -644,6 +630,9 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETFLAG(NOSHIELDABILITY) #undef GETFLAG + else if (!stricmp(stoken, "natkcolor")) + skin->natkcolor = R_GetColorByName(value); // SKINCOLOR_NONE is allowed here + else // let's check if it's a sound, otherwise error out { boolean found = false; @@ -721,8 +710,10 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile) buf2[size] = '\0'; // set defaults - skin = &skins[numskins]; + skins = Z_Realloc(skins, sizeof(skin_t*) * (numskins + 1), PU_STATIC, NULL); + skin = skins[numskins] = Z_Calloc(sizeof(skin_t), PU_STATIC, NULL); Sk_SetDefaultValue(skin); + skin->skinnum = numskins; skin->wadnum = wadnum; hudname = realname = supername = false; // parse @@ -836,15 +827,6 @@ next_token: if (mainfile == false) CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name); -#ifdef SKINVALUES - skin_cons_t[numskins].value = numskins; - skin_cons_t[numskins].strvalue = skin->name; -#endif - -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_AddPlayerModel(numskins); -#endif numskins++; } @@ -915,7 +897,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile) strlwr(value); skinnum = R_SkinAvailable(value); if (skinnum != -1) - skin = &skins[skinnum]; + skin = skins[skinnum]; else { CONS_Debug(DBG_SETUP, "R_PatchSkins: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); @@ -1066,7 +1048,7 @@ static void R_RefreshSprite2ForWad(UINT16 wadnum, UINT8 start_spr2) strlwr(value); skinnum = R_SkinAvailable(value); if (skinnum != -1) - skin = &skins[skinnum]; + skin = skins[skinnum]; else { CONS_Debug(DBG_SETUP, "R_RefreshSprite2ForWad: unknown skin name in P_SKIN lump# %d(%s) in WAD %s\n", lump, W_CheckNameForNumPwad(wadnum,lump), wadfiles[wadnum]->filename); diff --git a/src/r_skins.h b/src/r_skins.h index bf2275a49..cc3b63cbe 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -31,7 +31,8 @@ /// The skin_t struct typedef struct { - char name[SKINNAMESIZE+1]; // INT16 descriptive name of the skin + char name[SKINNAMESIZE+1]; // name of the skin + UINT8 skinnum; UINT16 wadnum; skinflags_t flags; @@ -70,6 +71,7 @@ typedef struct UINT16 prefcolor; UINT16 supercolor; UINT16 prefoppositecolor; // if 0 use tables instead + UINT16 natkcolor; //Color for Nights Attack Menu fixed_t highresscale; // scale of highres, default is 0.5 UINT8 contspeed; // continue screen animation speed @@ -85,7 +87,7 @@ typedef struct /// Externs extern INT32 numskins; -extern skin_t skins[MAXSKINS]; +extern skin_t **skins; /// Function prototypes void R_InitSkins(void); diff --git a/src/r_splats.c b/src/r_splats.c index 4f284e14d..c5ef6433e 100644 --- a/src/r_splats.c +++ b/src/r_splats.c @@ -377,7 +377,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr ds_flatwidth = pSplat->width; ds_flatheight = pSplat->height; - ds_powersoftwo = ds_solidcolor = false; + ds_powersoftwo = ds_solidcolor = ds_fog = false; if (R_CheckSolidColorFlat()) ds_solidcolor = true; @@ -389,9 +389,7 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr if (pSplat->slope) { - R_SetTiltedSpan(0); R_SetScaledSlopePlane(pSplat->slope, vis->viewpoint.x, vis->viewpoint.y, vis->viewpoint.z, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewpoint.angle, pSplat->angle); - R_CalculateSlopeVectors(); } else if (!ds_solidcolor) { @@ -551,6 +549,8 @@ static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, visspr angle_t planecos = FINECOSINE(angle); angle_t planesin = FINESINE(angle); + // [RH] Notice that I dumped the caching scheme used by Doom. + // It did not offer any appreciable speedup. distance = FixedMul(planeheight, yslope[y]); span = abs(centery - y); diff --git a/src/r_things.c b/src/r_things.c index 393995191..80061c8a9 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -509,10 +509,6 @@ void R_AddSpriteDefs(UINT16 wadnum) if (R_AddSingleSpriteDef(sprnames[i], &sprites[i], wadnum, start, end)) { -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_AddSpriteModel(i); -#endif // if a new sprite was added (not just replaced) addsprites++; #ifndef ZDEBUG @@ -584,14 +580,10 @@ void R_InitSprites(void) } ST_ReloadSkinFaceGraphics(); - // - // check if all sprites have frames - // - /* - for (i = 0; i < numsprites; i++) - if (sprites[i].numframes < 1) - CONS_Debug(DBG_SETUP, "R_InitSprites: sprite %s has no frames at all\n", sprnames[i]); - */ +#ifdef HWRENDER + if (rendermode == render_opengl) + HWR_LoadModels(); +#endif } // @@ -822,8 +814,8 @@ UINT8 *R_GetSpriteTranslation(vissprite_t *vis) } else if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && vis->mobj->sprite == SPR_PLAY) // This thing is a player! { - size_t skinnum = (skin_t*)vis->mobj->skin-skins; - return R_GetTranslationColormap((INT32)skinnum, vis->color, GTC_CACHE); + UINT8 skinnum = ((skin_t*)vis->mobj->skin)->skinnum; + return R_GetTranslationColormap(skinnum, vis->color, GTC_CACHE); } else // Use the defaults return R_GetTranslationColormap(TC_DEFAULT, vis->color, GTC_CACHE); @@ -1655,6 +1647,8 @@ static void R_ProjectSprite(mobj_t *thing) height = interp.height; // Ditto // transform the origin point + if (thing->type == MT_OVERLAY) // Handle overlays + R_ThingOffsetOverlay(thing, &interp.x, &interp.y); tr_x = interp.x - viewx; tr_y = interp.y - viewy; @@ -2000,6 +1994,8 @@ static void R_ProjectSprite(mobj_t *thing) radius = tracer_interp.radius; // For drop shadows height = tracer_interp.height; // Ditto + if (thing->type == MT_OVERLAY) // Handle overlays + R_ThingOffsetOverlay(thing, &tracer_interp.x, &tracer_interp.y); tr_x = (tracer_interp.x + sort_x) - viewx; tr_y = (tracer_interp.y + sort_y) - viewy; tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); @@ -3594,6 +3590,50 @@ boolean R_ThingIsFullDark(mobj_t *thing) return ((thing->frame & FF_BRIGHTMASK) == FF_FULLDARK || (thing->renderflags & RF_BRIGHTMASK) == RF_FULLDARK); } +// Offsets MT_OVERLAY towards the camera at render-time - Works in splitscreen! +// The &x and &y arguments should be pre-interpolated, and will be modified +void R_ThingOffsetOverlay(mobj_t *thing, fixed_t *x, fixed_t *y) +{ + mobj_t *mobj = thing; + INT16 offset = 0; // Offset towards or away from the camera, and how much + fixed_t offsetscale = thing->scale; // Occasionally needs to be interpolated + angle_t viewingangle; + UINT8 looplimit = 255; // Prevent infinite loops - A chain of 255 connected overlays is enough for any sane use case + +#ifdef PARANOIA + if (P_MobjWasRemoved(mobj) || !x || !y) + I_Error("R_ThingOffsetOverlay: thing, x, or y is invalid"); +#endif + + do // Get the overlay's offset + { + // Does the overlay use FF_ANIMATE? If not, if var1 is non-zero, it's an underlay instead of an overlay + if (!(mobj->state->frame & FF_ANIMATE) && mobj->state->var1) + offset += 1; // Underlay below the target, away from the camera + else + offset -= 1; // Overlay on top of the target, towards the camera + + looplimit -= 1; + mobj = mobj->target; + } while (!P_MobjWasRemoved(mobj) && mobj->type == MT_OVERLAY && looplimit > 0); // Handle overlays following other overlays + + // Does the offset scale need to be interpolated? + if (thing->scale != thing->old_scale && R_UsingFrameInterpolation() && !paused) + { + interpmobjstate_t interp = {0}; + R_InterpolateMobjState(thing, rendertimefrac, &interp); + offsetscale = interp.scale; + } + + + // Get the angle from the camera to the X and Y coordinates + viewingangle = R_PointToAngle(*x, *y); + + // Finally, offset the X and Y coordinates towards or away from the camera + *x += P_ReturnThrustX(thing, viewingangle, FixedMul(offset * (FRACUNIT/4), offsetscale)); + *y += P_ReturnThrustY(thing, viewingangle, FixedMul(offset * (FRACUNIT/4), offsetscale)); +} + // // R_DrawMasked // diff --git a/src/r_things.h b/src/r_things.h index f849e07d5..bcaea276a 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -88,6 +88,8 @@ boolean R_ThingIsFullBright (mobj_t *thing); boolean R_ThingIsSemiBright (mobj_t *thing); boolean R_ThingIsFullDark (mobj_t *thing); +void R_ThingOffsetOverlay (mobj_t *thing, fixed_t *outx, fixed_t *outy); + // -------------- // MASKED DRAWING // -------------- diff --git a/src/screen.c b/src/screen.c index b7a488487..1a1450f5d 100644 --- a/src/screen.c +++ b/src/screen.c @@ -115,14 +115,6 @@ INT32 scr_bpp; // current video mode bytes per pixel // ========================================================================= -boolean R_486 = false; -boolean R_586 = false; -boolean R_MMX = false; -boolean R_SSE = false; -boolean R_3DNow = false; -boolean R_MMXExt = false; -boolean R_SSE2 = false; - void SCR_SetDrawFuncs(void) { colfuncs[BASEDRAWFUNC] = R_DrawColumn_8; @@ -207,48 +199,6 @@ void SCR_SetMode(void) // void SCR_Startup(void) { - const CPUInfoFlags *RCpuInfo = I_CPUInfo(); - if (!M_CheckParm("-NOCPUID") && RCpuInfo) - { -#if defined (__i386__) || defined (_M_IX86) || defined (__WATCOMC__) - R_486 = true; -#endif - if (RCpuInfo->RDTSC) - R_586 = true; - if (RCpuInfo->MMX) - R_MMX = true; - if (RCpuInfo->AMD3DNow) - R_3DNow = true; - if (RCpuInfo->MMXExt) - R_MMXExt = true; - if (RCpuInfo->SSE) - R_SSE = true; - if (RCpuInfo->SSE2) - R_SSE2 = true; - CONS_Printf("CPU Info: 486: %i, 586: %i, MMX: %i, 3DNow: %i, MMXExt: %i, SSE2: %i\n", R_486, R_586, R_MMX, R_3DNow, R_MMXExt, R_SSE2); - } - - if (M_CheckParm("-486")) - R_486 = true; - if (M_CheckParm("-586")) - R_586 = true; - if (M_CheckParm("-MMX")) - R_MMX = true; - if (M_CheckParm("-3DNow")) - R_3DNow = true; - if (M_CheckParm("-MMXExt")) - R_MMXExt = true; - - if (M_CheckParm("-SSE")) - R_SSE = true; - if (M_CheckParm("-noSSE")) - R_SSE = false; - - if (M_CheckParm("-SSE2")) - R_SSE2 = true; - - M_SetupMemcpy(); - if (dedicated) { V_Init(); diff --git a/src/screen.h b/src/screen.h index 7c4843bf2..268a8a379 100644 --- a/src/screen.h +++ b/src/screen.h @@ -148,16 +148,6 @@ extern void (*spanfunc)(void); extern void (*spanfuncs[SPANDRAWFUNC_MAX])(void); extern void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void); -// ----- -// CPUID -// ----- -extern boolean R_486; -extern boolean R_586; -extern boolean R_MMX; -extern boolean R_3DNow; -extern boolean R_MMXExt; -extern boolean R_SSE2; - // ---------------- // screen variables // ---------------- diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 450237149..847806270 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -41,6 +41,12 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); #include #undef SystemFunction036 +// A little more than the minimum sleep duration on Windows. +// May be incorrect for other platforms, but we don't currently have a way to +// query the scheduler granularity. SDL will do what's needed to make this as +// low as possible though. +#define MIN_SLEEP_DURATION_MS 2.1 + #endif #include #include @@ -138,7 +144,9 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); #endif #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) +#ifndef NOEXECINFO #include +#endif #include #define UNIXBACKTRACE #endif @@ -269,13 +277,17 @@ UINT8 keyboard_started = false; static void write_backtrace(INT32 signal) { int fd = -1; +#ifndef NOEXECINFO size_t size; +#endif time_t rawtime; struct tm timeinfo; ssize_t junk; enum { BT_SIZE = 1024, STR_SIZE = 32 }; +#ifndef NOEXECINFO void *array[BT_SIZE]; +#endif char timestr[STR_SIZE]; const char *error = "An error occurred within SRB2! Send this stack trace to someone who can help!\n"; @@ -308,12 +320,14 @@ static void write_backtrace(INT32 signal) CRASHLOG_WRITE(strsignal(signal)); CRASHLOG_WRITE("\n"); // Newline for the signal name +#ifndef NOEXECINFO CRASHLOG_STDERR_WRITE("\nBacktrace:\n"); // Flood the output and log with the backtrace size = backtrace(array, BT_SIZE); backtrace_symbols_fd(array, size, fd); backtrace_symbols_fd(array, size, STDERR_FILENO); +#endif CRASHLOG_WRITE("\n"); // Write another newline to the log so it looks nice :) (void)junk; @@ -638,6 +652,7 @@ void I_GetConsoleEvents(void) else if (tty_con.cursor < sizeof (tty_con.buffer)) { // push regular character + ev.type = ev_text; ev.key = tty_con.buffer[tty_con.cursor] = key; tty_con.cursor++; // print the current line (this is differential) @@ -2266,6 +2281,52 @@ void I_Sleep(UINT32 ms) SDL_Delay(ms); } +void I_SleepDuration(precise_t duration) +{ +#if defined(__linux__) || defined(__FreeBSD__) + UINT64 precision = I_GetPrecisePrecision(); + struct timespec ts = { + .tv_sec = duration / precision, + .tv_nsec = duration * 1000000000 / precision % 1000000000, + }; + int status; + do status = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts); + while (status == EINTR); +#else + UINT64 precision = I_GetPrecisePrecision(); + INT32 sleepvalue = cv_sleep.value; + UINT64 delaygranularity; + precise_t cur; + precise_t dest; + + { + double gran = round(((double)(precision / 1000) * sleepvalue * MIN_SLEEP_DURATION_MS)); + delaygranularity = (UINT64)gran; + } + + cur = I_GetPreciseTime(); + dest = cur + duration; + + // the reason this is not dest > cur is because the precise counter may wrap + // two's complement arithmetic is our friend here, though! + // e.g. cur 0xFFFFFFFFFFFFFFFE = -2, dest 0x0000000000000001 = 1 + // 0x0000000000000001 - 0xFFFFFFFFFFFFFFFE = 3 + while ((INT64)(dest - cur) > 0) + { + // If our cv_sleep value exceeds the remaining sleep duration, use the + // hard sleep function. + if (sleepvalue > 0 && (dest - cur) > delaygranularity) + { + I_Sleep(sleepvalue); + } + + // Otherwise, this is a spinloop. + + cur = I_GetPreciseTime(); + } +#endif +} + #ifdef NEWSIGNALHANDLER ATTRNORETURN static FUNCNORETURN void newsignalhandler_Warn(const char *pr) { diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index a13d17240..459f3f207 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -543,10 +543,8 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code) return 0; } -static boolean IgnoreMouse(void) +static boolean ShouldIgnoreMouse(void) { - if (cv_alwaysgrabmouse.value) - return false; if (menuactive) return !M_MouseNeeded(); if (paused || con_destlines || chat_on) @@ -554,11 +552,20 @@ static boolean IgnoreMouse(void) if (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION && gamestate != GS_CONTINUING && gamestate != GS_CUTSCENE) return true; - if (!mousegrabbedbylua) - return true; return false; } +static boolean ShouldGrabMouse(void) +{ + if (cv_alwaysgrabmouse.value) + return true; + if (ShouldIgnoreMouse()) + return false; + if (!mousegrabbedbylua) + return false; + return true; +} + static void SDLdoGrabMouse(void) { SDL_ShowCursor(SDL_DISABLE); @@ -585,7 +592,7 @@ void I_UpdateMouseGrab(void) { if (SDL_WasInit(SDL_INIT_VIDEO) == SDL_INIT_VIDEO && window != NULL && SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window - && USE_MOUSEINPUT && !IgnoreMouse()) + && USE_MOUSEINPUT && ShouldGrabMouse()) SDLdoGrabMouse(); } @@ -758,7 +765,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) if (!firsttimeonmouse && cv_usemouse.value) I_StartupMouse(); - if (USE_MOUSEINPUT && !IgnoreMouse()) + if (USE_MOUSEINPUT && ShouldGrabMouse()) SDLdoGrabMouse(); } else if (!mousefocus && !kbfocus) @@ -796,13 +803,26 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type) if (event.key) D_PostEvent(&event); } +static void Impl_HandleTextEvent(SDL_TextInputEvent evt) +{ + event_t event; + event.type = ev_text; + if (evt.text[1] != '\0') + { + // limit ourselves to ASCII for now, we can add UTF-8 support later + return; + } + event.key = evt.text[0]; + D_PostEvent(&event); +} + static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt) { static boolean firstmove = true; if (USE_MOUSEINPUT) { - if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || (IgnoreMouse() && !firstmove)) + if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || (!ShouldGrabMouse() && !firstmove)) { SDLdoUngrabMouse(); firstmove = false; @@ -855,7 +875,7 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type) // this apparently makes a mouse button down event but not a mouse button up event, // resulting in whatever key was pressed down getting "stuck" if we don't ignore it. // -- Monster Iestyn (28/05/18) - if (SDL_GetMouseFocus() != window || IgnoreMouse()) + if (SDL_GetMouseFocus() != window || ShouldIgnoreMouse()) return; /// \todo inputEvent.button.which @@ -1071,6 +1091,9 @@ void I_GetEvent(void) case SDL_KEYDOWN: Impl_HandleKeyboardEvent(evt.key, evt.type); break; + case SDL_TEXTINPUT: + Impl_HandleTextEvent(evt.text); + break; case SDL_MOUSEMOTION: //if (!mouseMotionOnce) Impl_HandleMouseMotionEvent(evt.motion); @@ -1265,7 +1288,7 @@ void I_StartupMouse(void) } else firsttimeonmouse = SDL_FALSE; - if (cv_usemouse.value && !IgnoreMouse()) + if (cv_usemouse.value && ShouldGrabMouse()) SDLdoGrabMouse(); else SDLdoUngrabMouse(); diff --git a/src/sdl/macosx/Srb2mac.pbproj/project.pbxproj b/src/sdl/macosx/Srb2mac.pbproj/project.pbxproj index 909bb2ced..40f580be1 100644 --- a/src/sdl/macosx/Srb2mac.pbproj/project.pbxproj +++ b/src/sdl/macosx/Srb2mac.pbproj/project.pbxproj @@ -2133,7 +2133,7 @@ INSTALL_PATH = "$(HOME)/Applications"; JAVA_COMPILER_DEBUGGING_SYMBOLS = NO; OPTIMIZATION_CFLAGS = "-O2"; - OTHER_CFLAGS = "-DMAC_ALERT -DUNIXCOMMON -DSDLMAIN -DHAVE_MIXER -DHAVE_PNG -D_BIG_ENDIAN -DSTDC_HEADERS -DSDL -Wall -Winline -fno-strict-aliasing"; + OTHER_CFLAGS = "-DMAC_ALERT -DUNIXCOMMON -DSDLMAIN -DHAVE_MIXER -DHAVE_PNG -D_BIG_ENDIAN -DSTDC_HEADERS -DSDL -Wall -Winline -fno-strict-aliasing -fwrapv"; OTHER_REZFLAGS = ""; PREBINDING = NO; PRODUCT_NAME = Srb2; diff --git a/src/sounds.h b/src/sounds.h index 102881e99..bf9342768 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -43,7 +43,7 @@ typedef enum // free sfx for S_AddSoundFx() #define NUMSFXFREESLOTS 1600 // Matches SOC Editor. -#define NUMSKINSFXSLOTS (MAXSKINS*NUMSKINSOUNDS) +#define NUMSKINSFXSLOTS (128*NUMSKINSOUNDS) // // SoundFX struct. @@ -874,7 +874,7 @@ typedef enum // free slots for S_AddSoundFx() at run-time -------------------- sfx_freeslot0, // - // ... 60 free sounds here ... + // ... 1600 free sounds here ... // sfx_lastfreeslot = sfx_freeslot0 + NUMSFXFREESLOTS-1, // end of freeslots --------------------------------------------- diff --git a/src/st_stuff.c b/src/st_stuff.c index 6b8f25e6d..b52c7bf60 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -54,8 +54,8 @@ UINT16 objectsdrawn = 0; // STATUS BAR DATA // -patch_t *faceprefix[MAXSKINS]; // face status patches -patch_t *superprefix[MAXSKINS]; // super face status patches +patch_t **faceprefix; // face status patches +patch_t **superprefix; // super face status patches // ------------------------------------------ // status bar overlay @@ -135,8 +135,6 @@ static patch_t *gotrflag; static patch_t *gotbflag; static patch_t *fnshico; -static boolean facefreed[MAXPLAYERS]; - hudinfo_t hudinfo[NUMHUDITEMS] = { { 16, 176, V_SNAPTOLEFT|V_SNAPTOBOTTOM}, // HUD_LIVES @@ -364,14 +362,14 @@ void ST_LoadGraphics(void) // made separate so that skins code can reload custom face graphics void ST_LoadFaceGraphics(INT32 skinnum) { - if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_LIFEPIC) + if (skins[skinnum]->sprites[SPR2_XTRA].numframes > XTRA_LIFEPIC) { - spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + spritedef_t *sprdef = &skins[skinnum]->sprites[SPR2_XTRA]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_LIFEPIC]; faceprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX); - if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes > XTRA_LIFEPIC) + if (skins[skinnum]->sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes > XTRA_LIFEPIC) { - sprdef = &skins[skinnum].sprites[SPR2_XTRA|FF_SPR2SUPER]; + sprdef = &skins[skinnum]->sprites[SPR2_XTRA|FF_SPR2SUPER]; sprframe = &sprdef->spriteframes[0]; superprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX); } @@ -380,13 +378,21 @@ void ST_LoadFaceGraphics(INT32 skinnum) } else faceprefix[skinnum] = superprefix[skinnum] = W_CachePatchName("MISSING", PU_HUDGFX); // ditto - facefreed[skinnum] = false; } void ST_ReloadSkinFaceGraphics(void) { INT32 i; + Z_Free(faceprefix); + Z_Free(superprefix); + + if (!numskins) + return; + + faceprefix = Z_Malloc(sizeof(patch_t *) * numskins, PU_STATIC, NULL); + superprefix = Z_Malloc(sizeof(patch_t *) * numskins, PU_STATIC, NULL); + for (i = 0; i < numskins; i++) ST_LoadFaceGraphics(i); } @@ -425,11 +431,6 @@ void ST_Start(void) // void ST_Init(void) { - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - facefreed[i] = true; - if (dedicated) return; @@ -989,14 +990,14 @@ static void ST_drawLivesArea(void) // name v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER); - if (strlen(skins[stplyr->skin].hudname) <= 5) - V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname); - else if (V_StringWidth(skins[stplyr->skin].hudname, v_colmap) <= 48) - V_DrawString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname); - else if (V_ThinStringWidth(skins[stplyr->skin].hudname, v_colmap) <= 40) - V_DrawRightAlignedThinString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname); + if (strlen(skins[stplyr->skin]->hudname) <= 5) + V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin]->hudname); + else if (V_StringWidth(skins[stplyr->skin]->hudname, v_colmap) <= 48) + V_DrawString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin]->hudname); + else if (V_ThinStringWidth(skins[stplyr->skin]->hudname, v_colmap) <= 40) + V_DrawRightAlignedThinString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin]->hudname); else - V_DrawThinString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname); + V_DrawThinString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin]->hudname); // Power Stones collected if (G_RingSlingerGametype() && LUA_HudEnabled(hud_powerstones)) diff --git a/src/st_stuff.h b/src/st_stuff.h index 1bf7c5f22..b24e6b18f 100644 --- a/src/st_stuff.h +++ b/src/st_stuff.h @@ -42,7 +42,7 @@ void ST_UnloadGraphics(void); void ST_LoadGraphics(void); // face load graphics, called when skin changes -void ST_LoadFaceGraphics(INT32 playernum); +void ST_LoadFaceGraphics(INT32 skinnum); void ST_ReloadSkinFaceGraphics(void); void ST_doPaletteStuff(void); @@ -75,8 +75,8 @@ extern patch_t *sboscore; extern patch_t *sbotime; extern patch_t *sbocolon; extern patch_t *sboperiod; -extern patch_t *faceprefix[MAXSKINS]; // face status patches -extern patch_t *superprefix[MAXSKINS]; // super face status patches +extern patch_t **faceprefix; // face status patches +extern patch_t **superprefix; // super face status patches extern patch_t *livesback; extern patch_t *stlivex; extern patch_t *ngradeletters[7]; diff --git a/src/taglist.c b/src/taglist.c index e4e385b9e..7bd004627 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -180,10 +180,10 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) if (Taggroup_Find(group, id) != (size_t)-1) return; - if (! in_bit_array(tags_available, tag)) + if (! in_bit_array(tags_available, (UINT16)tag)) { num_tags++; - set_bit_array(tags_available, tag); + set_bit_array(tags_available, (UINT16)tag); } // Create group if empty. @@ -220,10 +220,10 @@ static void Taggroup_Add_Init(taggroup_t *garray[], const mtag_t tag, size_t id) group = garray[(UINT16)tag]; - if (! in_bit_array(tags_available, tag)) + if (! in_bit_array(tags_available, (UINT16)tag)) { num_tags++; - set_bit_array(tags_available, tag); + set_bit_array(tags_available, (UINT16)tag); } // Create group if empty. @@ -271,7 +271,7 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) if (group->count == 1 && total_elements_with_tag(tag) == 1) { num_tags--; - unset_bit_array(tags_available, tag); + unset_bit_array(tags_available, (UINT16)tag); } // Strip away taggroup if no elements left. diff --git a/src/v_video.c b/src/v_video.c index 01c65141b..bab2d74f7 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1060,9 +1060,9 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN // void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT16 skincolor) { - if (skinnum >= 0 && skinnum < numskins && skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_CONTINUE) + if (skinnum >= 0 && skinnum < numskins && skins[skinnum]->sprites[SPR2_XTRA].numframes > XTRA_CONTINUE) { - spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + spritedef_t *sprdef = &skins[skinnum]->sprites[SPR2_XTRA]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CONTINUE]; patch_t *patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); const UINT8 *colormap = R_GetTranslationColormap(skinnum, skincolor, GTC_CACHE); @@ -1163,12 +1163,32 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) { UINT8 *dest; const UINT8 *deststop; + UINT32 alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT); + UINT32 blendmode = ((c & V_BLENDMASK) >> V_BLENDSHIFT); UINT8 perplayershuffle = 0; if (rendermode == render_none) return; + v_translevel = NULL; + if (alphalevel || blendmode) + { + if (alphalevel == 10) // V_HUDTRANSHALF + alphalevel = hudminusalpha[st_translucency]; + else if (alphalevel == 11) // V_HUDTRANS + alphalevel = 10 - st_translucency; + else if (alphalevel == 12) // V_HUDTRANSDOUBLE + alphalevel = hudplusalpha[st_translucency]; + + if (alphalevel >= 10) + return; // invis + + if (alphalevel || blendmode) + v_translevel = R_GetBlendTable(blendmode+1, alphalevel); + } + + #ifdef HWRENDER //if (rendermode != render_soft && !con_startup) // Not this again if (rendermode == render_opengl) @@ -1178,6 +1198,8 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) } #endif + + if (splitscreen && (c & V_PERPLAYER)) { fixed_t adjusty = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1; @@ -1312,8 +1334,21 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) c &= 255; - for (;(--h >= 0) && dest < deststop; dest += vid.width) - memset(dest, c, w * vid.bpp); + // borrowing this from jimitia's new hud drawing functions rq + if (alphalevel) + { + v_translevel += c<<8; + for (;(--h >= 0) && dest < deststop; dest += vid.width) + { + for (x = 0; x < w; x++) + dest[x] = v_translevel[dest[x]]; + } + } + else + { + for (;(--h >= 0) && dest < deststop; dest += vid.width) + memset(dest, c, w * vid.bpp); + } } #ifdef HWRENDER diff --git a/src/w_wad.c b/src/w_wad.c index d012182f1..051469ef5 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -208,7 +208,7 @@ static void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile) posStart = W_CheckNumForFullNamePK3("Init.lua", wadnum, 0); if (posStart != INT16_MAX) { - LUA_LoadLump(wadnum, posStart, true); + LUA_DoLump(wadnum, posStart, true); } else { @@ -217,7 +217,7 @@ static void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile) { posEnd = W_CheckNumForFolderEndPK3("Lua/", wadnum, posStart); for (; posStart < posEnd; posStart++) - LUA_LoadLump(wadnum, posStart, true); + LUA_DoLump(wadnum, posStart, true); } } @@ -250,7 +250,7 @@ static void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile) lumpinfo_t *lump_p = wadfiles[wadnum]->lumpinfo; for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++) if (memcmp(lump_p->name,"LUA_",4)==0) - LUA_LoadLump(wadnum, lump, true); + LUA_DoLump(wadnum, lump, true); } { @@ -993,7 +993,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0, mainfile); break; case RET_LUA: - LUA_LoadLump(numwadfiles - 1, 0, true); + LUA_DoLump(numwadfiles - 1, 0, true); break; default: break; diff --git a/src/y_inter.c b/src/y_inter.c index cbe057582..1f008eaf0 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -97,7 +97,7 @@ typedef union // Continues UINT8 continues; patch_t *pcontinues; - INT32 *playerchar; // Continue HUD + UINT8 *playerchar; // Continue HUD UINT16 *playercolor; UINT8 gotlife; // Number of extra lives obtained @@ -108,7 +108,7 @@ typedef union UINT32 scores[MAXPLAYERS]; // Winner's score UINT16 *color[MAXPLAYERS]; // Winner's color # boolean spectator[MAXPLAYERS]; // Spectator list - INT32 *character[MAXPLAYERS]; // Winner's character # + UINT8 *character[MAXPLAYERS]; // Winner's character # INT32 num[MAXPLAYERS]; // Winner's player # char *name[MAXPLAYERS]; // Winner's name patch_t *result; // RESULT @@ -121,7 +121,7 @@ typedef union struct { UINT16 *color[MAXPLAYERS]; // Winner's color # - INT32 *character[MAXPLAYERS]; // Winner's character # + UINT8 *character[MAXPLAYERS]; // Winner's character # INT32 num[MAXPLAYERS]; // Winner's player # char name[MAXPLAYERS][9]; // Winner's name UINT32 times[MAXPLAYERS]; @@ -1382,22 +1382,22 @@ void Y_StartIntermission(void) else { // too long so just show "YOU GOT THROUGH THE ACT" - if (strlen(skins[players[consoleplayer].skin].realname) > 13) + if (strlen(skins[players[consoleplayer].skin]->realname) > 13) { strcpy(data.coop.passed1, "you got"); strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act"); } // long enough that "X GOT" won't fit so use "X PASSED THE ACT" - else if (strlen(skins[players[consoleplayer].skin].realname) > 8) + else if (strlen(skins[players[consoleplayer].skin]->realname) > 8) { - strcpy(data.coop.passed1, skins[players[consoleplayer].skin].realname); + strcpy(data.coop.passed1, skins[players[consoleplayer].skin]->realname); strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "passed act" : "passed the act"); } // length is okay for normal use else { snprintf(data.coop.passed1, sizeof data.coop.passed1, "%s got", - skins[players[consoleplayer].skin].realname); + skins[players[consoleplayer].skin]->realname); strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act"); } } @@ -1469,26 +1469,26 @@ void Y_StartIntermission(void) { snprintf(data.spec.passed1, sizeof data.spec.passed1, "%s", - skins[players[consoleplayer].skin].realname); + skins[players[consoleplayer].skin]->realname); data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0'; strcpy(data.spec.passed2, "got them all!"); if (players[consoleplayer].charflags & SF_SUPER) { strcpy(data.spec.passed3, "can now become"); - if (strlen(skins[players[consoleplayer].skin].supername) > 20) //too long, use generic + if (strlen(skins[players[consoleplayer].skin]->supername) > 20) //too long, use generic strcpy(data.spec.passed4, "their super form"); else - strcpy(data.spec.passed4, skins[players[consoleplayer].skin].supername); + strcpy(data.spec.passed4, skins[players[consoleplayer].skin]->supername); } } else { - if (strlen(skins[players[consoleplayer].skin].realname) <= SKINNAMESIZE-5) + if (strlen(skins[players[consoleplayer].skin]->realname) <= SKINNAMESIZE-5) { snprintf(data.spec.passed1, sizeof data.spec.passed1, "%s got", - skins[players[consoleplayer].skin].realname); + skins[players[consoleplayer].skin]->realname); data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0'; } else