diff --git a/.travis.yml b/.travis.yml
index f2ed43000addf70ea07416fad3d5281472eec927..9d91b77dfc937a48da16ead5cf80298711e92da5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -459,11 +459,15 @@ matrix:
         - os: linux
           addons:
             apt:
+              sources:
+              - ubuntu-toolchain-r-test
+              - sourceline: 'ppa:stjr/srb2'
               packages:
               - libsdl2-mixer-dev
               - libpng-dev
               - libgl1-mesa-dev
               - libgme-dev
+              - libopenmpt-dev
               - p7zip-full
               - gcc-4.8
           compiler: gcc-4.8
@@ -473,43 +477,51 @@ matrix:
               AND env(DPL_TERMINATE_MAIN) != "1"
           env:
           - _DPL_JOB_ENABLED=1
-          - _DPL_JOB_NAME=bionic
+          - _DPL_JOB_NAME=eoan
           - _DPL_DPUT_TARGET=1
           - _DPL_PACKAGE_SOURCE=1
-          - PACKAGE_DISTRO=bionic
-          - PACKAGE_SUBVERSION=~18.04bionic
+          - PACKAGE_DISTRO=eoan
+          - PACKAGE_SUBVERSION=~19.10eoan
           #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
         - os: linux
           addons:
             apt:
+              sources:
+              - ubuntu-toolchain-r-test
+              - sourceline: 'ppa:stjr/srb2'
               packages:
               - libsdl2-mixer-dev
               - libpng-dev
               - libgl1-mesa-dev
               - libgme-dev
+              - libopenmpt-dev
               - p7zip-full
               - gcc-4.8
           compiler: gcc-4.8
-          dist: trusty
+          dist: xenial
           if: env(DPL_ENABLED) = "1" AND (env(_DPL_JOB_ENABLED) = "1" OR env(DPL_JOB_ENABLE_ALL) = "1")
               AND (branch =~ /^.*deployer.*$/ OR (tag IS present AND env(DPL_TAG_ENABLED) = "1"))
               AND env(DPL_TERMINATE_MAIN) != "1"
           env:
           - _DPL_JOB_ENABLED=1
-          - _DPL_JOB_NAME=trusty
+          - _DPL_JOB_NAME=disco
           - _DPL_DPUT_TARGET=1
           - _DPL_PACKAGE_SOURCE=1
-          - PACKAGE_DISTRO=trusty
-          - PACKAGE_SUBVERSION=~14.04trusty
+          - PACKAGE_DISTRO=disco
+          - PACKAGE_SUBVERSION=~19.04disco
           #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
         - os: linux
           addons:
             apt:
+              sources:
+              - ubuntu-toolchain-r-test
+              - sourceline: 'ppa:stjr/srb2'
               packages:
               - libsdl2-mixer-dev
               - libpng-dev
               - libgl1-mesa-dev
               - libgme-dev
+              - libopenmpt-dev
               - p7zip-full
               - gcc-4.8
           compiler: gcc-4.8
@@ -519,20 +531,24 @@ matrix:
               AND env(DPL_TERMINATE_MAIN) != "1"
           env:
           - _DPL_JOB_ENABLED=1
-          - _DPL_JOB_NAME=disco
+          - _DPL_JOB_NAME=bionic
           - _DPL_DPUT_TARGET=1
           - _DPL_PACKAGE_SOURCE=1
-          - PACKAGE_DISTRO=disco
-          - PACKAGE_SUBVERSION=~19.04disco
+          - PACKAGE_DISTRO=bionic
+          - PACKAGE_SUBVERSION=~18.04bionic
           #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
         - os: linux
           addons:
             apt:
+              sources:
+              - ubuntu-toolchain-r-test
+              - sourceline: 'ppa:stjr/srb2'
               packages:
               - libsdl2-mixer-dev
               - libpng-dev
               - libgl1-mesa-dev
               - libgme-dev
+              - libopenmpt-dev
               - p7zip-full
               - gcc-4.8
           compiler: gcc-4.8
@@ -542,34 +558,38 @@ matrix:
               AND env(DPL_TERMINATE_MAIN) != "1"
           env:
           - _DPL_JOB_ENABLED=1
-          - _DPL_JOB_NAME=cosmic
+          - _DPL_JOB_NAME=xenial
           - _DPL_DPUT_TARGET=1
           - _DPL_PACKAGE_SOURCE=1
-          - PACKAGE_DISTRO=cosmic
-          - PACKAGE_SUBVERSION=~18.10cosmic
+          - PACKAGE_DISTRO=xenial
+          - PACKAGE_SUBVERSION=~16.04xenial
           #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
         - os: linux
           addons:
             apt:
+              sources:
+              - ubuntu-toolchain-r-test
+              - sourceline: 'ppa:stjr/srb2'
               packages:
               - libsdl2-mixer-dev
               - libpng-dev
               - libgl1-mesa-dev
               - libgme-dev
+              - libopenmpt-dev
               - p7zip-full
               - gcc-4.8
           compiler: gcc-4.8
-          dist: xenial
+          dist: trusty
           if: env(DPL_ENABLED) = "1" AND (env(_DPL_JOB_ENABLED) = "1" OR env(DPL_JOB_ENABLE_ALL) = "1")
               AND (branch =~ /^.*deployer.*$/ OR (tag IS present AND env(DPL_TAG_ENABLED) = "1"))
               AND env(DPL_TERMINATE_MAIN) != "1"
           env:
           - _DPL_JOB_ENABLED=1
-          - _DPL_JOB_NAME=xenial
+          - _DPL_JOB_NAME=trusty
           - _DPL_DPUT_TARGET=1
           - _DPL_PACKAGE_SOURCE=1
-          - PACKAGE_DISTRO=xenial
-          - PACKAGE_SUBVERSION=~16.04xenial
+          - PACKAGE_DISTRO=trusty
+          - PACKAGE_SUBVERSION=~14.04trusty
           #gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
     allow_failures:
       - compiler: clang-3.5
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 96e32a06d95f081af4004d791b2d778d56d7de1a..8bbf46945f0eb0f561702ef014e96dfd3d72ce96 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
 # DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string.
 # Version change is fine.
 project(SRB2
-	VERSION 2.1.25
+	VERSION 2.2.0
 	LANGUAGES C)
 
 if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
@@ -95,7 +95,6 @@ set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
 
 # Set EXE names so the assets CMakeLists can refer to its target
 set(SRB2_SDL2_EXE_NAME srb2 CACHE STRING "Executable binary output name")
-set(SRB2_WIN_EXE_NAME srb2dd CACHE STRING "Executable binary output name for DirectDraw build")
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR}/src)
 
diff --git a/appveyor.yml b/appveyor.yml
index d58976fd5fc543844aba2fa5ec495458e7ac3a0a..6b80b4ec37e438a9af240c4768ba0e2338466d36 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,145 +1,136 @@
-version: 2.1.25.{branch}-{build}
-os: MinGW
-
-environment:
- CC: ccache
- CCACHE_CC: i686-w64-mingw32-gcc
- CCACHE_CC_64: x86_64-w64-mingw32-gcc
- WINDRES: windres
- # c:\mingw-w64 i686 has gcc 6.3.0, so use c:\msys64 7.3.0 instead
- MINGW_SDK: c:\msys64\mingw32
- # c:\msys64 x86_64 has gcc 8.2.0, so use c:\mingw-w64 7.3.0 instead
- MINGW_SDK_64: C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64
- CFLAGS: -Wall -W -Werror -Wno-error=implicit-fallthrough -Wimplicit-fallthrough=3 -Wno-tautological-compare -Wno-error=suggest-attribute=noreturn
- NASM_ZIP: nasm-2.12.01
- NASM_URL: http://www.nasm.us/pub/nasm/releasebuilds/2.12.01/win64/nasm-2.12.01-win64.zip
- UPX_ZIP: upx391w
- UPX_URL: http://upx.sourceforge.net/download/upx391w.zip
- CCACHE_EXE: ccache.exe
- CCACHE_URL: http://alam.srb2.org/ccache.exe
- CCACHE_COMPRESS: true
- CCACHE_DIR: C:\Users\appveyor\.ccache
- # Disable UPX by default. The user can override this in their Appveyor project settings
- NOUPX: 1
- ##############################
- # DEPLOYER VARIABLES
- # DPL_ENABLED=1 builds installers for branch names starting with `deployer`.
- # DPL_TAG_ENABLED=1 will also build installers for release tags. DPL_ENABLED=1 must also be set.
- # Set these in the Appveyor project settings
- ##############################
- DPL_ENABLED: 0
- DPL_TAG_ENABLED: 0
- DPL_INSTALLER_NAME: SRB2-v2123
- # Asset handling is barebones vs. Travis Deployer. We operate on 7z only.
- # Include the README files and the OpenGL batch in the main and patch archives.
- # The x86/x64 archives contain the DLL binaries.
- ASSET_ARCHIVE_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-assets.7z
- ASSET_ARCHIVE_PATCH_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-patch-assets.7z
- ASSET_ARCHIVE_X86_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-x86-assets.7z
- ASSET_ARCHIVE_X64_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-x64-assets.7z
- ASSET_ARCHIVE_OPTIONAL_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-optional-assets.7z
- # This is overridden to 1 for release tag builds
- ASSET_FILES_OPTIONAL_GET: 0
- # For patches, also include the X86/X64 DLLs.
- PACKAGE_PATCH_DLL_GET: 0
- # Delete all asset downloads so they can be redownloaded
- ASSET_CLEAN: 0
-
-cache:
-- nasm-2.12.01.zip
-- upx391w.zip
-- ccache.exe
-- C:\Users\appveyor\.ccache
-- C:\Users\appveyor\srb2_cache
-
-install:
-- if [%CONFIGURATION%] == [SDL64] ( set "X86_64=1" )
-- if [%CONFIGURATION%] == [SDL64] ( set "CONFIGURATION=SDL" )
-- if [%CONFIGURATION%] == [DD64] ( set "X86_64=1" )
-- if [%CONFIGURATION%] == [DD64] ( set "CONFIGURATION=DD" )
-- if [%X86_64%] == [1] ( set "MINGW_SDK=%MINGW_SDK_64%" )
-- if [%X86_64%] == [1] ( set "CCACHE_CC=%CCACHE_CC_64%" )
-
-- if not exist "%NASM_ZIP%.zip" appveyor DownloadFile "%NASM_URL%" -FileName "%NASM_ZIP%.zip"
-- 7z x -y "%NASM_ZIP%.zip" -o%TMP% >null
-- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%NASM_ZIP%" "%MINGW_SDK%\bin" nasm.exe || exit 0
-
-- if not exist "%UPX_ZIP%.zip" appveyor DownloadFile "%UPX_URL%" -FileName "%UPX_ZIP%.zip"
-- 7z x -y "%UPX_ZIP%.zip" -o%TMP% >null
-- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%UPX_ZIP%" "%MINGW_SDK%\bin" upx.exe || exit 0
-
-- if not exist "%CCACHE_EXE%" appveyor DownloadFile "%CCACHE_URL%" -FileName "%CCACHE_EXE%"
-- ccache -M 99M
-- xcopy /Y /V /I ccache.exe "%MINGW_SDK%\bin"
-
-configuration:
-- SDL
-- SDL64
-- DD
-- DD64
-
-matrix:
-  allow_failures:
-    - configuration: DD
-    - configuration: DD64
-
-before_build:
-- set "Path=%MINGW_SDK%\bin;%Path%"
-- if [%X86_64%] == [1] ( x86_64-w64-mingw32-gcc --version ) else ( i686-w64-mingw32-gcc --version )
-- mingw32-make --version
-- if not [%X86_64%] == [1] ( nasm -v )
-- if not [%NOUPX%] == [1] ( upx -V )
-- ccache -V
-- ccache -s
-- if [%NOUPX%] == [1] ( set "NOUPX=NOUPX=1" ) else ( set "NOUPX=" )
-- set "SRB2_MFLAGS=-C src WARNINGMODE=1 CCACHE=1 GCC73=1 NOOBJDUMP=1 %NOUPX%"
-- if [%X86_64%] == [1] ( set "MINGW_FLAGS=MINGW64=1 X86_64=1" ) else ( set "MINGW_FLAGS=MINGW=1 GCC91=1" )
-- set "SRB2_MFLAGS=%SRB2_MFLAGS% %MINGW_FLAGS% %CONFIGURATION%=1"
-
-build_script:
-- cmd: mingw32-make.exe %SRB2_MFLAGS% clean
-- cmd: mingw32-make.exe %SRB2_MFLAGS% ERRORMODE=1 -k
-
-after_build:
-- if [%X86_64%] == [1] (
-    set "BUILD_PATH=bin\Mingw64\Release"
-  ) else (
-    set "BUILD_PATH=bin\Mingw\Release"
-  )
-- if [%X86_64%] == [1] ( set "CONFIGURATION=%CONFIGURATION%64" )
-- ccache -s
-- cmd: git rev-parse --short %APPVEYOR_REPO_COMMIT%>%TMP%/gitshort.txt
-- cmd: set /P GITSHORT=<%TMP%/gitshort.txt
-- set BUILD_ARCHIVE=%APPVEYOR_REPO_BRANCH%-%GITSHORT%-%CONFIGURATION%.7z
-- set BUILDSARCHIVE=%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%.7z
-- cmd: 7z a %BUILD_ARCHIVE% %BUILD_PATH% -xr!.gitignore
-- appveyor PushArtifact %BUILD_ARCHIVE%
-- cmd: copy %BUILD_ARCHIVE% %BUILDSARCHIVE%
-- appveyor PushArtifact %BUILDSARCHIVE%
-##############################
-# DEPLOYER SCRIPT
-##############################
-- if [%DPL_ENABLED%] == [1] ( call "deployer\appveyor\deployer.bat" )
-
-test: off
-
-#deploy:
-#  - provider: FTP
-#    protocol: ftps
-#    host:
-#      secure: NsLJEPIBvmwCOj8Tg8RoRQ==
-#    username:
-#      secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA=
-#    password:
-#      secure: Hbn6Uy3lT0YZ88yFJ3aW4w==
-#    folder: appveyor
-#    application:
-#    active_mode: false
-#    on:
-#      branch: master
-#      appveyor_repo_tag: true
-
-
-on_finish:
-#- cmd: echo xfreerdp /u:appveyor /cert-ignore +clipboard /v:<ip>:<port>
-#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
+version: 2.2.0.{branch}-{build}
+os: MinGW
+
+environment:
+ CC: ccache
+ CCACHE_CC: i686-w64-mingw32-gcc
+ CCACHE_CC_64: x86_64-w64-mingw32-gcc
+ WINDRES: windres
+ # c:\mingw-w64 i686 has gcc 6.3.0, so use c:\msys64 7.3.0 instead
+ MINGW_SDK: c:\msys64\mingw32
+ # c:\msys64 x86_64 has gcc 8.2.0, so use c:\mingw-w64 7.3.0 instead
+ MINGW_SDK_64: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64
+ CFLAGS: -Wall -W -Werror -Wno-error=implicit-fallthrough -Wimplicit-fallthrough=3 -Wno-tautological-compare -Wno-error=suggest-attribute=noreturn
+ NASM_ZIP: nasm-2.12.01
+ NASM_URL: http://www.nasm.us/pub/nasm/releasebuilds/2.12.01/win64/nasm-2.12.01-win64.zip
+ UPX_ZIP: upx391w
+ UPX_URL: http://upx.sourceforge.net/download/upx391w.zip
+ CCACHE_EXE: ccache.exe
+ CCACHE_URL: http://alam.srb2.org/ccache.exe
+ CCACHE_COMPRESS: true
+ CCACHE_DIR: C:\Users\appveyor\.ccache
+ # Disable UPX by default. The user can override this in their Appveyor project settings
+ NOUPX: 1
+ ##############################
+ # DEPLOYER VARIABLES
+ # DPL_ENABLED=1 builds installers for branch names starting with `deployer`.
+ # DPL_TAG_ENABLED=1 will also build installers for release tags. DPL_ENABLED=1 must also be set.
+ # Set these in the Appveyor project settings
+ ##############################
+ DPL_ENABLED: 0
+ DPL_TAG_ENABLED: 0
+ DPL_INSTALLER_NAME: SRB2-v220
+ # Asset handling is barebones vs. Travis Deployer. We operate on 7z only.
+ # Include the README files and the OpenGL batch in the main and patch archives.
+ # The x86/x64 archives contain the DLL binaries.
+ ASSET_ARCHIVE_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets_220/SRB2-v220-assets.7z
+ ASSET_ARCHIVE_PATCH_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets_220/SRB2-v220-patch-assets.7z
+ ASSET_ARCHIVE_X86_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets_220/SRB2-v220-x86-assets.7z
+ ASSET_ARCHIVE_X64_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets_220/SRB2-v220-x64-assets.7z
+ ASSET_ARCHIVE_OPTIONAL_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets_220/SRB2-v220-optional-assets.7z
+ # This is overridden to 1 for release tag builds
+ ASSET_FILES_OPTIONAL_GET: 0
+ # For patches, also include the X86/X64 DLLs.
+ PACKAGE_PATCH_DLL_GET: 0
+ # Delete all asset downloads so they can be redownloaded
+ ASSET_CLEAN: 0
+
+cache:
+- nasm-2.12.01.zip
+- upx391w.zip
+- ccache.exe
+- C:\Users\appveyor\.ccache
+- C:\Users\appveyor\srb2_cache
+
+install:
+- if [%CONFIGURATION%] == [SDL64] ( set "X86_64=1" )
+- if [%CONFIGURATION%] == [SDL64] ( set "CONFIGURATION=SDL" )
+- if [%X86_64%] == [1] ( set "MINGW_SDK=%MINGW_SDK_64%" )
+- if [%X86_64%] == [1] ( set "CCACHE_CC=%CCACHE_CC_64%" )
+
+- if not exist "%NASM_ZIP%.zip" appveyor DownloadFile "%NASM_URL%" -FileName "%NASM_ZIP%.zip"
+- 7z x -y "%NASM_ZIP%.zip" -o%TMP% >null
+- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%NASM_ZIP%" "%MINGW_SDK%\bin" nasm.exe || exit 0
+
+- if not exist "%UPX_ZIP%.zip" appveyor DownloadFile "%UPX_URL%" -FileName "%UPX_ZIP%.zip"
+- 7z x -y "%UPX_ZIP%.zip" -o%TMP% >null
+- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%UPX_ZIP%" "%MINGW_SDK%\bin" upx.exe || exit 0
+
+- if not exist "%CCACHE_EXE%" appveyor DownloadFile "%CCACHE_URL%" -FileName "%CCACHE_EXE%"
+- ccache -M 99M
+- xcopy /Y /V /I ccache.exe "%MINGW_SDK%\bin"
+
+configuration:
+- SDL
+- SDL64
+
+before_build:
+- set "Path=%MINGW_SDK%\bin;%Path%"
+- if [%X86_64%] == [1] ( x86_64-w64-mingw32-gcc --version ) else ( i686-w64-mingw32-gcc --version )
+- mingw32-make --version
+- if not [%X86_64%] == [1] ( nasm -v )
+- if not [%NOUPX%] == [1] ( upx -V )
+- ccache -V
+- ccache -s
+- if [%NOUPX%] == [1] ( set "NOUPX=NOUPX=1" ) else ( set "NOUPX=" )
+- set "SRB2_MFLAGS=-C src WARNINGMODE=1 CCACHE=1 NOOBJDUMP=1 %NOUPX%"
+- if [%X86_64%] == [1] ( set "MINGW_FLAGS=MINGW64=1 X86_64=1 GCC81=1" ) else ( set "MINGW_FLAGS=MINGW=1 GCC91=1" )
+- set "SRB2_MFLAGS=%SRB2_MFLAGS% %MINGW_FLAGS% %CONFIGURATION%=1"
+
+build_script:
+- cmd: mingw32-make.exe %SRB2_MFLAGS% clean
+- cmd: mingw32-make.exe %SRB2_MFLAGS% ERRORMODE=1 -k
+
+after_build:
+- if [%X86_64%] == [1] (
+    set "BUILD_PATH=bin\Mingw64\Release"
+  ) else (
+    set "BUILD_PATH=bin\Mingw\Release"
+  )
+- if [%X86_64%] == [1] ( set "CONFIGURATION=%CONFIGURATION%64" )
+- ccache -s
+- cmd: git rev-parse --short %APPVEYOR_REPO_COMMIT%>%TMP%/gitshort.txt
+- cmd: set /P GITSHORT=<%TMP%/gitshort.txt
+- set BUILD_ARCHIVE=%APPVEYOR_REPO_BRANCH%-%GITSHORT%-%CONFIGURATION%.7z
+- set BUILDSARCHIVE=%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%.7z
+- cmd: 7z a %BUILD_ARCHIVE% %BUILD_PATH% -xr!.gitignore
+- appveyor PushArtifact %BUILD_ARCHIVE%
+- cmd: copy %BUILD_ARCHIVE% %BUILDSARCHIVE%
+- appveyor PushArtifact %BUILDSARCHIVE%
+##############################
+# DEPLOYER SCRIPT
+##############################
+- if [%DPL_ENABLED%] == [1] ( call "deployer\appveyor\deployer.bat" )
+
+test: off
+
+#deploy:
+#  - provider: FTP
+#    protocol: ftps
+#    host:
+#      secure: NsLJEPIBvmwCOj8Tg8RoRQ==
+#    username:
+#      secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA=
+#    password:
+#      secure: Hbn6Uy3lT0YZ88yFJ3aW4w==
+#    folder: appveyor
+#    application:
+#    active_mode: false
+#    on:
+#      branch: master
+#      appveyor_repo_tag: true
+
+
+on_finish:
+#- cmd: echo xfreerdp /u:appveyor /cert-ignore +clipboard /v:<ip>:<port>
+#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
diff --git a/assets/CMakeLists.txt b/assets/CMakeLists.txt
index 8811536826795daf8667d943a5a725ea112e19cf..0636c1e599e4ca793fa0e253b23e7ec76ae8c802 100644
--- a/assets/CMakeLists.txt
+++ b/assets/CMakeLists.txt
@@ -12,18 +12,22 @@ ENDFUNCTION(PREPEND)
 set(SRB2_ASSET_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/installer"
 	CACHE STRING "Path to directory that contains all asset files for the installer.")
 
+####################
+# POST-V2.2 NOTE: Do not forget to add patch.pk3 to the end of this list!
+####################
+
 set(SRB2_ASSET_HASHED
 "srb2.pk3;\
 player.dta;\
-zones.pk3;\
-patch.pk3"
+zones.pk3"
 	CACHE STRING "Asset filenames to apply MD5 checks. No spaces between entries!"
 )
 
 set(SRB2_ASSET_DOCS
 "README.txt;\
 LICENSE.txt;\
-LICENSE-3RD-PARTY.txt"
+LICENSE-3RD-PARTY.txt;\
+README-SDL.txt"
 	CACHE STRING "Documentation filenames. In OS X, these are packaged separately from other assets. No spaces between entries!"
 )
 
diff --git a/assets/README.txt b/assets/README.txt
index 37f7d8f29e3cf0e1744ce3f34c032f3ff0dbd414..8d2fefa54ce90136a6b9c3d368bd3cd817057459 100644
--- a/assets/README.txt
+++ b/assets/README.txt
@@ -34,11 +34,11 @@ SRB2 Message Board:
 https://mb.srb2.org
 
 SRB2 Official Discord:
-https://discord.gg/pYDXzpX
+https://discord.gg/pYDXzpX (13+)
 
 COPYRIGHT AND DISCLAIMER
 
-Design and content on SRB2 is copyright 1998-2018 by Sonic Team Junior.
+Design and content on SRB2 is copyright 1998-2019 by Sonic Team Junior.
 All non-original material on SRB2.ORG is copyrighted by their
 respective owners, and no copyright infringement is intended. The owner
 of the SRB2.ORG domain is only acting as an ISP, and is therefore not
diff --git a/debian-template/control b/debian-template/control
index e1348d704e3187dfc0e70a7165d467a2c6354492..74d11ae9023215f91120d549078042bd5bdfade0 100644
--- a/debian-template/control
+++ b/debian-template/control
@@ -10,6 +10,7 @@ Build-Depends: debhelper (>= 7.0.50~),
  libpng-dev | libpng16-dev | libpng12-dev (>= 1.2.7),
  zlib1g-dev,
  libgme-dev,
+ libopenmpt-dev,
  libglu1-dev | libglu-dev,
  libosmesa6-dev | libgl-dev,
  nasm [i386]
@@ -24,6 +25,7 @@ Depends: ${SHLIBS_DEPENDS}, ${MISC_DEPENDS},
  libsdl2-mixer-2.0-0,
  zlib1g,
  libgme0,
+ libopenmpt | libopenmpt0,
  libpng | libpng16-16 | libpng12-0
 Description: A cross-platform 3D Sonic fangame
  Sonic Robo Blast 2 is a 3D open-source Sonic the Hedgehog
diff --git a/deployer/travis/deployer_defaults.sh b/deployer/travis/deployer_defaults.sh
index bccb7409ac2750564d3a75433e12ff747ad21ae7..fe7ba6993fa66ea1fce68f156493a3d45dfd0acc 100644
--- a/deployer/travis/deployer_defaults.sh
+++ b/deployer/travis/deployer_defaults.sh
@@ -27,10 +27,10 @@
 : ${_DPL_PACKAGE_ASSET}         # Build asset installation package. Linux only.
 
 # Asset File Parameters
-: ${ASSET_ARCHIVE_PATH:=https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-assets.7z}
-: ${ASSET_ARCHIVE_OPTIONAL_PATH:=https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-optional-assets.7z}
-: ${ASSET_FILES_HASHED:=srb2.srb zones.dta player.dta rings.dta patch.dta}
-: ${ASSET_FILES_DOCS:=README.txt LICENSE.txt LICENSE-3RD-PARTY.txt}
+: ${ASSET_ARCHIVE_PATH:=https://github.com/mazmazz/SRB2/releases/download/SRB2_assets_220/SRB2-v220-assets.7z}
+: ${ASSET_ARCHIVE_OPTIONAL_PATH:=https://github.com/mazmazz/SRB2/releases/download/SRB2_assets_220/SRB2-v220-optional-assets.7z}
+: ${ASSET_FILES_HASHED:=srb2.pk3 zones.pk3 player.dta} # POST v2.2 NOTE: Don't forget to add patch.pk3!
+: ${ASSET_FILES_DOCS:=README.txt LICENSE.txt LICENSE-3RD-PARTY.txt README-SDL.txt}
 : ${ASSET_FILES_OPTIONAL_GET:=0}
 
 # FTP Parameters
@@ -51,7 +51,7 @@
 
 # Package Parameters
 : ${PACKAGE_NAME:=srb2}
-: ${PACKAGE_VERSION:=2.1.23}
+: ${PACKAGE_VERSION:=2.2.0}
 : ${PACKAGE_SUBVERSION}         # Highly recommended to set this to reflect the distro series target (e.g., ~18.04bionic)
 : ${PACKAGE_REVISION}           # Defaults to UTC timestamp
 : ${PACKAGE_INSTALL_PATH:=/usr/games/SRB2}
@@ -62,12 +62,12 @@
 : ${PACKAGE_GROUP_NAME_EMAIL:=Sonic Team Junior <stjr@srb2.org>}
 : ${PACKAGE_WEBSITE:=<http://www.srb2.org>}
 
-: ${PACKAGE_ASSET_MINVERSION:=2.1.21}  # Number this the version BEFORE the actual required version, because we do a > check
-: ${PACKAGE_ASSET_MAXVERSION:=2.1.24}  # Number this the version AFTER the actual required version, because we do a < check
+: ${PACKAGE_ASSET_MINVERSION:=2.1.26}  # Number this the version BEFORE the actual required version, because we do a > check
+: ${PACKAGE_ASSET_MAXVERSION:=2.2.1}  # Number this the version AFTER the actual required version, because we do a < check
 
 : ${PROGRAM_NAME:=Sonic Robo Blast 2}
 : ${PROGRAM_VENDOR:=Sonic Team Junior}
-: ${PROGRAM_VERSION:=2.1.23}
+: ${PROGRAM_VERSION:=2.2.0}
 : ${PROGRAM_DESCRIPTION:=A free 3D Sonic the Hedgehog fangame closely inspired by the original Sonic games on the Sega Genesis.}
 : ${PROGRAM_FILENAME:=srb2}
 
diff --git a/extras/conf/Includes/Game_SRB222.cfg b/extras/conf/Includes/Game_SRB222.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3c4b11e9f301f90b68a5ee90c366416048946b73
--- /dev/null
+++ b/extras/conf/Includes/Game_SRB222.cfg
@@ -0,0 +1,77 @@
+// Default lump name for new map
+defaultlumpname = "MAP01";
+//GZDB specific. Don't try to load lumps that don't exist.
+basegame = 0;
+
+//Sky textures for vanilla maps
+defaultskytextures
+{
+	SKY1 = "MAP01,MAP02,MAP03,MAP33,MAP50,MAP60,MAPF0,MAPM0";
+	SKY2 = "MAPM7,MAPMB";
+	SKY4 = "MAP04,MAP06,MAP61,MAPF6,MAPM1";
+	SKY6 = "MAP05,MAP51,MAPMA";
+	SKY7 = "MAPM2,MAPM5";
+	SKY8 = "MAP07,MAP08,MAP09,MAP52,MAP62,MAPF1";
+	SKY10 = "MAP10,MAP12,MAP53,MAP63,MAPM3";
+	SKY11 = "MAP11,MAPF7";
+	SKY13 = "MAP13,MAP64";
+	SKY14 = "MAP14";
+	SKY15 = "MAP15,MAP54";
+	SKY17 = "MAP70";
+	SKY20 = "MAP32,MAP55,MAP65,MAPF2,MAPF5";
+	SKY21 = "MAPM4";
+	SKY22 = "MAP22,MAP23,MAP25,MAP26,MAP27,MAP56,MAP66,MAPF4,MAPM6";
+	SKY30 = "MAP30";
+	SKY31 = "MAP31";
+	SKY35 = "MAP42";
+	SKY40 = "MAP41,MAP71,MAPM9";
+	SKY55 = "MAPF3,MAPM8";
+	SKY68 = "MAPF8";
+	SKY99 = "MAP57,MAPZ0";
+	SKY159 = "MAP16";
+	SKY172 = "MAP40";
+	SKY300 = "MAP72";
+	SKY301 = "MAP73";
+}
+
+// Skill levels
+skills
+{
+	1 = "Normal";
+}
+
+// Skins
+skins
+{
+	Sonic;
+	Tails;
+	Knuckles;
+	Amy;
+	Fang;
+	Metalsonic;
+}
+
+// Gametypes
+gametypes
+{
+	-1 = "Single Player";
+	0 = "Co-op";
+	1 = "Competition";
+	2 = "Race";
+	3 = "Match";
+	4 = "Team Match";
+	5 = "Tag";
+	6 = "Hide and Seek";
+	7 = "CTF";
+}
+
+// Texture loading options
+defaultwalltexture = "GFZROCK";
+defaultfloortexture = "GFZFLR01";
+defaultceilingtexture = "F_SKY1";
+
+// Default texture sets
+// (these are not required, but useful for new users)
+texturesets
+{
+}
\ No newline at end of file
diff --git a/extras/conf/Includes/SRB222_common.cfg b/extras/conf/Includes/SRB222_common.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..a832e6cefe6c2ba1580cbd48083bcf1c2702da81
--- /dev/null
+++ b/extras/conf/Includes/SRB222_common.cfg
@@ -0,0 +1,309 @@
+common
+{
+	// Some common settings
+
+
+
+	// Default testing parameters
+	testparameters = "-file \"%AP\" \"%F\" -warp %L";
+	testshortpaths = true;
+
+	// Action special help (mxd)
+	actionspecialhelp = "https://wiki.srb2.org/wiki/Linedef_type_%K";
+
+	// Default nodebuilder configurations
+	defaultsavecompiler = "zennode_normal";
+	defaulttestcompiler = "zennode_fast";
+
+	// Generalized actions
+	generalizedlinedefs = false;
+	generalizedsectors = true;
+
+	mixtexturesflats = true;
+	defaulttexturescale = 1.0f;
+	defaultflatscale = 1.0f;
+	scaledtextureoffsets = true;
+
+	// Thing number for start position in 3D Mode
+	start3dmode = 3328;
+
+	// Texture sources
+	textures
+	{
+		include("SRB222_misc.cfg", "textures");
+	}
+
+	// Patch sources
+	patches
+	{
+		include("SRB222_misc.cfg", "patches");
+	}
+
+	// Sprite sources
+	sprites
+	{
+		include("SRB222_misc.cfg", "sprites");
+	}
+
+	// Flat sources
+	flats
+	{
+		include("SRB222_misc.cfg", "flats");
+	}
+}
+
+mapformat_doom
+{
+	// The format interface handles the map data format - DoomMapSetIO for SRB2DB2, SRB2MapSetIO for Zone Builder
+	formatinterface = "SRB2MapSetIO";
+
+	/*
+	GAME DETECT PATTERN
+	Used to guess the game for which a WAD file is made.
+
+	1 = One of these lumps must exist
+	2 = None of these lumps must exist
+	3 = All of these lumps must exist
+	*/
+
+	gamedetect
+	{
+		EXTENDED = 2;
+
+
+		BEHAVIOR = 2;
+
+		E#M# = 2;
+
+		MAP?? = 1;
+	}
+
+	/*
+	MAP LUMP NAMES
+	Map lumps are loaded with the map as long as they are right after each other. When the editor
+	meets a lump which is not defined in this list it will ignore the map if not satisfied.
+	The order of items defines the order in which lumps will be written to WAD file on save.
+	To indicate the map header lump, use ~MAP
+
+	Legenda:
+	required = Lump is required to exist.
+	blindcopy = Lump will be copied along with the map blindly. (usefull for lumps Doom Builder doesn't use)
+	nodebuild = The nodebuilder generates this lump.
+	allowempty = The nodebuilder is allowed to leave this lump empty.
+	script = This lump is a text-based script. Specify the filename of the script configuration to use.
+	*/
+
+	maplumpnames
+	{
+		include("SRB222_misc.cfg", "doommaplumpnames");
+	}
+
+	// When this is set to true, sectors with the same tag will light up when a line is highlighted
+	linetagindicatesectors = true;
+
+	// Special linedefs
+	include("SRB222_misc.cfg", "speciallinedefs");
+
+	// Default flags for first new thing (As far as 2.2 goes, they're empty just like in 2.1)
+	defaultthingflags
+	{
+	}
+
+	// DEFAULT SECTOR BRIGHTNESS LEVELS
+	sectorbrightness
+	{
+		include("SRB222_misc.cfg", "sectorbrightness");
+	}
+
+	// SECTOR TYPES-----------------------------------------------------------------
+	sectortypes
+	{
+		include("SRB222_sectors.cfg", "sectortypes");
+	}
+
+	// GENERALISED SECTOR TYPES-----------------------------------------------------------------
+	gen_sectortypes
+	{
+		include("SRB222_sectors.cfg", "gen_sectortypes");
+	}
+
+	// LINEDEF FLAGS
+	linedefflags
+	{
+		include("SRB222_misc.cfg", "linedefflags");
+	}
+
+	// Linedef flags UDMF translation table
+	// This is needed for copy/paste and prefabs to work properly
+	// When the UDMF field name is prefixed with ! it is inverted
+	linedefflagstranslation
+	{
+		include("SRB222_misc.cfg", "linedefflagstranslation");
+	}
+
+	// LINEDEF ACTIVATIONS
+	linedefactivations
+	{
+	}
+
+	// LINEDEF TYPES
+	linedeftypes
+	{
+		include("SRB222_linedefs.cfg", "doom");
+	}
+
+	// THING FLAGS
+	thingflags
+	{
+		include("SRB222_misc.cfg", "thingflags");
+	}
+
+	// Thing flags UDMF translation table
+	// This is needed for copy/paste and prefabs to work properly
+	// When the UDMF field name is prefixed with ! it is inverted
+	thingflagstranslation
+	{
+		include("SRB222_misc.cfg", "thingflagstranslation");
+	}
+
+	// THING FLAGS ERROR MASK
+	// Mask for the thing flags which indicates the options
+	// that make the same thing appear in the same modes
+	thingflagsmask1 = 7;	// 1 + 2 + 4
+	thingflagsmask2 = 0;
+}
+
+mapformat_udmf
+{
+	// The format interface handles the map data format
+	formatinterface = "UniversalMapSetIO";
+
+	// Enables support for long (> 8 chars) texture names
+	// WARNING: this should only be enabled for UDMF game configurations!
+	// WARNING: enabling this will make maps incompatible with Doom Builder 2 and can lead to problems in Slade 3!
+	longtexturenames = false;
+
+	// Default nodebuilder configurations
+	defaultsavecompiler = "zdbsp_udmf_normal";
+	defaulttestcompiler = "zdbsp_udmf_fast";
+
+	engine = "srb2"; // override that so that DB2 uses the correct namespace
+
+	maplumpnames
+	{
+		include("UDMF_misc.cfg", "udmfmaplumpnames_begin");
+		include("SRB222_misc.cfg", "udmfmaplumpnames");
+		include("UDMF_misc.cfg", "udmfmaplumpnames_end");
+	}
+
+	universalfields
+	{
+//		include("SRB222_misc.cfg", "universalfields");
+	}
+
+	// When this is set to true, sectors with the same tag will light up when a line is highlighted
+	linetagindicatesectors = false;
+
+	// Special linedefs
+	include("SRB222_misc.cfg", "speciallinedefs_udmf");
+
+	// Default flags for first new thing (As far as 2.2 goes, they're empty just like in 2.1)
+	defaultthingflags
+	{
+	}
+
+	// Generalized actions
+	generalizedlinedefs = false;
+
+	// SECTOR FLAGS
+	sectorflags
+	{
+//		include("SRB222_misc.cfg", "sectorflags");
+	}
+
+	// DEFAULT SECTOR BRIGHTNESS LEVELS
+	sectorbrightness
+	{
+		include("SRB222_misc.cfg", "sectorbrightness");
+	}
+
+	// SECTOR TYPES
+	sectortypes
+	{
+		include("SRB222_sectors.cfg", "sectortypes");
+	}
+
+	// SECTOR RENSERSTYLES
+/*	sectorrenderstyles
+	{
+		include("SRB222_misc.cfg", "sectorrenderstyles");
+	}*/
+
+	// LINEDEF FLAGS
+	linedefflags
+	{
+		include("SRB222_misc.cfg", "linedefflags_udmf");
+	}
+
+	// LINEDEF ACTIVATIONS
+	linedefactivations
+	{
+		include("SRB222_misc.cfg", "linedefactivations_udmf");
+	}
+
+	linedefflagstranslation
+	{
+	}
+
+
+	// LINEDEF RENSERSTYLES
+	linedefrenderstyles
+	{
+		include("SRB222_misc.cfg", "linedefrenderstyles");
+	}
+
+	//SIDEDEF FLAGS
+/*	sidedefflags
+	{
+		include("UDMF_misc.cfg", "sidedefflags");
+	}*/
+
+	// THING FLAGS
+	thingflags
+	{
+		include("SRB222_misc.cfg", "thingflags_udmf");
+	}
+
+	// Thing flags UDMF translation table
+	// This is needed for copy/paste and prefabs to work properly
+	// When the UDMF field name is prefixed with ! it is inverted
+	thingflagstranslation
+	{
+		include("SRB222_misc.cfg", "thingflagstranslation");
+	}
+
+	// THING RENSERSTYLES
+/*	thingrenderstyles
+	{
+		include("SRB222_misc.cfg", "thingrenderstyles");
+	}*/
+
+	// How to compare thing flags (for the stuck things error checker)
+/*	thingflagscompare
+	{
+		include("UDMF_misc.cfg", "thingflagscompare");
+	}*/
+
+	//mxd. Thing flags UDMF translation table
+	// This is needed for copy/paste and prefabs to work properly
+	// When the UDMF field name is prefixed with ! it is inverted
+	thingflagstranslation
+	{
+	}
+
+	// LINEDEF TYPES
+	linedeftypes
+	{
+		include("SRB222_linedefs.cfg", "udmf");
+	}
+}
\ No newline at end of file
diff --git a/extras/conf/Includes/SRB222_linedefs.cfg b/extras/conf/Includes/SRB222_linedefs.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..fdf1918502a582abebe398e2ecb7bc8c98db442e
--- /dev/null
+++ b/extras/conf/Includes/SRB222_linedefs.cfg
@@ -0,0 +1,2486 @@
+doom
+{
+	misc
+	{
+		title = "Miscellaneous";
+
+		0
+		{
+			title = "None";
+			prefix = "(0)";
+		}
+
+		1
+		{
+			title = "Per-Sector Gravity";
+			prefix = "(1)";
+			flags64text = "[6] Flip in reverse gravity";
+		}
+
+		5
+		{
+			title = "Camera Scanner";
+			prefix = "(5)";
+		}
+
+		7
+		{
+			title = "Sector Flat Alignment";
+			prefix = "(7)";
+			flags2048text = "[11] Don't align floor";
+			flags4096text = "[12] Don't align ceiling";
+			flags8192text = "[13] Use texture offsets";
+		}
+
+		10
+		{
+			title = "Culling Plane";
+			prefix = "(10)";
+			flags64text = "[6] Cull only while in sector";
+		}
+
+		13
+		{
+			title = "Heat Wave Effect";
+			prefix = "(13)";
+		}
+
+	    40
+		{
+			title = "Visual Portal Between Tagged Linedefs";
+			prefix = "(40)";
+		}
+
+	    41
+		{
+			title = "Horizon Effect";
+			prefix = "(41)";
+		}
+
+		50
+		{
+			title = "Instantly Lower Floor on Level Load";
+			prefix = "(50)";
+		}
+
+		51
+		{
+			title = "Instantly Raise Ceiling on Level Load";
+			prefix = "(51)";
+		}
+
+		63
+		{
+			title = "Fake Floor/Ceiling Planes";
+			prefix = "(63)";
+		}
+
+		540
+		{
+			title = "Floor Friction";
+			prefix = "(540)";
+		}
+	}
+
+	parameters
+	{
+		title = "Parameters";
+
+		2
+		{
+			title = "Custom Exit";
+			prefix = "(2)";
+			flags2text = "[1] Check emeralds";
+			flags64text = "[6] Skip score tally";
+		}
+
+		3
+		{
+			title = "Zoom Tube Parameters";
+			prefix = "(3)";
+			flags512text = "[9] Ignore player direction";
+		}
+
+		4
+		{
+			title = "Speed Pad Parameters";
+			prefix = "(4)";
+			flags512text = "[9] No teleport to center";
+			flags1024text = "[10] Force spinning frames";
+		}
+
+		8
+		{
+			title = "Special Sector Properties";
+			prefix = "(8)";
+			flags32text = "[5] Invert precipitation";
+			flags64text = "[6] Touch only ceiling";
+			flags128text = "[7] Allow opposite gravity";
+			flags256text = "[8] Touch sector edge";
+			flags512text = "[9] Touch floor or ceiling";
+		}
+
+		9
+		{
+			title = "Chain Parameters";
+			prefix = "(9)";
+			flags32text = "[5] Swing instead of spin";
+			flags64text = "[6] Player-turnable chain";
+			flags128text = "[7] Make chain from end item";
+			flags256text = "[8] Spawn link at origin";
+			flags512text = "[9] Don't clip inside ground";
+			flags1024text = "[10] No distance check";
+		}
+
+		11
+		{
+			title = "Rope Hang Parameters";
+			prefix = "(11)";
+			flags32text = "[5] Don't loop";
+			flags64text = "[6] Static";
+		}
+
+		12
+		{
+			title = "Rock Spawner Parameters";
+			prefix = "(12)";
+			flags64text = "[6] Randomize speed";
+		}
+
+		14
+		{
+			title = "Bustable Block Parameters";
+			prefix = "(14)";
+			flags32text = "[5] Particles launch from center";
+		}
+
+		15
+		{
+			title = "Fan Particle Spawner Parameters";
+			prefix = "(15)";
+		}
+
+		16
+		{
+			title = "Minecart Parameters";
+			prefix = "(16)";
+		}
+
+		64
+		{
+			title = "Continuously Appearing/Disappearing FOF";
+			prefix = "(64)";
+			flags2text = "[1] Use control sector tag";
+			flags64text = "[6] No sound effect";
+		}
+
+		65
+		{
+			title = "Bridge Thinker <disabled>";
+			prefix = "(65)";
+		}
+	}
+
+	polyobject
+	{
+		title = "PolyObject";
+
+		20
+		{
+			title = "First Line";
+			prefix = "(20)";
+		}
+
+		21
+		{
+			title = "Explicitly Include Line <disabled>";
+			prefix = "(21)";
+		}
+
+		22
+		{
+			title = "Parameters";
+			prefix = "(22)";
+			flags64text = "[6] Trigger linedef executor";
+			flags128text = "[7] Intangible";
+			flags256text = "[8] Stopped by pushables";
+			flags512text = "[9] Render flats";
+		}
+
+		30
+		{
+			title = "Waving Flag";
+			prefix = "(30)";
+		}
+
+		31
+		{
+			title = "Displacement by Front Sector";
+			prefix = "(31)";
+		}
+
+		32
+		{
+			title = "Angular Displacement by Front Sector";
+			prefix = "(32)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+	}
+
+	planemove
+	{
+		title = "Plane Movement";
+
+		52
+		{
+			title = "Continuously Falling Sector";
+			prefix = "(52)";
+			flags64text = "[6] Continuously rising";
+		}
+
+		53
+		{
+			title = "Continuous Floor/Ceiling Mover";
+			prefix = "(53)";
+		}
+
+		54
+		{
+			title = "Continuous Floor Mover";
+			prefix = "(54)";
+		}
+
+		55
+		{
+			title = "Continuous Ceiling Mover";
+			prefix = "(55)";
+		}
+
+		56
+		{
+			title = "Continuous Two-Speed Floor/Ceiling Mover";
+			prefix = "(56)";
+		}
+
+		57
+		{
+			title = "Continuous Two-Speed Floor Mover";
+			prefix = "(57)";
+		}
+
+		58
+		{
+			title = "Continuous Two-Speed Ceiling Mover";
+			prefix = "(58)";
+		}
+
+		59
+		{
+			title = "Activate Moving Platform";
+			prefix = "(59)";
+			flags64text = "[6] Move upwards at start";
+		}
+
+		60
+		{
+			title = "Activate Moving Platform (Adjustable Speed)";
+			prefix = "(60)";
+			flags64text = "[6] Move upwards at start";
+		}
+
+		61
+		{
+			title = "Crusher (Ceiling to Floor)";
+			prefix = "(61)";
+			flags512text = "[9] Double, constant speed";
+		}
+
+		62
+		{
+			title = "Crusher (Floor to Ceiling)";
+			prefix = "(62)";
+			flags512text = "[9] Double, constant speed";
+		}
+
+		66
+		{
+			title = "Move Floor by Displacement";
+			prefix = "(66)";
+			flags64text = "[6] Inverse movement";
+		}
+
+		67
+		{
+			title = "Move Ceiling by Displacement";
+			prefix = "(67)";
+			flags64text = "[6] Inverse movement";
+		}
+
+		68
+		{
+			title = "Move Floor and Ceiling by Displacement";
+			prefix = "(68)";
+			flags64text = "[6] Inverse movement";
+		}
+	}
+
+	fofsolid
+	{
+		title = "FOF (solid)";
+
+		100
+		{
+			title = "Solid, Opaque";
+			prefix = "(100)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		101
+		{
+			title = "Solid, Opaque, No Shadow";
+			prefix = "(101)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1DF";
+		}
+
+		102
+		{
+			title = "Solid, Translucent";
+			prefix = "(102)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Render insides";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "195F";
+			flags643dfloorflagsadd = "7C80";
+		}
+
+		103
+		{
+			title = "Solid, Sides Only";
+			prefix = "(103)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1CF";
+		}
+
+		104
+		{
+			title = "Solid, No Sides";
+			prefix = "(104)";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1D7";
+			flags643dfloorflagsremove = "40";
+		}
+
+		105
+		{
+			title = "Solid, Invisible";
+			prefix = "(105)";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "47";
+		}
+
+		140
+		{
+			title = "Intangible from Bottom, Opaque";
+			prefix = "(140)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "200841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		141
+		{
+			title = "Intangible from Bottom, Translucent";
+			prefix = "(141)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			3dfloor = true;
+			3dfloorflags = "200191F";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		142
+		{
+			title = "Intangible from Bottom, Translucent, No Sides";
+			prefix = "(142)";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			3dfloor = true;
+			3dfloorflags = "2001917";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		143
+		{
+			title = "Intangible from Top, Opaque";
+			prefix = "(143)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "400841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		144
+		{
+			title = "Intangible from Top, Translucent";
+			prefix = "(144)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			3dfloor = true;
+			3dfloorflags = "400191F";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		145
+		{
+			title = "Intangible from Top, Translucent, No Sides";
+			prefix = "(145)";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Render insides/block non-plr";
+			3dfloor = true;
+			3dfloorflags = "4001917";
+			flags1283dfloorflagsadd = "7C80";
+			flags643dfloorflagsadd = "40";
+		}
+
+		146
+		{
+			title = "Only Tangible from Sides";
+			prefix = "(146)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "600800F";
+		}
+	}
+
+	fofintangible
+	{
+		title = "FOF (intangible)";
+
+		120
+		{
+			title = "Water, Opaque";
+			prefix = "(120)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "8F39";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		121
+		{
+			title = "Water, Translucent";
+			prefix = "(121)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "9F39";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		122
+		{
+			title = "Water, Opaque, No Sides";
+			prefix = "(122)";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "F31";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		123
+		{
+			title = "Water, Translucent, No Sides";
+			prefix = "(123)";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "1F31";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		124
+		{
+			title = "Goo Water, Translucent";
+			prefix = "(124)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "209F39";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		125
+		{
+			title = "Goo Water, Translucent, No Sides";
+			prefix = "(125)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Use two light levels";
+			flags512text = "[9] Use target light level";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "201F31";
+			flags643dfloorflagsadd = "20000";
+			flags5123dfloorflagsadd = "80000000";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		220
+		{
+			title = "Intangible, Opaque";
+			prefix = "(220)";
+			flags8text = "[3] Slope skew sides";
+			3dfloor = true;
+			3dfloorflags = "8F19";
+		}
+
+		221
+		{
+			title = "Intangible, Translucent";
+			prefix = "(221)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Cast shadow";
+			3dfloor = true;
+			3dfloorflags = "1B59";
+			flags643dfloorflagsremove = "40";
+		}
+
+		222
+		{
+			title = "Intangible, Sides Only";
+			prefix = "(222)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Cast shadow";
+			3dfloor = true;
+			3dfloorflags = "8249";
+			flags643dfloorflagsremove = "240";
+		}
+
+		223
+		{
+			title = "Intangible, Invisible";
+			prefix = "(223)";
+			3dfloor = true;
+			3dfloorflags = "41";
+		}
+	}
+
+	fofmoving
+	{
+		title = "FOF (moving)";
+
+		150
+		{
+			title = "Air Bobbing";
+			prefix = "(150)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		151
+		{
+			title = "Air Bobbing (Adjustable)";
+			prefix = "(151)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		152
+		{
+			title = "Reverse Air Bobbing (Adjustable)";
+			prefix = "(152)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		160
+		{
+			title = "Floating, Bobbing";
+			prefix = "(160)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "4019F";
+		}
+
+		190
+		{
+			title = "Rising Platform, Solid, Opaque";
+			prefix = "(190)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		191
+		{
+			title = "Rising Platform, Solid, Opaque, No Shadow";
+			prefix = "(191)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1DF";
+		}
+
+		192
+		{
+			title = "Rising Platform, Solid, Translucent";
+			prefix = "(192)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "195F";
+		}
+
+		193
+		{
+			title = "Rising Platform, Solid, Invisible";
+			prefix = "(193)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "47";
+		}
+
+		194
+		{
+			title = "Rising Platform, Intangible from Bottom, Opaque";
+			prefix = "(194)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash, no shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "200841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		195
+		{
+			title = "Rising Platform, Intangible from Bottom, Translucent";
+			prefix = "(195)";
+			flags2text = "[1] Sink when stepped on";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash, no shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "2009D1F";
+			flags643dfloorflagsadd = "40";
+		}
+	}
+
+	fofcrumbling
+	{
+		title = "FOF (crumbling)";
+
+		170
+		{
+			title = "Crumbling, Respawn";
+			prefix = "(170)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "10019F";
+		}
+
+		171
+		{
+			title = "Crumbling, No Respawn";
+			prefix = "(171)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "80019F";
+		}
+
+		172
+		{
+			title = "Crumbling, Respawn, Intangible from Bottom";
+			prefix = "(172)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "210841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		173
+		{
+			title = "Crumbling, No Respawn, Intangible from Bottom";
+			prefix = "(173)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "218841F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		174
+		{
+			title = "Crumbling, Respawn, Int. from Bottom, Translucent";
+			prefix = "(174)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "210959F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		175
+		{
+			title = "Crumbling, No Respawn, Int. from Bottom, Translucent";
+			prefix = "(175)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Don't cast shadow";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "218959F";
+			flags643dfloorflagsadd = "40";
+		}
+
+		176
+		{
+			title = "Crumbling, Respawn, Floating, Bobbing";
+			prefix = "(176)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "14019F";
+		}
+
+		177
+		{
+			title = "Crumbling, No Respawn, Floating, Bobbing";
+			prefix = "(177)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1C019F";
+		}
+
+		178
+		{
+			title = "Crumbling, Respawn, Floating";
+			prefix = "(178)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "14019F";
+		}
+
+		179
+		{
+			title = "Crumbling, No Respawn, Floating";
+			prefix = "(179)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "1C019F";
+		}
+
+		180
+		{
+			title = "Crumbling, Respawn, Air Bobbing";
+			prefix = "(180)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags64text = "[6] Spindash to move";
+			flags128text = "[7] Only block non-players";
+			3dfloor = true;
+			3dfloorflags = "10019F";
+		}
+	}
+
+	fofspecial
+	{
+		title = "FOF (special)";
+
+		200
+		{
+			title = "Light Block";
+			prefix = "(200)";
+			3dfloor = true;
+			3dfloorflags = "20201";
+		}
+
+		201
+		{
+			title = "Half Light Block";
+			prefix = "(201)";
+			3dfloor = true;
+			3dfloorflags = "201";
+		}
+
+		202
+		{
+			title = "Fog Block";
+			prefix = "(202)";
+			3dfloor = true;
+			3dfloorflags = "3EF19";
+		}
+
+		250
+		{
+			title = "Mario Block";
+			prefix = "(250)";
+			flags32text = "[5] Invisible block";
+			flags64text = "[6] Brick block";
+			3dfloor = true;
+			3dfloorflags = "40019F";
+		}
+
+		251
+		{
+			title = "Thwomp Block";
+			prefix = "(251)";
+			flags512text = "[9] Custom crushing sound";
+			flags1024text = "[10] Custom speed";
+			3dfloor = true;
+			3dfloorflags = "19F";
+		}
+
+		252
+		{
+			title = "Shatter Block";
+			prefix = "(252)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Shatter only from below";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "8800019";
+			flags643dfloorflagsadd = "200006";
+		}
+
+		253
+		{
+			title = "Shatter Block, Translucent";
+			prefix = "(253)";
+			flags8text = "[3] Slope skew sides";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "8801019";
+		}
+
+		254
+		{
+			title = "Bustable Block";
+			prefix = "(254)";
+			flags8text = "[3] Slope skew sides";
+			flags64text = "[6] Strong characters only";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "80001F";
+			flags643dfloorflagsadd = "20000000";
+		}
+
+		255
+		{
+			title = "Spin-Bustable Block";
+			prefix = "(255)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "1080001F";
+		}
+
+		256
+		{
+			title = "Spin-Bustable Block, Translucent";
+			prefix = "(256)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorflags = "1080101F";
+		}
+
+		257
+		{
+			title = "Quicksand";
+			prefix = "(257)";
+			flags8text = "[3] Slope skew sides";
+			flags1024text = "[10] Ripple effect";
+			3dfloor = true;
+			3dfloorflags = "1008219";
+			flags10243dfloorflagsadd = "40000000";
+		}
+
+		258
+		{
+			title = "Laser";
+			prefix = "(258)";
+			flags8text = "[3] Slope skew sides";
+			flags32text = "[5] Don't damage bosses";
+			3dfloor = true;
+			3dfloorflags = "959";
+		}
+
+		259
+		{
+			title = "Custom FOF";
+			prefix = "(259)";
+			flags32text = "[5] Only block player";
+			flags128text = "[7] Only block non-players";
+			flags512text = "[9] Shattered by pushables";
+			flags1024text = "[10] Trigger linedef executor";
+			3dfloor = true;
+			3dfloorcustom = true;
+		}
+	}
+
+	linedeftrigger
+	{
+		title = "Linedef Executor Trigger";
+
+		300
+		{
+			title = "Continuous";
+			prefix = "(300)";
+		}
+
+		301
+		{
+			title = "Each Time";
+			prefix = "(301)";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		302
+		{
+			title = "Once";
+			prefix = "(302)";
+		}
+
+		303
+		{
+			title = "Ring Count - Continuous";
+			prefix = "(303)";
+			flags2text = "[1] Rings greater or equal";
+			flags64text = "[6] Rings less or equal";
+			flags512text = "[9] Consider all players";
+		}
+
+		304
+		{
+			title = "Ring Count - Once";
+			prefix = "(304)";
+			flags2text = "[1] Rings greater or equal";
+			flags64text = "[6] Rings less or equal";
+			flags512text = "[9] Consider all players";
+		}
+
+		305
+		{
+			title = "Character Ability - Continuous";
+			prefix = "(305)";
+		}
+
+		306
+		{
+			title = "Character Ability - Each Time";
+			prefix = "(306)";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		307
+		{
+			title = "Character Ability - Once";
+			prefix = "(307)";
+		}
+
+		308
+		{
+			title = "Race Only - Once";
+			prefix = "(308)";
+		}
+
+		309
+		{
+			title = "CTF Red Team - Continuous";
+			prefix = "(309)";
+		}
+
+		310
+		{
+			title = "CTF Red Team - Each Time";
+			prefix = "(310)";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		311
+		{
+			title = "CTF Blue Team - Continuous";
+			prefix = "(311)";
+		}
+
+		312
+		{
+			title = "CTF Blue Team - Each Time";
+			prefix = "(312)";
+			flags16384text = "[14] Also trigger on exit";
+		}
+
+		313
+		{
+			title = "No More Enemies - Once";
+			prefix = "(313)";
+		}
+
+		314
+		{
+			title = "Number of Pushables - Continuous";
+			prefix = "(314)";
+			flags64text = "[6] Number greater or equal";
+			flags512text = "[9] Number less";
+		}
+
+		315
+		{
+			title = "Number of Pushables - Once";
+			prefix = "(315)";
+			flags64text = "[6] Number greater or equal";
+			flags512text = "[9] Number less";
+		}
+
+		317
+		{
+			title = "Condition Set Trigger - Continuous";
+			prefix = "(317)";
+		}
+
+		318
+		{
+			title = "Condition Set Trigger - Once";
+			prefix = "(318)";
+		}
+
+		319
+		{
+			title = "Unlockable - Continuous";
+			prefix = "(319)";
+		}
+
+		320
+		{
+			title = "Unlockable - Once";
+			prefix = "(320)";
+		}
+
+		321
+		{
+			title = "Trigger After X Calls - Continuous";
+			prefix = "(321)";
+			flags64text = "[6] Trigger more than once";
+
+		}
+
+		322
+		{
+			title = "Trigger After X Calls - Each Time";
+			prefix = "(322)";
+			flags64text = "[6] Trigger more than once";
+		}
+
+		323
+		{
+			title = "NiGHTSerize - Each Time";
+			prefix = "(323)";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run only if player is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags16384text = "[14] Run if no more mares";
+			flags32768text = "[15] Run if player is not NiGHTS";
+		}
+
+		324
+		{
+			title = "NiGHTSerize - Once";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run only if player is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags16384text = "[14] Run if no more mares";
+			flags32768text = "[15] Run if player is not NiGHTS";
+			prefix = "(324)";
+		}
+
+		325
+		{
+			title = "De-NiGHTSerize - Each Time";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run if anyone is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags32768text = "[15] Run if no one is NiGHTS";
+			prefix = "(325)";
+		}
+
+		326
+		{
+			title = "De-NiGHTSerize - Once";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run if anyone is NiGHTS";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags32768text = "[15] Run if no one is NiGHTS";
+			prefix = "(326)";
+		}
+
+		327
+		{
+			title = "NiGHTS Lap - Each Time";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			prefix = "(327)";
+		}
+
+		328
+		{
+			title = "NiGHTS Lap - Once";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			prefix = "(328)";
+		}
+
+		329
+		{
+			title = "Ideya Capture Touch - Each Time";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run regardless of spheres";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags16384text = "[14] Only if not enough spheres";
+			flags32768text = "[15] Run when entering Capture";
+			prefix = "(329)";
+		}
+
+		330
+		{
+			title = "Ideya Capture Touch - Once";
+			flags2text = "[1] Mare >= Front X Offset";
+			flags8text = "[3] Run regardless of spheres";
+			flags16text = "[4] Count from lowest of players";
+			flags32text = "[5] Lap <= Front Y Offset";
+			flags64text = "[6] Mare <= Front X Offset";
+			flags128text = "[7] Lap >= Front Y Offset";
+			flags256text = "[8] Count laps from Bonus Time";
+			flags512text = "[9] Count from triggering player";
+			flags16384text = "[14] Only if not enough spheres";
+			flags32768text = "[15] Run when entering Capture";
+			prefix = "(330)";
+		}
+
+		331
+		{
+			title = "Player Skin - Continuous";
+			flags64text = "[6] Disable for this skin";
+			prefix = "(331)";
+		}
+
+		332
+		{
+			title = "Player Skin - Each Time";
+			flags64text = "[6] Disable for this skin";
+			prefix = "(332)";
+		}
+
+		333
+		{
+			title = "Player Skin - Once";
+			flags64text = "[6] Disable for this skin";
+			prefix = "(333)";
+		}
+
+		399
+		{
+			title = "Level Load";
+			prefix = "(399)";
+		}
+	}
+
+	linedefexecsector
+	{
+		title = "Linedef Executor (sector)";
+
+		400
+		{
+			title = "Set Tagged Sector's Floor Height/Texture";
+			prefix = "(400)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Keep floor flat";
+		}
+
+		401
+		{
+			title = "Set Tagged Sector's Ceiling Height/Texture";
+			prefix = "(401)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		402
+		{
+			title = "Set Tagged Sector's Light Level";
+			prefix = "(402)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		409
+		{
+			title = "Change Tagged Sector's Tag";
+			prefix = "(409)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		410
+		{
+			title = "Change Front Sector's Tag";
+			prefix = "(410)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		416
+		{
+			title = "Start Adjustable Flickering Light";
+			prefix = "(416)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		417
+		{
+			title = "Start Adjustable Pulsating Light";
+			prefix = "(417)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		418
+		{
+			title = "Start Adjustable Blinking Light (unsynchronized)";
+			prefix = "(418)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		419
+		{
+			title = "Start Adjustable Blinking Light (synchronized)";
+			prefix = "(419)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Second level from back";
+		}
+
+		420
+		{
+			title = "Fade Light Level";
+			prefix = "(420)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Set params by X/Y offsets";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+		}
+
+		421
+		{
+			title = "Stop Lighting Effect";
+			prefix = "(421)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		435
+		{
+			title = "Change Plane Scroller Direction";
+			prefix = "(435)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+	}
+
+	linedefexecplane
+	{
+		title = "Linedef Executor (plane movement)";
+
+		403
+		{
+			title = "Move Tagged Sector's Floor";
+			prefix = "(403)";
+			flags2text = "[1] Trigger linedef executor";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Change floor flat";
+		}
+
+		404
+		{
+			title = "Move Tagged Sector's Ceiling";
+			prefix = "(404)";
+			flags2text = "[1] Trigger linedef executor";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Change ceiling flat";
+		}
+
+		405
+		{
+			title = "Move Floor According to Front Texture Offsets";
+			prefix = "(405)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Move instantly";
+		}
+
+		407
+		{
+			title = "Move Ceiling According to Front Texture Offsets";
+			prefix = "(407)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Move instantly";
+		}
+
+		411
+		{
+			title = "Stop Plane Movement";
+			prefix = "(411)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		428
+		{
+			title = "Start Platform Movement";
+			prefix = "(428)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Move upwards at start";
+		}
+
+		429
+		{
+			title = "Crush Ceiling Once";
+			prefix = "(429)";
+			flags8text = "[3] Set delay by backside sector";
+			flags512text = "[9] Double, constant speed";
+		}
+
+		430
+		{
+			title = "Crush Floor Once";
+			prefix = "(430)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		431
+		{
+			title = "Crush Floor and Ceiling Once";
+			prefix = "(431)";
+			flags8text = "[3] Set delay by backside sector";
+			flags512text = "[9] Double, constant speed";
+		}
+	}
+
+	linedefexecplayer
+	{
+		title = "Linedef Executor (player/object)";
+
+		412
+		{
+			title = "Teleporter";
+			prefix = "(412)";
+			flags2text = "[1] Silent";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Retain angle";
+			flags256text = "[8] Relative, silent";
+			flags512text = "[9] Retain momentum";
+		}
+
+		425
+		{
+			title = "Change Object State";
+			prefix = "(425)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		426
+		{
+			title = "Stop Object";
+			prefix = "(426)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Teleport to sector center";
+		}
+
+		427
+		{
+			title = "Award Score";
+			prefix = "(427)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		432
+		{
+			title = "Enable/Disable 2D Mode";
+			prefix = "(432)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Return to 3D";
+		}
+
+		433
+		{
+			title = "Enable/Disable Gravity Flip";
+			prefix = "(433)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Return to normal";
+		}
+
+		434
+		{
+			title = "Award Power-Up";
+			prefix = "(434)";
+			flags2text = "[1] Use back upper texture";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] No time limit";
+		}
+
+		437
+		{
+			title = "Disable Player Control";
+			prefix = "(437)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Allow jumping";
+		}
+
+		438
+		{
+			title = "Change Object Size";
+			prefix = "(438)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		442
+		{
+			title = "Change Object Type State";
+			prefix = "(442)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		457
+		{
+			title = "Track Object's Angle";
+			prefix = "(457)";
+			flags8text = "[3] Set delay by backside sector";
+			flags128text = "[7] Don't stop after first fail";
+		}
+
+		458
+		{
+			title = "Stop Tracking Object's Angle";
+			prefix = "(458)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		460
+		{
+			title = "Award Rings";
+			prefix = "(460)";
+		}
+
+		461
+		{
+			title = "Spawn Object";
+			prefix = "(461)";
+			flags64text = "[6] Spawn inside a range";
+		}
+
+		462
+		{
+			title = "Stop Timer/Exit Stage in Record Attack";
+			prefix = "(462)";
+		}
+	}
+
+	linedefexecmisc
+	{
+		title = "Linedef Executor (misc.)";
+
+		413
+		{
+			title = "Change Music";
+			prefix = "(413)";
+			flags2text = "[1] Keep after death";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Seek to current song position";
+			flags64text = "[6] For everyone";
+			flags128text = "[7] Fade to custom volume";
+			flags512text = "[9] Don't loop";
+			flags16384text = "[14] Force music reload";
+		}
+
+		414
+		{
+			title = "Play Sound Effect";
+			prefix = "(414)";
+			flags2text = "[1] From calling sector";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] From nowhere for triggerer";
+			flags512text = "[9] For everyone";
+			flags1024text = "[10] From tagged sectors";
+		}
+
+		415
+		{
+			title = "Run Script";
+			prefix = "(415)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		422
+		{
+			title = "Switch to Cut-Away View";
+			prefix = "(422)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Adjust pitch";
+		}
+
+		423
+		{
+			title = "Change Sky";
+			prefix = "(423)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] For everyone";
+		}
+
+		424
+		{
+			title = "Change Weather";
+			prefix = "(424)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] For everyone";
+		}
+
+		436
+		{
+			title = "Shatter FOF";
+			prefix = "(436)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		439
+		{
+			title = "Change Tagged Linedef's Textures";
+			prefix = "(439)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Only existing";
+		}
+
+		440
+		{
+			title = "Start Metal Sonic Race";
+			prefix = "(440)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		441
+		{
+			title = "Condition Set Trigger";
+			prefix = "(441)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		443
+		{
+			title = "Call Lua Function";
+			prefix = "(443)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		444
+		{
+			title = "Earthquake";
+			prefix = "(444)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+
+		445
+		{
+			title = "Make FOF Disappear/Reappear";
+			prefix = "(445)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Reappear";
+		}
+
+		446
+		{
+			title = "Make FOF Crumble";
+			prefix = "(446)";
+			flags2text = "[1] Flags determine respawn";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't respawn";
+		}
+
+		447
+		{
+			title = "Change Tagged Sector's Colormap";
+			prefix = "(447)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Front X/Y = Alpha";
+			flags32text = "[5] Subtract Red value";
+			flags64text = "[6] Subtract Green value";
+			flags128text = "[7] Subtract Blue value";
+			flags256text = "[8] Calc relative values";
+			flags32768text = "[15] Use back side colormap";
+		}
+
+		448
+		{
+			title = "Change Skybox";
+			prefix = "(448)";
+			flags2text = "[1] Change centerpoint";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] For everyone";
+			flags512text = "[9] Don't change viewpoint";
+		}
+
+		450
+		{
+			title = "Execute Linedef Executor (specific tag)";
+			prefix = "(450)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		451
+		{
+			title = "Execute Linedef Executor (random tag in range)";
+			prefix = "(451)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		452
+		{
+			title = "Set FOF Translucency";
+			prefix = "(452)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Do not handle FF_TRANS";
+			flags256text = "[8] Set relative to current val";
+		}
+
+		453
+		{
+			title = "Fade FOF";
+			prefix = "(453)";
+			flags2text = "[1] Do not handle FF_EXISTS";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] No collision during fade";
+			flags64text = "[6] Do not handle FF_TRANS";
+			flags128text = "[7] Do not handle lighting";
+			flags256text = "[8] Set relative to current val";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+			flags16384text = "[14] Do not handle collision";
+			flags32768text = "[15] Use exact alpha in OGL";
+		}
+
+		454
+		{
+			title = "Stop Fading FOF";
+			prefix = "(454)";
+			flags2text = "[1] Do not finalize collision";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		455
+		{
+			title = "Fade Tagged Sector's Colormap";
+			prefix = "(455)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Front X/Y = Alpha";
+			flags32text = "[5] Subtract Red value";
+			flags64text = "[6] Subtract Green value";
+			flags128text = "[7] Subtract Blue value";
+			flags256text = "[8] Calc relative values";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+			flags16384text = "[14] Fade from invisible black";
+			flags32768text = "[15] Use back side colormap";
+		}
+
+		456
+		{
+			title = "Stop Fading Tagged Sector's Colormap";
+			prefix = "(456)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		459
+		{
+			title = "Control Text Prompt";
+			prefix = "(459)";
+			flags2text = "[1] Close text prompt";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Run executor tag on close";
+			flags64text = "[6] For everyone";
+			flags128text = "[7] Do not block controls";
+			flags256text = "[8] Do not freeze time";
+			flags32768text = "[15] Find prompt by name";
+		}
+	}
+
+	linedefexecpoly
+	{
+		title = "Linedef Executor (polyobject)";
+
+		480
+		{
+			title = "Door Slide";
+			prefix = "(480)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		481
+		{
+			title = "Door Swing";
+			prefix = "(481)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		482
+		{
+			title = "Move";
+			prefix = "(482)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		483
+		{
+			title = "Move, Override";
+			prefix = "(483)";
+			flags8text = "[3] Set delay by backside sector";
+		}
+
+		484
+		{
+			title = "Rotate Right";
+			prefix = "(484)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		485
+		{
+			title = "Rotate Right, Override";
+			prefix = "(485)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		486
+		{
+			title = "Rotate Left";
+			prefix = "(486)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		487
+		{
+			title = "Rotate Left, Override";
+			prefix = "(487)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Don't turn players";
+			flags512text = "[9] Turn all objects";
+		}
+
+		488
+		{
+			title = "Move by Waypoints";
+			prefix = "(488)";
+			flags8text = "[3] Set delay by backside sector";
+			flags32text = "[5] Reverse order";
+			flags128text = "[7] There and back";
+			flags256text = "[8] Return when done";
+			flags512text = "[9] Loop movement";
+		}
+
+		489
+		{
+			title = "Turn Invisible, Intangible";
+			prefix = "(489)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Only invisible";
+		}
+
+		490
+		{
+			title = "Turn Visible, Tangible";
+			prefix = "(490)";
+			flags8text = "[3] Set delay by backside sector";
+			flags64text = "[6] Only visible";
+		}
+
+		491
+		{
+			title = "Set Translucency";
+			prefix = "(491)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Set raw alpha by Front X";
+			flags256text = "[8] Calc relative values";
+		}
+
+		492
+		{
+			title = "Fade Translucency";
+			prefix = "(492)";
+			flags8text = "[3] Set delay by backside sector";
+			flags16text = "[4] Set raw alpha by Front X";
+			flags32text = "[5] No collision during fade";
+			flags256text = "[8] Calc relative values";
+			flags512text = "[9] Speed = Tic Duration";
+			flags1024text = "[10] Override existing fade";
+			flags16384text = "[14] Do not handle collision";
+		}
+	}
+
+	wallscroll
+	{
+		title = "Wall Scrolling";
+
+		500
+		{
+			title = "Scroll Wall Front Side Left";
+			prefix = "(500)";
+		}
+
+		501
+		{
+			title = "Scroll Wall Front Side Right";
+			prefix = "(501)";
+		}
+
+		502
+		{
+			title = "Scroll Wall According to Linedef";
+			prefix = "(502)";
+		}
+
+		503
+		{
+			title = "Scroll Wall According to Linedef (Accelerative)";
+			prefix = "(503)";
+		}
+
+		504
+		{
+			title = "Scroll Wall According to Linedef (Displacement)";
+			prefix = "(504)";
+		}
+
+		505
+		{
+			title = "Scroll Texture by Front Side Offsets";
+			prefix = "(505)";
+		}
+
+		506
+		{
+			title = "Scroll Texture by Back Side Offsets";
+			prefix = "(506)";
+		}
+	}
+
+	planescroll
+	{
+		title = "Plane Scrolling";
+
+		510
+		{
+			title = "Scroll Floor Texture";
+			prefix = "(510)";
+		}
+
+		511
+		{
+			title = "Scroll Floor Texture (Accelerative)";
+			prefix = "(511)";
+		}
+
+		512
+		{
+			title = "Scroll Floor Texture (Displacement)";
+			prefix = "(512)";
+		}
+
+		513
+		{
+			title = "Scroll Ceiling Texture";
+			prefix = "(513)";
+		}
+
+		514
+		{
+			title = "Scroll Ceiling Texture (Accelerative)";
+			prefix = "(514)";
+		}
+
+		515
+		{
+			title = "Scroll Ceiling Texture (Displacement)";
+			prefix = "(515)";
+		}
+
+		520
+		{
+			title = "Carry Objects on Floor";
+			prefix = "(520)";
+		}
+
+		521
+		{
+			title = "Carry Objects on Floor (Accelerative)";
+			prefix = "(521)";
+			flags64text = "[6] Even across edges";
+		}
+
+		522
+		{
+			title = "Carry Objects on Floor (Displacement)";
+			prefix = "(522)";
+		}
+
+		523
+		{
+			title = "Carry Objects on Ceiling";
+			prefix = "(523)";
+			flags64text = "[6] Even across edges";
+		}
+
+		524
+		{
+			title = "Carry Objects on Ceiling (Accelerative)";
+			prefix = "(524)";
+		}
+
+		525
+		{
+			title = "Carry Objects on Ceiling (Displacement)";
+			prefix = "(525)";
+		}
+
+		530
+		{
+			title = "Scroll Floor Texture and Carry Objects";
+			prefix = "(530)";
+			flags64text = "[6] Even across edges";
+		}
+
+		531
+		{
+			title = "Scroll Floor Texture and Carry Objects (Accelerative)";
+			prefix = "(531)";
+		}
+
+		532
+		{
+			title = "Scroll Floor Texture and Carry Objects (Displacement)";
+			prefix = "(532)";
+		}
+
+		533
+		{
+			title = "Scroll Ceiling Texture and Carry Objects";
+			prefix = "(533)";
+			flags64text = "[6] Even across edges";
+		}
+
+		534
+		{
+			title = "Scroll Ceiling Texture and Carry Objects (Accelerative)";
+			prefix = "(534)";
+		}
+
+		535
+		{
+			title = "Scroll Ceiling Texture and Carry Objects (Displacement)";
+			prefix = "(535)";
+		}
+	}
+
+	pusher
+	{
+		title = "Pusher";
+
+		541
+		{
+			title = "Wind";
+			prefix = "(541)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Even across edges";
+		}
+
+		542
+		{
+			title = "Upwards Wind";
+			prefix = "(542)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Even across edges";
+		}
+
+		543
+		{
+			title = "Downwards Wind";
+			prefix = "(543)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Even across edges";
+		}
+
+		544
+		{
+			title = "Current";
+			prefix = "(544)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Even across edges";
+		}
+
+		545
+		{
+			title = "Upwards Current";
+			prefix = "(545)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Even across edges";
+		}
+
+		546
+		{
+			title = "Downwards Current";
+			prefix = "(546)";
+			flags512text = "[9] Player slides";
+			flags64text = "[6] Even across edges";
+		}
+
+		547
+		{
+			title = "Push/Pull";
+			prefix = "(547)";
+		}
+	}
+
+	light
+	{
+		title = "Lighting";
+
+		600
+		{
+			title = "Floor Lighting";
+			prefix = "(600)";
+		}
+
+		601
+		{
+			title = "Ceiling Lighting";
+			prefix = "(601)";
+		}
+
+		602
+		{
+			title = "Adjustable Pulsating Light";
+			prefix = "(602)";
+		}
+
+		603
+		{
+			title = "Adjustable Flickering Light";
+			prefix = "(603)";
+		}
+
+		604
+		{
+			title = "Adjustable Blinking Light (unsynchronized)";
+			prefix = "(604)";
+		}
+
+		605
+		{
+			title = "Adjustable Blinking Light (synchronized)";
+			prefix = "(605)";
+		}
+
+		606
+		{
+			title = "Colormap";
+			prefix = "(606)";
+		}
+	}
+
+	slope
+	{
+		title = "Slope";
+
+		700
+		{
+			title = "Slope Frontside Floor";
+			prefix = "(700)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 1;
+		}
+
+		701
+		{
+			title = "Slope Frontside Ceiling";
+			prefix = "(701)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 2;
+		}
+
+		702
+		{
+			title = "Slope Frontside Floor and Ceiling";
+			prefix = "(702)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 3;
+		}
+
+		703
+		{
+			title = "Slope Frontside Floor and Backside Ceiling";
+			prefix = "(703)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 9;
+		}
+
+		704
+		{
+			title = "Slope Frontside Floor by 3 Tagged Vertex Things";
+			prefix = "(704)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 0;
+		}
+
+		705
+		{
+			title = "Slope Frontside Ceiling by 3 Tagged Vertex Things";
+			prefix = "(705)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 1;
+		}
+
+		710
+		{
+			title = "Slope Backside Floor";
+			prefix = "(710)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 4;
+		}
+
+		711
+		{
+			title = "Slope Backside Ceiling";
+			prefix = "(711)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 8;
+		}
+
+		712
+		{
+			title = "Slope Backside Floor and Ceiling";
+			prefix = "(712)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 12;
+		}
+
+		713
+		{
+			title = "Slope Backside Floor and Frontside Ceiling";
+			prefix = "(713)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			slope = "regular";
+			slopeargs = 6;
+		}
+
+		714
+		{
+			title = "Slope Backside Floor by 3 Tagged Vertex Things";
+			prefix = "(714)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 2;
+		}
+
+		715
+		{
+			title = "Slope Backside Ceiling by 3 Tagged Vertex Things";
+			prefix = "(715)";
+			flags2048text = "[11] No physics";
+			flags4096text = "[12] Dynamic";
+			flags8192text = "[13] Use tag and offsets";
+			slope = "vertex";
+			slopeargs = 3;
+		}
+
+		720
+		{
+			title = "Copy Frontside Floor Slope from Line Tag";
+			prefix = "(720)";
+			slope = "copy";
+			slopeargs = 1;
+		}
+
+		721
+		{
+			title = "Copy Frontside Ceiling Slope from Line Tag";
+			prefix = "(721)";
+			slope = "copy";
+			slopeargs = 2;
+		}
+
+		722
+		{
+			title = "Copy Frontside Floor and Ceiling Slope from Line Tag";
+			prefix = "(722)";
+			slope = "copy";
+			slopeargs = 3;
+		}
+
+		799
+		{
+			title = "Set Tagged Dynamic Slope Vertex to Front Sector Height";
+			prefix = "(799)";
+		}
+	}
+
+	transwall
+	{
+		title = "Translucent Wall";
+
+		900
+		{
+			title = "90% Opaque";
+			prefix = "(900)";
+		}
+
+		901
+		{
+			title = "80% Opaque";
+			prefix = "(901)";
+		}
+
+		902
+		{
+			title = "70% Opaque";
+			prefix = "(902)";
+		}
+
+		903
+		{
+			title = "60% Opaque";
+			prefix = "(903)";
+		}
+
+		904
+		{
+			title = "50% Opaque";
+			prefix = "(904)";
+		}
+
+		905
+		{
+			title = "40% Opaque";
+			prefix = "(905)";
+		}
+
+		906
+		{
+			title = "30% Opaque";
+			prefix = "(906)";
+		}
+
+		907
+		{
+			title = "20% Opaque";
+			prefix = "(907)";
+		}
+
+		908
+		{
+			title = "10% Opaque";
+			prefix = "(908)";
+		}
+
+		909
+		{
+			title = "Fog Wall";
+			prefix = "(909)";
+		}
+	}
+}
+
+udmf
+{
+	misc
+	{
+		title = "Miscellaneous";
+
+		0
+		{
+			title = "None";
+			prefix = "(0)";
+		}
+	}
+}
\ No newline at end of file
diff --git a/extras/conf/Includes/SRB222_misc.cfg b/extras/conf/Includes/SRB222_misc.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..ce23388b2362094d097ff3893a106e6664a0b255
--- /dev/null
+++ b/extras/conf/Includes/SRB222_misc.cfg
@@ -0,0 +1,726 @@
+linedefflags
+{
+	1 = "[0] Impassable";
+	2 = "[1] Block Enemies";
+	4 = "[2] Double-Sided";
+	8 = "[3] Upper Unpegged";
+	16 = "[4] Lower Unpegged";
+	32 = "[5] Slope Skew (E1)";
+	64 = "[6] Not Climbable";
+	128 = "[7] No Midtexture Skew (E2)";
+	256 = "[8] Peg Midtexture (E3)";
+	512 = "[9] Solid Midtexture (E4)";
+	1024 = "[10] Repeat Midtexture (E5)";
+	2048 = "[11] Netgame Only";
+	4096 = "[12] No Netgame";
+	8192 = "[13] Effect 6";
+	16384 = "[14] Bouncy Wall";
+	32768 = "[15] Transfer Line";
+}
+
+
+// LINEDEF ACTIVATIONS
+// Make sure these are in order from lowest value to highest value
+linedefactivations
+{
+}
+
+
+// Linedef flags UDMF translation table
+// This is needed for copy/paste and prefabs to work properly
+// When the UDMF field name is prefixed with ! it is inverted
+linedefflagstranslation
+{
+	1 = "blocking";
+	2 = "blockmonsters";
+	4 = "twosided";
+	8 = "dontpegtop";
+	16 = "dontpegbottom";
+	32 = "skewtd";
+	64 = "noclimb";
+	128 = "noskew";
+	256 = "midpeg";
+	512 = "midsolid";
+	1024 = "wrapmidtex";
+	2048 = "netonly";
+	4096 = "nonet";
+	8192 = "effect6";
+	16384 = "bouncy";
+	32768 = "transfer";
+}
+
+
+linedefflags_udmf
+{
+	blocking = "Impassable";
+	blockmonsters = "Block Enemies";
+	twosided = "Double-Sided";
+	dontpegtop = "Upper Unpegged";
+	dontpegbottom = "Lower Unpegged";
+	skewtd = "Slope Skew";
+	noclimb = "Not Climbable";
+	noskew = "No Midtexture Skew";
+	midpeg = "Peg Midtexture";
+	midsolid = "Solid Midtexture";
+	wrapmidtex = "Repeat Midtexture";
+//	netonly = "Netgame-Only special";
+//	nonet = "No netgame special";
+//	effect6 = "Effect 6";
+	bouncy = "Bouncy Wall";
+//	transfer = "Transfer Line";
+}
+
+
+linedefactivations_udmf
+{
+	notriggerorder = "Out of Order";
+	netonly = "Netgame-Only";
+	nonet = "No netgame";
+}
+
+sidedefflags
+{
+	clipmidtex = "Clip middle texture";
+	wrapmidtex = "Wrap middle texture";
+	smoothlighting = "Smooth lighting";
+	nofakecontrast = "Even lighting";
+	nodecals = "No decals";
+	lightfog = "Use sidedef brightness on fogged walls";
+}
+
+//RENDER STYLES
+thingrenderstyles
+{
+}
+
+linedefrenderstyles
+{
+	translucent = "Translucent";
+	fog = "Fog";
+}
+
+sectorrenderstyles
+{
+}
+
+thingflags
+{
+	1 = "[1] Extra";
+	2 = "[2] Flip";
+	4 = "[4] Special";
+	8 = "[8] Ambush";
+}
+
+// THING FLAGS
+thingflags_udmf
+{
+	extra = "Extra";
+	flip = "Flip";
+	special = "Special";
+	ambush = "Ambush";
+}
+
+
+// Thing flags UDMF translation table
+// This is needed for copy/paste and prefabs to work properly
+// When the UDMF field name is prefixed with ! it is inverted
+thingflagstranslation
+{
+	1 = "extra";
+	2 = "flip";
+	4 = "special";
+	8 = "ambush";
+}
+
+
+// DEFAULT SECTOR BRIGHTNESS LEVELS
+sectorbrightness
+{
+	255;
+	248;
+	240;
+	232;
+	224;
+	216;
+	208;
+	200;
+	192;
+	184;
+	176;
+	168;
+	160;
+	152;
+	144;
+	136;
+	128;
+	120;
+	112;
+	104;
+	96;
+	88;
+	80;
+	72;
+	64;
+	56;
+	48;
+	40;
+	32;
+	24;
+	16;
+	8;
+	0;
+}
+
+/*
+TEXTURES AND FLAT SOURCES
+This tells Doom Builder where to find the information for textures
+and flats in the IWAD file, Addition WAD file and Map WAD file.
+
+Start and end lumps must be given in a structure (of which the
+key name doesnt matter) and any textures or flats in between them
+are loaded in either the textures category or flats category.
+
+For textures: PNAMES, TEXTURE1 and TEXTURE2 are loaded by default.
+*/
+textures
+{
+	zdoom1
+	{
+		start = "TX_START";
+		end = "TX_END";
+	}
+}
+
+/*
+ADDITIONAL UNIVERSAL DOOM MAP FORMAT FIELD DEFINITIONS
+Only add fields here that Doom Builder does not edit with its own user-interface!
+The "default" field must match the UDMF specifications!
+
+Field data types:
+0 = integer *
+1 = float
+2 = string
+3 = bool
+4 = linedef action (integer) *
+5 = sector effect (integer) *
+6 = texture (string)
+7 = flat (string)
+8 = angle in degrees (integer)
+9 = angle in radians (float)
+10 = XXRRGGBB color (integer)
+11 = enum option (integer) *
+12 = enum bits (integer) *
+13 = sector tag (integer) *
+14 = thing tag (integer) *
+15 = linedef tag (integer) *
+16 = enum option (string)
+17 = angle in degrees (float)
+22 = byte angle (integer)
+*/
+universalfields
+{
+	sector
+	{
+		friction
+		{
+			name = "Friction";
+			type = 1;
+			default = 1;
+		}
+
+		specialeffectplanes
+		{
+			type = 11;
+			enum = "floorceiling";
+			default = 0;
+		}
+
+		colormapbegin
+		{
+			type = 0;
+			default = 0;
+		}
+
+		colormapend
+		{
+			type = 0;
+			default = 33;
+		}
+
+		foglighting
+		{
+			type = 3;
+			default = false;
+		}
+
+		teambase
+		{
+			type = 11;
+			enum = "ctfteam";
+			default = 0;
+		}
+
+		triggersector
+		{
+			type = 3;
+			default = false;
+		}
+
+		triggerobject
+		{
+			type = 11;
+			enum = "triggerobjects";
+			default = 0;
+		}
+
+		triggersurface
+		{
+			type = 11;
+			enum = "triggersurfaces";
+			default = 0;
+		}
+
+		ringdrain
+		{
+			type = 1;
+			default = 0;
+		}
+	}
+
+	linedef
+	{
+		executordelay
+		{
+			type = 0;
+			default = 0;
+		}
+		midtexrepetitions
+		{
+			type = 0;
+			default = 0;
+		}
+		arg5
+		{
+			type = 0;
+			default = 0;
+		}
+		arg1str
+		{
+			type = 2;
+			default = "";
+		}
+	}
+
+	thing
+	{
+	}
+}
+
+/*
+MAP LUMP NAMES
+Map lumps are loaded with the map as long as they are right after each other. When the editor
+meets a lump which is not defined in this list it will ignore the map if not satisfied.
+The order of items defines the order in which lumps will be written to WAD file on save.
+To indicate the map header lump, use ~MAP
+
+Legenda:
+required = Lump is required to exist.
+blindcopy = Lump will be copied along with the map blindly. (useful for lumps Doom Builder doesn't use)
+nodebuild = The nodebuilder generates this lump.
+allowempty = The nodebuilder is allowed to leave this lump empty.
+scriptbuild = This lump is a text-based script, which should be compiled using current script compiler;
+script = This lump is a text-based script. Specify the filename of the script configuration to use.
+*/
+
+doommaplumpnames
+{
+	~MAP
+	{
+		required = true;
+		blindcopy = true;
+		nodebuild = false;
+	}
+
+	THINGS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = true;
+	}
+
+	LINEDEFS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SIDEDEFS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	VERTEXES
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SEGS
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SSECTORS
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	NODES
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	SECTORS
+	{
+		required = true;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	REJECT
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	BLOCKMAP
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = true;
+	}
+}
+
+udmfmaplumpnames
+{
+	ZNODES
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	REJECT
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = false;
+	}
+
+	BLOCKMAP
+	{
+		required = false;
+		nodebuild = true;
+		allowempty = true;
+	}
+}
+
+// ENUMERATIONS
+// These are enumerated lists for linedef types and UDMF fields.
+// Reserved names are: angledeg, anglerad, color, texture, flat
+enums
+{
+	falsetrue
+	{
+		0 = "False";
+		1 = "True";
+	}
+
+	yesno
+	{
+		0 = "Yes";
+		1 = "No";
+	}
+
+	noyes
+	{
+		0 = "No";
+		1 = "Yes";
+	}
+
+	onoff
+	{
+		0 = "On";
+		1 = "Off";
+	}
+
+	offon
+	{
+		0 = "Off";
+		1 = "On";
+	}
+
+	updown
+	{
+		0 = "Up";
+		1 = "Down";
+	}
+
+	downup
+	{
+		0 = "Down";
+		1 = "Up";
+	}
+
+	addset
+	{
+		0 = "Add";
+		1 = "Set";
+	}
+
+	floorceiling
+	{
+		0 = "Floor";
+		1 = "Ceiling";
+		2 = "Floor and ceiling";
+	}
+
+	triggertype
+	{
+		0 = "Continuous";
+		1 = "Each Time (Enter)";
+		2 = "Each Time (Enter and leave)";
+		3 = "Once";
+	}
+
+	frontback
+	{
+		0 = "None";
+		1 = "Front";
+		2 = "Back";
+	}
+
+	ctfteam
+	{
+		0 = "None";
+		1 = "Red";
+		2 = "Blue";
+	}
+
+	triggerobjects
+	{
+		0 = "Any player";
+		1 = "All players";
+		2 = "Pushable object";
+		3 = "Any object with thinker";
+	}
+
+	triggersurfaces
+	{
+		0 = "Floor touch";
+		1 = "Ceiling touch";
+		2 = "Floor or ceiling touch";
+		3 = "Anywhere in sector";
+	}
+
+	tangibility
+	{
+		1 = "Intangible from top";
+		2 = "Intangible from bottom";
+		4 = "Don't block players";
+		8 = "Don't block non-players";
+	}
+}
+
+//Default things filters
+thingsfilters
+{
+
+	filter0
+	{
+		name = "Player starts";
+		category = "starts";
+		type = -1;
+	}
+
+
+	filter1
+	{
+		name = "Enemies";
+		category = "enemies";
+		type = -1;
+
+	}
+
+
+	filter2
+	{
+		name = "NiGHTS Track";
+		category = "nightstrk";
+		type = -1;
+
+	}
+
+
+	filter3
+	{
+		name = "Normal Gravity";
+		category = "";
+		type = -1;
+
+		fields
+		{
+			2 = false;
+		}
+
+	}
+
+
+	filter4
+	{
+		name = "Reverse Gravity";
+		category = "";
+		type = -1;
+
+		fields
+		{
+			2 = true;
+		}
+
+	}
+}
+
+thingsfilters_udmf
+{
+}
+
+// Special linedefs
+speciallinedefs
+{
+	soundlinedefflag = 64;	// See linedefflags
+	singlesidedflag = 1;	// See linedefflags
+	doublesidedflag = 4;	// See linedefflags
+	impassableflag = 1;
+	upperunpeggedflag = 8;
+	lowerunpeggedflag = 16;
+	repeatmidtextureflag = 1024;
+	pegmidtextureflag = 256;
+}
+
+speciallinedefs_udmf
+{
+	soundlinedefflag = "noclimb";
+	singlesidedflag = "blocking";
+	doublesidedflag = "twosided";
+	impassableflag = "blocking";
+	upperunpeggedflag = "dontpegtop";
+	lowerunpeggedflag = "dontpegbottom";
+	repeatmidtextureflag = "wrapmidtex";
+	pegmidtextureflag = "midpeg";
+}
+
+scriptlumpnames
+{
+	MAINCFG
+	{
+		script = "SOC.cfg";
+	}
+
+	OBJCTCFG
+	{
+		script = "SOC.cfg";
+	}
+
+	SOC_
+	{
+		script = "SOC.cfg";
+		isprefix = true;
+	}
+
+	LUA_
+	{
+		script = "Lua.cfg";
+		isprefix = true;
+	}
+}
+
+// Texture sources
+textures
+{
+	zdoom1
+	{
+		start = "TX_START";
+		end = "TX_END";
+	}
+}
+
+// Patch sources
+patches
+{
+	standard1
+	{
+		start = "P_START";
+		end = "P_END";
+	}
+
+	standard2
+	{
+		start = "PP_START";
+		end = "PP_END";
+	}
+}
+
+// Sprite sources
+sprites
+{
+	standard1
+	{
+		start = "S_START";
+		end = "S_END";
+	}
+
+	standard2
+	{
+		start = "SS_START";
+		end = "SS_END";
+	}
+}
+
+// Flat sources
+flats
+{
+	standard1
+	{
+		start = "F_START";
+		end = "F_END";
+	}
+
+	standard2
+	{
+		start = "FF_START";
+		end = "FF_END";
+	}
+
+	standard3
+	{
+		start = "FF_START";
+		end = "F_END";
+	}
+
+	standard4
+	{
+		start = "F_START";
+		end = "FF_END";
+	}
+}
\ No newline at end of file
diff --git a/extras/conf/Includes/SRB222_sectors.cfg b/extras/conf/Includes/SRB222_sectors.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3bcbeb1b14d8ac6466f79e888a5be7ef3aa9bc24
--- /dev/null
+++ b/extras/conf/Includes/SRB222_sectors.cfg
@@ -0,0 +1,109 @@
+sectortypes
+{
+	0 = "Normal";
+	1 = "Damage";
+	2 = "Damage (Water)";
+	3 = "Damage (Fire)";
+	4 = "Damage (Electrical)";
+	5 = "Spikes";
+	6 = "Death Pit (Camera Tilt)";
+	7 = "Death Pit (No Camera Tilt)";
+	8 = "Instant Kill";
+	9 = "Ring Drainer (Floor Touch)";
+	10 = "Ring Drainer (Anywhere in Sector)";
+	11 = "Special Stage Damage";
+	12 = "Space Countdown";
+	13 = "Ramp Sector (double step-up/down)";
+	14 = "Non-Ramp Sector (no step-down)";
+	15 = "Bouncy FOF";
+	16 = "Trigger Line Ex. (Pushable Objects)";
+	32 = "Trigger Line Ex. (Anywhere, All Players)";
+	48 = "Trigger Line Ex. (Floor Touch, All Players)";
+	64 = "Trigger Line Ex. (Anywhere in Sector)";
+	80 = "Trigger Line Ex. (Floor Touch)";
+	96 = "Trigger Line Ex. (Emerald Check)";
+	112 = "Trigger Line Ex. (NiGHTS Mare)";
+	128 = "Check for Linedef Executor on FOFs";
+	144 = "Egg Capsule";
+	160 = "Special Stage Time/Rings Parameters";
+	176 = "Custom Global Gravity";
+	512 = "Wind/Current";
+	1024 = "Conveyor Belt";
+	1280 = "Speed Pad";
+	4096 = "Star Post Activator";
+	8192 = "Exit/Special Stage Pit/Return Flag";
+	12288 = "CTF Red Team Base";
+	16384 = "CTF Blue Team Base";
+	20480 = "Fan Sector";
+	24576 = "Super Sonic Transform";
+	28672 = "Force Spin";
+	32768 = "Zoom Tube Start";
+	36864 = "Zoom Tube End";
+	40960 = "Circuit Finish Line";
+	45056 = "Rope Hang";
+	49152 = "Intangible to the Camera";
+}
+
+gen_sectortypes
+{
+	first
+	{
+		0 = "Normal";
+		1 = "Damage";
+		2 = "Damage (Water)";
+		3 = "Damage (Fire)";
+		4 = "Damage (Electrical)";
+		5 = "Spikes";
+		6 = "Death Pit (Camera Tilt)";
+		7 = "Death Pit (No Camera Tilt)";
+		8 = "Instant Kill";
+		9 = "Ring Drainer (Floor Touch)";
+		10 = "Ring Drainer (Anywhere in Sector)";
+		11 = "Special Stage Damage";
+		12 = "Space Countdown";
+		13 = "Ramp Sector (double step-up/down)";
+		14 = "Non-Ramp Sector (no step-down)";
+		15 = "Bouncy FOF";
+	}
+
+	second
+	{
+		0 = "Normal";
+		16 = "Trigger Line Ex. (Pushable Objects)";
+		32 = "Trigger Line Ex. (Anywhere, All Players)";
+		48 = "Trigger Line Ex. (Floor Touch, All Players)";
+		64 = "Trigger Line Ex. (Anywhere in Sector)";
+		80 = "Trigger Line Ex. (Floor Touch)";
+		96 = "Trigger Line Ex. (Emerald Check)";
+		112 = "Trigger Line Ex. (NiGHTS Mare)";
+		128 = "Check for Linedef Executor on FOFs";
+		144 = "Egg Capsule";
+		160 = "Special Stage Time/Rings Parameters";
+		176 = "Custom Global Gravity";
+	}
+
+	third
+	{
+		0 = "Normal";
+		512 = "Wind/Current";
+		1024 = "Conveyor Belt";
+		1280 = "Speed Pad";
+	}
+
+	fourth
+	{
+		0 = "Normal";
+		4096 = "Star Post Activator";
+		8192 = "Exit/Special Stage Pit/Return Flag";
+		12288 = "CTF Red Team Base";
+		16384 = "CTF Blue Team Base";
+		20480 = "Fan Sector";
+		24576 = "Super Sonic Transform";
+		28672 = "Force Spin";
+		32768 = "Zoom Tube Start";
+		36864 = "Zoom Tube End";
+		40960 = "Circuit Finish Line";
+		45056 = "Rope Hang";
+		49152 = "Intangible to the Camera";
+	}
+}
\ No newline at end of file
diff --git a/extras/conf/Includes/SRB222_things.cfg b/extras/conf/Includes/SRB222_things.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..194e43630b70f5f98b6a3963b4b27371c85d2e02
--- /dev/null
+++ b/extras/conf/Includes/SRB222_things.cfg
@@ -0,0 +1,3398 @@
+// THING TYPES------------------------------------------------------------------
+// Color values: 1-Dark_Blue 2-Dark_Green 3-Turqoise 4-Dark_Red 5-Purple 6-Brown 7-Gray
+// 8-Dark_Gray 9-Blue 10-Green 11-Cyan 12-Red 13-Magenta
+// 14-Yellow 15-White 16-Pink 17-Orange 18-Gold 19-Cream
+
+editor
+{
+	color = 15; // White
+	arrow = 1;
+	title = "<Editor Things>";
+	error = -1;
+	width = 8;
+	height = 16;
+	sort = 1;
+
+	3328 = "3D Mode Start";
+}
+
+starts
+{
+	color = 1; // Blue
+	arrow = 1;
+	title = "Player Starts";
+	width = 16;
+	height = 48;
+	flags8text = "[8] Spawn on ceiling";
+	sprite = "PLAYA0";
+
+	1
+	{
+		title = "Player 01 Start";
+		sprite = "PLAYA0";
+	}
+	2
+	{
+		title = "Player 02 Start";
+		sprite = "PLAYA0";
+	}
+	3
+	{
+		title = "Player 03 Start";
+		sprite = "PLAYA0";
+	}
+	4
+	{
+		title = "Player 04 Start";
+		sprite = "PLAYA0";
+	}
+	5
+	{
+		title = "Player 05 Start";
+		sprite = "PLAYA0";
+	}
+	6
+	{
+		title = "Player 06 Start";
+		sprite = "PLAYA0";
+	}
+	7
+	{
+		title = "Player 07 Start";
+		sprite = "PLAYA0";
+	}
+	8
+	{
+		title = "Player 08 Start";
+		sprite = "PLAYA0";
+	}
+	9
+	{
+		title = "Player 09 Start";
+		sprite = "PLAYA0";
+	}
+	10
+	{
+		title = "Player 10 Start";
+		sprite = "PLAYA0";
+	}
+	11
+	{
+		title = "Player 11 Start";
+		sprite = "PLAYA0";
+	}
+	12
+	{
+		title = "Player 12 Start";
+		sprite = "PLAYA0";
+	}
+	13
+	{
+		title = "Player 13 Start";
+		sprite = "PLAYA0";
+	}
+	14
+	{
+		title = "Player 14 Start";
+		sprite = "PLAYA0";
+	}
+	15
+	{
+		title = "Player 15 Start";
+		sprite = "PLAYA0";
+	}
+	16
+	{
+		title = "Player 16 Start";
+		sprite = "PLAYA0";
+	}
+	17
+	{
+		title = "Player 17 Start";
+		sprite = "PLAYA0";
+	}
+	18
+	{
+		title = "Player 18 Start";
+		sprite = "PLAYA0";
+	}
+	19
+	{
+		title = "Player 19 Start";
+		sprite = "PLAYA0";
+	}
+	20
+	{
+		title = "Player 20 Start";
+		sprite = "PLAYA0";
+	}
+	21
+	{
+		title = "Player 21 Start";
+		sprite = "PLAYA0";
+	}
+	22
+	{
+		title = "Player 22 Start";
+		sprite = "PLAYA0";
+	}
+	23
+	{
+		title = "Player 23 Start";
+		sprite = "PLAYA0";
+	}
+	24
+	{
+		title = "Player 24 Start";
+		sprite = "PLAYA0";
+	}
+	25
+	{
+		title = "Player 25 Start";
+		sprite = "PLAYA0";
+	}
+	26
+	{
+		title = "Player 26 Start";
+		sprite = "PLAYA0";
+	}
+	27
+	{
+		title = "Player 27 Start";
+		sprite = "PLAYA0";
+	}
+	28
+	{
+		title = "Player 28 Start";
+		sprite = "PLAYA0";
+	}
+	29
+	{
+		title = "Player 29 Start";
+		sprite = "PLAYA0";
+	}
+	30
+	{
+		title = "Player 30 Start";
+		sprite = "PLAYA0";
+	}
+	31
+	{
+		title = "Player 31 Start";
+		sprite = "PLAYA0";
+	}
+	32
+	{
+		title = "Player 32 Start";
+		sprite = "PLAYA0";
+	}
+	33
+	{
+		title = "Match Start";
+		sprite = "NDRNA2A8";
+	}
+	34
+	{
+		title = "CTF Red Team Start";
+		sprite = "SIGNG0";
+	}
+	35
+	{
+		title = "CTF Blue Team Start";
+		sprite = "SIGNE0";
+	}
+}
+
+enemies
+{
+	color = 9; // Light_Blue
+	arrow = 1;
+	title = "Enemies";
+
+	100
+	{
+		title = "Crawla (Blue)";
+		sprite = "POSSA1";
+		width = 24;
+		height = 32;
+	}
+	101
+	{
+		title = "Crawla (Red)";
+		sprite = "SPOSA1";
+		width = 24;
+		height = 32;
+	}
+	102
+	{
+		title = "Stupid Dumb Unnamed RoboFish";
+		sprite = "FISHA0";
+		width = 8;
+		height = 28;
+		angletext = "Jump strength";
+	}
+	103
+	{
+		title = "Buzz (Gold)";
+		sprite = "BUZZA1";
+		width = 28;
+		height = 40;
+		flags8text = "[8] Cannot move";
+	}
+	104
+	{
+		title = "Buzz (Red)";
+		sprite = "RBUZA1";
+		width = 28;
+		height = 40;
+		flags8text = "[8] Cannot move";
+	}
+	108
+	{
+		title = "Deton";
+		sprite = "DETNA1";
+		width = 20;
+		height = 32;
+	}
+	110
+	{
+		title = "Turret";
+		sprite = "TRETA1";
+		width = 16;
+		height = 32;
+	}
+	111
+	{
+		title = "Pop-up Turret";
+		sprite = "TURRI1";
+		width = 12;
+		height = 64;
+		angletext = "Firing delay";
+	}
+	122
+	{
+		title = "Spring Shell (Green)";
+		sprite = "SSHLA1";
+		width = 24;
+		height = 40;
+	}
+	125
+	{
+		title = "Spring Shell (Yellow)";
+		sprite = "SSHLI1";
+		width = 24;
+		height = 40;
+	}
+	109
+	{
+		title = "Skim";
+		sprite = "SKIMA1";
+		width = 16;
+		height = 24;
+	}
+	113
+	{
+		title = "Jet Jaw";
+		sprite = "JJAWA3A7";
+		width = 12;
+		height = 20;
+	}
+	126
+	{
+		title = "Crushstacean";
+		sprite = "CRABA0";
+		width = 24;
+		height = 32;
+		flags8text = "[8] Move left from spawn";
+	}
+	138
+	{
+		title = "Banpyura";
+		sprite = "CR2BA0";
+		width = 24;
+		height = 32;
+		flags8text = "[8] Move left from spawn";
+	}
+	117
+	{
+		title = "Robo-Hood";
+		sprite = "ARCHA1";
+		width = 24;
+		height = 32;
+	}
+	118
+	{
+		title = "Lance-a-Bot";
+		sprite = "CBFSA1";
+		width = 32;
+		height = 72;
+	}
+	1113
+	{
+		title = "Suspicious Lance-a-Bot Statue";
+		sprite = "CBBSA1";
+		width = 32;
+		height = 72;
+	}
+	119
+	{
+		title = "Egg Guard";
+		sprite = "ESHIA1";
+		width = 16;
+		height = 48;
+		flags1text = "[1] 90 degrees counter-clockwise";
+		flags4text = "[4] 90 degrees clockwise";
+		flags8text = "[8] Double speed";
+	}
+	115
+	{
+		title = "Bird Aircraft Strike Hazard";
+		sprite = "VLTRF1";
+		width = 12;
+		height = 24;
+	}
+	120
+	{
+		title = "Green Snapper";
+		sprite = "GSNPA1";
+		width = 24;
+		height = 24;
+	}
+	121
+	{
+		title = "Minus";
+		sprite = "MNUSA0";
+		width = 24;
+		height = 32;
+	}
+	134
+	{
+		title = "Canarivore";
+		sprite = "CANAA0";
+		width = 12;
+		height = 80;
+		hangs = 1;
+	}
+	123
+	{
+		title = "Unidus";
+		sprite = "UNIDA1";
+		width = 18;
+		height = 36;
+	}
+	135
+	{
+		title = "Pterabyte Spawner";
+		sprite = "PTERA2A8";
+		width = 16;
+		height = 16;
+		parametertext = "No. Pterabytes";
+	}
+	136
+	{
+		title = "Pyre Fly";
+		sprite = "PYREA0";
+		width = 24;
+		height = 34;
+		flags8text = "[8] Start on fire";
+	}
+	137
+	{
+		title = "Dragonbomber";
+		sprite = "DRABA1";
+		width = 28;
+		height = 48;
+	}
+	105
+	{
+		title = "Jetty-Syn Bomber";
+		sprite = "JETBB1";
+		width = 20;
+		height = 50;
+		flags8text = "[8] Cannot move";
+	}
+	106
+	{
+		title = "Jetty-Syn Gunner";
+		sprite = "JETGB1";
+		width = 20;
+		height = 48;
+		flags8text = "[8] Cannot move";
+	}
+	112
+	{
+		title = "Spincushion";
+		sprite = "SHRPA1";
+		width = 16;
+		height = 24;
+	}
+	114
+	{
+		title = "Snailer";
+		sprite = "SNLRA3A7";
+		width = 24;
+		height = 48;
+	}
+	129
+	{
+		title = "Penguinator";
+		sprite = "PENGA1";
+		width = 24;
+		height = 32;
+	}
+	130
+	{
+		title = "Pophat";
+		sprite = "POPHA1";
+		width = 24;
+		height = 32;
+	}
+	107
+	{
+		title = "Crawla Commander";
+		sprite = "CCOMA1";
+		width = 16;
+		height = 32;
+	}
+	131
+	{
+		title = "Spinbobert";
+		sprite = "SBOBB0";
+		width = 32;
+		height = 32;
+	}
+	132
+	{
+		title = "Cacolantern";
+		sprite = "CACOA0";
+		width = 32;
+		height = 32;
+		flags8text = "[8] Cannot move";
+	}
+	133
+	{
+		title = "Hangster";
+		sprite = "HBATC1";
+		width = 24;
+		height = 24;
+		hangs = 1;
+	}
+	127
+	{
+		title = "Hive Elemental";
+		sprite = "HIVEA0";
+		width = 32;
+		height = 80;
+		parametertext = "No. bees";
+	}
+	128
+	{
+		title = "Bumblebore";
+		sprite = "BUMBA1";
+		width = 16;
+		height = 32;
+	}
+	124
+	{
+		title = "Buggle";
+		sprite = "BBUZA1";
+		width = 20;
+		height = 24;
+	}
+	116
+	{
+		title = "Pointy";
+		sprite = "PNTYA1";
+		width = 8;
+		height = 16;
+	}
+}
+
+bosses
+{
+	color = 8; // Dark_Gray
+	arrow = 1;
+	title = "Bosses";
+
+	200
+	{
+		title = "Egg Mobile";
+		sprite = "EGGMA1";
+		width = 24;
+		height = 76;
+		flags4text = "[4] End level on death";
+		flags8text = "[8] Alternate laser attack";
+	}
+	201
+	{
+		title = "Egg Slimer";
+		sprite = "EGGNA1";
+		width = 24;
+		height = 76;
+		flags4text = "[4] End level on death";
+		flags8text = "[8] Speed up when hit";
+	}
+	202
+	{
+		title = "Sea Egg";
+		sprite = "EGGOA1";
+		width = 32;
+		height = 116;
+		flags4text = "[4] End level on death";
+	}
+	203
+	{
+		title = "Egg Colosseum";
+		sprite = "EGGPA1";
+		width = 24;
+		height = 76;
+		flags4text = "[4] End level on death";
+	}
+	204
+	{
+		title = "Fang";
+		sprite = "FANGA1";
+		width = 24;
+		height = 60;
+		flags1text = "[1] Grayscale mode";
+		flags4text = "[4] End level on death";
+	}
+	206
+	{
+		title = "Brak Eggman (Old)";
+		sprite = "BRAKB1";
+		width = 48;
+		height = 160;
+		flags4text = "[4] End level on death";
+	}
+	207
+	{
+		title = "Metal Sonic (Race)";
+		sprite = "METLI1";
+		width = 16;
+		height = 48;
+		flags1text = "[1] Grayscale mode";
+	}
+	208
+	{
+		title = "Metal Sonic (Battle)";
+		sprite = "METLC1";
+		width = 16;
+		height = 48;
+		flags1text = "[1] Grayscale mode";
+		flags4text = "[4] End level on death";
+	}
+	209
+	{
+		title = "Brak Eggman";
+		sprite = "BRAK01";
+		width = 48;
+		height = 160;
+		flags1text = "[1] No origin-fling death";
+		flags4text = "[4] End level on death";
+		flags8text = "[8] Electric barrier";
+	}
+	290
+	{
+		arrow = 0;
+		title = "Boss Escape Point";
+		width = 8;
+		height = 16;
+		sprite = "internal:eggmanend";
+	}
+	291
+	{
+		arrow = 0;
+		title = "Egg Capsule Center";
+		width = 8;
+		height = 16;
+		sprite = "internal:capsule";
+	}
+	292
+	{
+		arrow = 0;
+		title = "Boss Waypoint";
+		width = 8;
+		height = 16;
+		flags8text = "[8] Sea Egg shooting point";
+		sprite = "internal:eggmanway";
+		angletext = "No. (Sea Egg)";
+		flagsvaluetext = "No. (Brak)";
+		parametertext = "Next";
+	}
+	293
+	{
+		title = "Metal Sonic Gather Point";
+		sprite = "internal:metal";
+		width = 8;
+		height = 16;
+	}
+	294
+	{
+		title = "Fang Waypoint";
+		flags8text = "[8] Center waypoint";
+		sprite = "internal:eggmanway";
+		width = 8;
+		height = 16;
+	}
+}
+
+rings
+{
+	color = 14; // Yellow
+	title = "Rings and Weapon Panels";
+	width = 24;
+	height = 24;
+	flags8height = 24;
+	flags8text = "[8] Float";
+	sprite = "RINGA0";
+
+	300
+	{
+		title = "Ring";
+		sprite = "RINGA0";
+		width = 16;
+	}
+	301
+	{
+		title = "Bounce Ring";
+		sprite = "internal:RNGBA0";
+	}
+	302
+	{
+		title = "Rail Ring";
+		sprite = "internal:RNGRA0";
+	}
+	303
+	{
+		title = "Infinity Ring";
+		sprite = "internal:RNGIA0";
+	}
+	304
+	{
+		title = "Automatic Ring";
+		sprite = "internal:RNGAA0";
+	}
+	305
+	{
+		title = "Explosion Ring";
+		sprite = "internal:RNGEA0";
+	}
+	306
+	{
+		title = "Scatter Ring";
+		sprite = "internal:RNGSA0";
+	}
+	307
+	{
+		title = "Grenade Ring";
+		sprite = "internal:RNGGA0";
+	}
+	308
+	{
+		title = "CTF Team Ring (Red)";
+		sprite = "internal:RRNGA0";
+		width = 16;
+	}
+	309
+	{
+		title = "CTF Team Ring (Blue)";
+		sprite = "internal:BRNGA0";
+		width = 16;
+	}
+	330
+	{
+		title = "Bounce Ring Panel";
+		sprite = "internal:PIKBA0";
+	}
+	331
+	{
+		title = "Rail Ring Panel";
+		sprite = "internal:PIKRA0";
+	}
+	332
+	{
+		title = "Automatic Ring Panel";
+		sprite = "internal:PIKAA0";
+	}
+	333
+	{
+		title = "Explosion Ring Panel";
+		sprite = "internal:PIKEA0";
+	}
+	334
+	{
+		title = "Scatter Ring Panel";
+		sprite = "internal:PIKSA0";
+	}
+	335
+	{
+		title = "Grenade Ring Panel";
+		sprite = "internal:PIKGA0";
+	}
+}
+
+collectibles
+{
+	color = 10; // Light_Green
+	title = "Other Collectibles";
+	width = 16;
+	height = 32;
+	sort = 1;
+	sprite = "CEMGA0";
+
+	310
+	{
+		title = "CTF Red Flag";
+		sprite = "RFLGA0";
+		width = 24;
+		height = 64;
+	}
+	311
+	{
+		title = "CTF Blue Flag";
+		sprite = "BFLGA0";
+		width = 24;
+		height = 64;
+	}
+	312
+	{
+		title = "Emerald Token";
+		sprite = "TOKEA0";
+		width = 16;
+		height = 32;
+		flags8height = 24;
+		flags8text = "[8] Float";
+	}
+	313
+	{
+		title = "Chaos Emerald 1 (Green)";
+		sprite = "CEMGA0";
+	}
+	314
+	{
+		title = "Chaos Emerald 2 (Purple)";
+		sprite = "CEMGB0";
+	}
+	315
+	{
+		title = "Chaos Emerald 3 (Blue)";
+		sprite = "CEMGC0";
+	}
+	316
+	{
+		title = "Chaos Emerald 4 (Cyan)";
+		sprite = "CEMGD0";
+	}
+	317
+	{
+		title = "Chaos Emerald 5 (Orange)";
+		sprite = "CEMGE0";
+	}
+	318
+	{
+		title = "Chaos Emerald 6 (Red)";
+		sprite = "CEMGF0";
+	}
+	319
+	{
+		title = "Chaos Emerald 7 (Gray)";
+		sprite = "CEMGG0";
+	}
+	320
+	{
+		title = "Emerald Hunt Location";
+		sprite = "SHRDA0";
+	}
+	321
+	{
+		title = "Match Chaos Emerald Spawn";
+		sprite = "CEMGA0";
+		flags8height = 24;
+		flags8text = "[8] Float";
+	}
+	322
+	{
+		title = "Emblem";
+		sprite = "EMBMA0";
+		width = 16;
+		height = 30;
+		flags8height = 24;
+		flags8text = "[8] Float";
+		angletext = "Tag";
+	}
+}
+
+boxes
+{
+	color = 7; // Gray
+	blocking = 2;
+	title = "Monitors";
+	width = 18;
+	height = 40;
+	flags1text = "[1] Run Linedef Executor on pop";
+	flags4text = "[4] Random (Strong)";
+	flags8text = "[8] Random (Weak)";
+
+	400
+	{
+		title = "Super Ring (10 Rings)";
+		sprite = "TVRIA0";
+	}
+	401
+	{
+		title = "Pity Shield";
+		sprite = "TVPIA0";
+	}
+	402
+	{
+		title = "Attraction Shield";
+		sprite = "TVATA0";
+	}
+	403
+	{
+		title = "Force Shield";
+		sprite = "TVFOA0";
+	}
+	404
+	{
+		title = "Armageddon Shield";
+		sprite = "TVARA0";
+	}
+	405
+	{
+		title = "Whirlwind Shield";
+		sprite = "TVWWA0";
+	}
+	406
+	{
+		title = "Elemental Shield";
+		sprite = "TVELA0";
+	}
+	407
+	{
+		title = "Super Sneakers";
+		sprite = "TVSSA0";
+	}
+	408
+	{
+		title = "Invincibility";
+		sprite = "TVIVA0";
+	}
+	409
+	{
+		title = "Extra Life";
+		sprite = "TV1UA0";
+		flags4text = "[4] Random (Strong) / 10k points";
+		flags8text = "[8] Random (Weak) / 10k points";
+	}
+	410
+	{
+		title = "Eggman";
+		sprite = "TVEGA0";
+		flags4text = "[4] Special";
+		flags8text = "[8] Ambush";
+	}
+	411
+	{
+		title = "Teleporter";
+		sprite = "TVMXA0";
+	}
+	413
+	{
+		title = "Gravity Boots";
+		sprite = "TVGVA0";
+		flags4text = "[4] Special";
+		flags8text = "[8] Ambush";
+	}
+	414
+	{
+		title = "CTF Team Ring Monitor (Red)";
+		sprite = "TRRIA0";
+		flags4text = "[4] Special";
+		flags8text = "[8] Ambush";
+	}
+	415
+	{
+		title = "CTF Team Ring Monitor (Blue)";
+		sprite = "TBRIA0";
+		flags4text = "[4] Special";
+		flags8text = "[8] Ambush";
+	}
+	416
+	{
+		title = "Recycler";
+		sprite = "TVRCA0";
+	}
+	418
+	{
+		title = "Score (1,000 Points)";
+		sprite = "TV1KA0";
+		flags4text = "[4] Special";
+		flags8text = "[8] Ambush";
+	}
+	419
+	{
+		title = "Score (10,000 Points)";
+		sprite = "TVTKA0";
+		flags4text = "[4] Special";
+		flags8text = "[8] Ambush";
+	}
+	420
+	{
+		title = "Flame Shield";
+		sprite = "TVFLA0";
+	}
+	421
+	{
+		title = "Water Shield";
+		sprite = "TVBBA0";
+	}
+	422
+	{
+		title = "Lightning Shield";
+		sprite = "TVZPA0";
+	}
+}
+
+boxes2
+{
+	color = 18; // Gold
+	blocking = 2;
+	title = "Monitors (Respawning)";
+	width = 20;
+	height = 44;
+	flags1text = "[1] Run Linedef Executor on pop";
+
+	431
+	{
+		title = "Pity Shield (Respawn)";
+		sprite = "TVPIB0";
+	}
+	432
+	{
+		title = "Attraction Shield (Respawn)";
+		sprite = "TVATB0";
+	}
+	433
+	{
+		title = "Force Shield (Respawn)";
+		sprite = "TVFOB0";
+	}
+	434
+	{
+		title = "Armageddon Shield (Respawn)";
+		sprite = "TVARB0";
+	}
+	435
+	{
+		title = "Whirlwind Shield (Respawn)";
+		sprite = "TVWWB0";
+	}
+	436
+	{
+		title = "Elemental Shield (Respawn)";
+		sprite = "TVELB0";
+	}
+	437
+	{
+		title = "Super Sneakers (Respawn)";
+		sprite = "TVSSB0";
+	}
+	438
+	{
+		title = "Invincibility (Respawn)";
+		sprite = "TVIVB0";
+	}
+	440
+	{
+		title = "Eggman (Respawn)";
+		sprite = "TVEGB0";
+	}
+	443
+	{
+		title = "Gravity Boots (Respawn)";
+		sprite = "TVGVB0";
+	}
+	450
+	{
+		title = "Flame Shield (Respawn)";
+		sprite = "TVFLB0";
+	}
+	451
+	{
+		title = "Water Shield (Respawn)";
+		sprite = "TVBBB0";
+	}
+	452
+	{
+		title = "Lightning Shield (Respawn)";
+		sprite = "TVZPB0";
+	}
+}
+
+generic
+{
+	color = 11; // Light_Cyan
+	title = "Generic Items & Hazards";
+
+	500
+	{
+		title = "Air Bubble Patch";
+		sprite = "BUBLE0";
+		width = 8;
+		height = 16;
+		flags8text = "[8] No distance check";
+	}
+	501
+	{
+		title = "Signpost";
+		sprite = "SIGND0";
+		width = 8;
+		height = 32;
+	}
+	502
+	{
+		arrow = 1;
+		title = "Star Post";
+		sprite = "STPTA0M0";
+		width = 64;
+		height = 128;
+		angletext = "Angle/Order";
+	}
+	520
+	{
+		title = "Bomb Sphere";
+		sprite = "SPHRD0";
+		width = 16;
+		height = 24;
+		flags8height = 24;
+		flags8text = "[8] Float";
+		unflippable = true;
+	}
+	521
+	{
+		title = "Spikeball";
+		sprite = "SPIKA0";
+		width = 12;
+		height = 8;
+		flags8height = 24;
+		flags8text = "[8] Float";
+	}
+	522
+	{
+		title = "Wall Spike";
+		sprite = "WSPKALAR";
+		width = 16;
+		height = 14;
+		flags1text = "[1] Start retracted";
+		flags4text = "[4] Retractable";
+		flags8text = "[8] Intangible";
+		parametertext = "Initial delay";
+	}
+	523
+	{
+		title = "Spike";
+		sprite = "USPKA0";
+		width = 8;
+		height = 32;
+		flags1text = "[1] Start retracted";
+		flags4text = "[4] Retractable";
+		flags8text = "[8] Intangible";
+		angletext = "Retraction interval";
+		parametertext = "Initial delay";
+	}
+	1130
+	{
+		title = "Small Mace";
+		sprite = "SMCEA0";
+		width = 17;
+		height = 34;
+	}
+	1131
+	{
+		title = "Big Mace";
+		sprite = "BMCEA0";
+		width = 34;
+		height = 68;
+	}
+	1136
+	{
+		title = "Small Fireball";
+		sprite = "SFBRA0";
+		width = 17;
+		height = 34;
+	}
+	1137
+	{
+		title = "Large Fireball";
+		sprite = "BFBRA0";
+		width = 34;
+		height = 68;
+	}
+}
+
+springs
+{
+	color = 12; // Light_Red
+	title = "Springs and Fans";
+	width = 20;
+	height = 16;
+	sprite = "RSPRD2";
+
+	540
+	{
+		title = "Fan";
+		sprite = "FANSA0D0";
+		width = 16;
+		height = 8;
+		flags4text = "[4] Invisible";
+		flags8text = "[8] No distance check";
+		angletext = "Lift height";
+	}
+	541
+	{
+		title = "Gas Jet";
+		sprite = "STEMD0";
+		flags8text = "[8] No sounds";
+		width = 32;
+	}
+	542
+	{
+		title = "Bumper";
+		sprite = "BUMPA0";
+		width = 32;
+		height = 64;
+		angletext = "Strength";
+	}
+	543
+	{
+		title = "Balloon";
+		sprite = "BLONA0";
+		width = 32;
+		height = 64;
+		flags8text = "[8] Respawn";
+		angletext = "Color";
+	}
+	550
+	{
+		title = "Yellow Spring";
+		sprite = "SPRYA0";
+	}
+	551
+	{
+		title = "Red Spring";
+		sprite = "SPRRA0";
+	}
+	552
+	{
+		title = "Blue Spring";
+		sprite = "SPRBA0";
+	}
+	555
+	{
+		arrow = 1;
+		title = "Diagonal Yellow Spring";
+		sprite = "YSPRD2";
+		width = 16;
+		flags4text = "[4] Ignore gravity";
+		flags8text = "[8] Rotate 22.5° CCW";
+	}
+	556
+	{
+		arrow = 1;
+		title = "Diagonal Red Spring";
+		sprite = "RSPRD2";
+		width = 16;
+		flags4text = "[4] Ignore gravity";
+		flags8text = "[8] Rotate 22.5° CCW";
+	}
+	557
+	{
+		arrow = 1;
+		title = "Diagonal Blue Spring";
+		sprite = "BSPRD2";
+		width = 16;
+		flags4text = "[4] Ignore gravity";
+		flags8text = "[8] Rotate 22.5° CCW";
+	}
+	558
+	{
+		arrow = 1;
+		title = "Horizontal Yellow Spring";
+		sprite = "SSWYD2D8";
+		flags8height = 16;
+		flags8text = "[8] Float";
+		width = 16;
+		height = 32;
+	}
+	559
+	{
+		arrow = 1;
+		title = "Horizontal Red Spring";
+		sprite = "SSWRD2D8";
+		flags8height = 16;
+		flags8text = "[8] Float";
+		width = 16;
+		height = 32;
+	}
+	560
+	{
+		arrow = 1;
+		title = "Horizontal Blue Spring";
+		sprite = "SSWBD2D8";
+		flags8height = 16;
+		flags8text = "[8] Float";
+		width = 16;
+		height = 32;
+	}
+	1134
+	{
+		title = "Yellow Spring Ball";
+		sprite = "YSPBA0";
+		width = 17;
+		height = 34;
+	}
+	1135
+	{
+		title = "Red Spring Ball";
+		sprite = "RSPBA0";
+		width = 17;
+		height = 34;
+	}
+	544
+	{
+		arrow = 1;
+		title = "Yellow Boost Panel";
+		sprite = "BSTYA0";
+		flags8text = "[8] Force spin";
+		width = 28;
+		height = 2;
+	}
+	545
+	{
+		arrow = 1;
+		title = "Red Boost Panel";
+		sprite = "BSTRA0";
+		flags8text = "[8] Force spin";
+		width = 28;
+		height = 2;
+	}
+}
+
+patterns
+{
+	color = 5; // Magenta
+	arrow = 1;
+	title = "Special Placement Patterns";
+	width = 16;
+	height = 384;
+	sprite = "RINGA0";
+
+	600
+	{
+		arrow = 0;
+		title = "5 Vertical Rings (Yellow Spring)";
+		sprite = "RINGA0";
+	}
+	601
+	{
+		arrow = 0;
+		title = "5 Vertical Rings (Red Spring)";
+		sprite = "RINGA0";
+		height = 1024;
+	}
+	602
+	{
+		title = "5 Diagonal Rings (Yellow Spring)";
+		sprite = "RINGA0";
+		height = 32;
+	}
+	603
+	{
+		title = "10 Diagonal Rings (Red Spring)";
+		sprite = "RINGA0";
+		height = 32;
+	}
+	604
+	{
+		title = "Circle of Rings";
+		sprite = "RINGA0";
+		width = 96;
+		height = 192;
+		unflippable = true;
+		centerHitbox = true;
+	}
+	605
+	{
+		title = "Circle of Rings (Big)";
+		sprite = "RINGA0";
+		width = 192;
+		unflippable = true;
+		centerHitbox = true;
+	}
+	606
+	{
+		title = "Circle of Blue Spheres";
+		sprite = "SPHRA0";
+		width = 96;
+		height = 192;
+		unflippable = true;
+		centerHitbox = true;
+	}
+	607
+	{
+		title = "Circle of Blue Spheres (Big)";
+		sprite = "SPHRA0";
+		width = 192;
+		unflippable = true;
+		centerHitbox = true;
+	}
+	608
+	{
+		title = "Circle of Rings and Spheres";
+		sprite = "SPHRA0";
+		width = 96;
+		height = 192;
+		unflippable = true;
+		centerHitbox = true;
+	}
+	609
+	{
+		title = "Circle of Rings and Spheres (Big)";
+		sprite = "SPHRA0";
+		width = 192;
+		unflippable = true;
+		centerHitbox = true;
+	}
+}
+
+invisible
+{
+	color = 15; // White
+	title = "Misc. Invisible";
+	width = 0;
+	height = 0;
+	sprite = "UNKNA0";
+	sort = 1;
+	fixedsize = true;
+	blocking = 0;
+
+	700
+	{
+		title = "Water Ambience A (Large)";
+		sprite = "internal:ambiance";
+	}
+
+	701
+	{
+		title = "Water Ambience B (Large)";
+		sprite = "internal:ambiance";
+	}
+
+	702
+	{
+		title = "Water Ambience C (Medium)";
+		sprite = "internal:ambiance";
+	}
+
+	703
+	{
+		title = "Water Ambience D (Medium)";
+		sprite = "internal:ambiance";
+	}
+
+	704
+	{
+		title = "Water Ambience E (Small)";
+		sprite = "internal:ambiance";
+	}
+
+	705
+	{
+		title = "Water Ambience F (Small)";
+		sprite = "internal:ambiance";
+	}
+
+	706
+	{
+		title = "Water Ambience G (Extra Large)";
+		sprite = "internal:ambiance";
+	}
+
+	707
+	{
+		title = "Water Ambience H (Extra Large)";
+		sprite = "internal:ambiance";
+	}
+
+	708
+	{
+		title = "Disco Ambience";
+		sprite = "internal:ambiance";
+	}
+
+	709
+	{
+		title = "Volcano Ambience";
+		sprite = "internal:ambiance";
+	}
+
+	710
+	{
+		title = "Machine Ambience";
+		sprite = "internal:ambiance";
+	}
+
+	750
+	{
+		title = "Slope Vertex";
+		sprite = "internal:vertexslope";
+		angletext = "Tag";
+	}
+
+	751
+	{
+		arrow = 1;
+		title = "Teleport Destination";
+		sprite = "internal:tele";
+	}
+
+	752
+	{
+		arrow = 1;
+		title = "Alternate View Point";
+		sprite = "internal:view";
+	}
+
+	753
+	{
+		title = "Zoom Tube Waypoint";
+		sprite = "internal:zoom";
+		angletext = "Order";
+	}
+
+	754
+	{
+		title = "Push Point";
+		flags4text = "[4] Fades using XY";
+		flags8text = "[8] Push using XYZ";
+		sprite = "GWLGA0";
+		angletext = "Radius";
+	}
+	755
+	{
+		title = "Pull Point";
+		flags4text = "[4] Fades using XY";
+		flags8text = "[8] Pull using XYZ";
+		sprite = "GWLRA0";
+		angletext = "Radius";
+	}
+	756
+	{
+		title = "Blast Linedef Executor";
+		sprite = "TOADA0";
+		width = 32;
+		height = 16;
+	}
+	757
+	{
+		title = "Fan Particle Generator";
+		sprite = "PRTLA0";
+		width = 8;
+		height = 16;
+		angletext = "Tag";
+	}
+	758
+	{
+		title = "Object Angle Anchor";
+		sprite = "internal:view";
+	}
+	760
+	{
+		title = "PolyObject Anchor";
+		sprite = "internal:polyanchor";
+		angletext = "ID";
+	}
+
+	761
+	{
+		title = "PolyObject Spawn Point";
+		sprite = "internal:polycenter";
+		angletext = "ID";
+	}
+
+	762
+	{
+		title = "PolyObject Spawn Point (Crush)";
+		sprite = "internal:polycentercrush";
+		angletext = "ID";
+	}
+	780
+	{
+		title = "Skybox View Point";
+		sprite = "internal:skyb";
+		flags4text = "[4] In-map centerpoint";
+		parametertext = "ID";
+	}
+}
+
+greenflower
+{
+	color = 10; // Green
+	title = "Greenflower";
+
+	800
+	{
+		title = "GFZ Flower";
+		sprite = "FWR1A0";
+		width = 16;
+		height = 40;
+	}
+	801
+	{
+		title = "Sunflower";
+		sprite = "FWR2A0";
+		width = 16;
+		height = 96;
+	}
+	802
+	{
+		title = "Budding Flower";
+		sprite = "FWR3A0";
+		width = 8;
+		height = 32;
+	}
+	803
+	{
+		title = "Blueberry Bush";
+		sprite = "BUS3A0";
+		width = 16;
+		height = 32;
+	}
+	804
+	{
+		title = "Berry Bush";
+		sprite = "BUS1A0";
+		width = 16;
+		height = 32;
+	}
+	805
+	{
+		title = "Bush";
+		sprite = "BUS2A0";
+		width = 16;
+		height = 32;
+	}
+	806
+	{
+		title = "GFZ Tree";
+		sprite = "TRE1A0";
+		width = 20;
+		height = 128;
+	}
+	807
+	{
+		title = "GFZ Berry Tree";
+		sprite = "TRE1B0";
+		width = 20;
+		height = 128;
+	}
+	808
+	{
+		title = "GFZ Cherry Tree";
+		sprite = "TRE1C0";
+		width = 20;
+		height = 128;
+	}
+	809
+	{
+		title = "Checkered Tree";
+		sprite = "TRE2A0";
+		width = 20;
+		height = 200;
+	}
+	810
+	{
+		title = "Checkered Tree (Sunset)";
+		sprite = "TRE2B0";
+		width = 20;
+		height = 200;
+	}
+	811
+	{
+		title = "Polygon Tree";
+		sprite = "TRE4A0";
+		width = 20;
+		height = 200;
+	}
+	812
+	{
+		title = "Bush Tree";
+		sprite = "TRE5A0";
+		width = 20;
+		height = 200;
+	}
+	813
+	{
+		title = "Red Bush Tree";
+		sprite = "TRE5B0";
+		width = 20;
+		height = 200;
+	}
+}
+
+technohill
+{
+	color = 10; // Green
+	title = "Techno Hill";
+
+	900
+	{
+		title = "THZ Steam Flower";
+		sprite = "THZPA0";
+		width = 8;
+		height = 32;
+	}
+	901
+	{
+		title = "Alarm";
+		sprite = "ALRMA0";
+		width = 8;
+		height = 16;
+		hangs = 1;
+	}
+	902
+	{
+		title = "THZ Spin Flower (Red)";
+		sprite = "FWR5A0";
+		width = 16;
+		height = 64;
+	}
+	903
+	{
+		title = "THZ Spin Flower (Yellow)";
+		sprite = "FWR6A0";
+		width = 16;
+		height = 64;
+	}
+	904
+	{
+		arrow = 1;
+		title = "Whistlebush";
+		sprite = "THZTA0";
+		width = 16;
+		height = 64;
+	}
+}
+
+deepsea
+{
+	color = 10; // Green
+	title = "Deep Sea";
+
+	1000
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Gargoyle";
+		sprite = "GARGA1";
+		width = 16;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1009
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Gargoyle (Big)";
+		sprite = "GARGB1";
+		width = 32;
+		height = 80;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1001
+	{
+		title = "Seaweed";
+		sprite = "SEWEA0";
+		width = 24;
+		height = 56;
+	}
+	1002
+	{
+		title = "Dripping Water";
+		sprite = "DRIPD0";
+		width = 8;
+		height = 16;
+		hangs = 1;
+		angletext = "Dripping interval";
+	}
+	1003
+	{
+		title = "Coral (Green)";
+		sprite = "CORLA0";
+		width = 29;
+		height = 40;
+	}
+	1004
+	{
+		title = "Coral (Red)";
+		sprite = "CORLB0";
+		width = 30;
+		height = 53;
+	}
+	1005
+	{
+		title = "Coral (Orange)";
+		sprite = "CORLC0";
+		width = 28;
+		height = 41;
+	}
+	1006
+	{
+		title = "Blue Crystal";
+		sprite = "BCRYA1";
+		width = 8;
+		height = 16;
+	}
+	1007
+	{
+		title = "Kelp";
+		sprite = "KELPA0";
+		width = 16;
+		height = 292;
+		flags4text = "[4] Double size";
+	}
+	1008
+	{
+		title = "Stalagmite (DSZ1)";
+		sprite = "DSTGA0";
+		width = 8;
+		height = 116;
+		flags4text = "[4] Double size";
+	}
+	1010
+	{
+		arrow = 1;
+		title = "Light Beam";
+		sprite = "LIBEARAL";
+		width = 16;
+		height = 16;
+	}
+	1011
+	{
+		title = "Stalagmite (DSZ2)";
+		sprite = "DSTGA0";
+		width = 8;
+		height = 116;
+		flags4text = "[4] Double size";
+	}
+	1012
+	{
+		arrow = 1;
+		title = "Big Floating Mine";
+		width = 28;
+		height = 56;
+		sprite = "BMNEA1";
+	}
+	1013
+	{
+		title = "Animated Kelp";
+		sprite = "ALGAA0";
+		width = 48;
+		height = 120;
+	}
+	1014
+	{
+		title = "Large Coral (Brown)";
+		sprite = "CORLD0";
+		width = 56;
+		height = 112;
+	}
+	1015
+	{
+		title = "Large Coral (Beige)";
+		sprite = "CORLE0";
+		width = 56;
+		height = 112;
+	}
+}
+
+castleeggman
+{
+	color = 10; // Green
+	title = "Castle Eggman";
+
+	1100
+	{
+		title = "Chain (Decorative)";
+		sprite = "CHANA0";
+		width = 4;
+		height = 128;
+		hangs = 1;
+	}
+	1101
+	{
+		title = "Torch";
+		sprite = "FLAMA0E0";
+		width = 8;
+		height = 32;
+		flags1text = "[1] Add corona";
+	}
+	1102
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Eggman Statue";
+		sprite = "ESTAA1";
+		width = 32;
+		height = 240;
+		flags1text = "[1] Solid gold";
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1103
+	{
+		title = "CEZ Flower";
+		sprite = "FWR4A0";
+		width = 16;
+		height = 40;
+	}
+	1104
+	{
+		title = "Mace Spawnpoint";
+		sprite = "SMCEA0";
+		width = 17;
+		height = 34;
+		flags4text = "[4] No sounds";
+		flags8text = "[8] Double size";
+		angletext = "Tag";
+	}
+	1105
+	{
+		title = "Chain with Maces Spawnpoint";
+		sprite = "SMCEA0";
+		width = 17;
+		height = 34;
+		flags4text = "[4] No sounds";
+		flags8text = "[8] Double size";
+		angletext = "Tag";
+	}
+	1106
+	{
+		title = "Chained Spring Spawnpoint";
+		sprite = "YSPBA0";
+		width = 17;
+		height = 34;
+		flags4text = "[4] No sounds";
+		flags8text = "[8] Red spring";
+		angletext = "Tag";
+	}
+	1107
+	{
+		title = "Chain Spawnpoint";
+		sprite = "BMCHA0";
+		width = 17;
+		height = 34;
+		flags8text = "[8] Double size";
+		angletext = "Tag";
+	}
+	1108
+	{
+		arrow = 1;
+		title = "Hidden Chain Spawnpoint";
+		sprite = "internal:chain3";
+		width = 17;
+		height = 34;
+		flags8text = "[8] Double size";
+	}
+	1109
+	{
+		title = "Firebar Spawnpoint";
+		sprite = "BFBRA0";
+		width = 17;
+		height = 34;
+		flags4text = "[4] No sounds";
+		flags8text = "[8] Double size";
+		angletext = "Tag";
+	}
+	1110
+	{
+		title = "Custom Mace Spawnpoint";
+		sprite = "SMCEA0";
+		width = 17;
+		height = 34;
+		flags4text = "[4] No sounds";
+		angletext = "Tag";
+	}
+	1111
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Crawla Statue";
+		sprite = "CSTAA1";
+		width = 16;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1112
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Lance-a-Bot Statue";
+		sprite = "CBBSA1";
+		width = 32;
+		height = 72;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1114
+	{
+		title = "Pine Tree";
+		sprite = "PINEA0";
+		width = 16;
+		height = 628;
+	}
+	1115
+	{
+		title = "CEZ Shrub (Small)";
+		sprite = "CEZBA0";
+		width = 16;
+		height = 24;
+	}
+	1116
+	{
+		title = "CEZ Shrub (Large)";
+		sprite = "CEZBB0";
+		width = 32;
+		height = 48;
+	}
+	1117
+	{
+		arrow = 1;
+		title = "Pole Banner (Red)";
+		sprite = "BANRA0";
+		width = 40;
+		height = 224;
+	}
+	1118
+	{
+		arrow = 1;
+		title = "Pole Banner (Blue)";
+		sprite = "BANRA0";
+		width = 40;
+		height = 224;
+	}
+	1119
+	{
+		title = "Candle";
+		sprite = "CNDLA0";
+		width = 8;
+		height = 48;
+		flags1text = "[1] Add corona";
+	}
+	1120
+	{
+		title = "Candle Pricket";
+		sprite = "CNDLB0";
+		width = 8;
+		height = 176;
+		flags1text = "[1] Add corona";
+	}
+	1121
+	{
+		title = "Flame Holder";
+		sprite = "FLMHA0";
+		width = 24;
+		height = 80;
+		flags1text = "[1] Add corona";
+		flags4text = "[4] No flame";
+	}
+	1122
+	{
+		title = "Fire Torch";
+		sprite = "CTRCA0";
+		width = 16;
+		height = 80;
+	}
+	1123
+	{
+		title = "Cannonball Launcher";
+		sprite = "internal:cannonball";
+		width = 8;
+		height = 16;
+	}
+	1124
+	{
+		blocking = 2;
+		title = "Cannonball";
+		sprite = "CBLLA0";
+		width = 20;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1125
+	{
+		title = "Brambles";
+		sprite = "CABRALAR";
+		width = 48;
+		height = 32;
+	}
+	1126
+	{
+		title = "Invisible Lockon Object";
+		sprite = "LCKNC0";
+		width = 16;
+		height = 32;
+	}
+	1127
+	{
+		title = "Spectator Eggrobo";
+		sprite = "EGR1A1";
+		width = 20;
+		height = 72;
+	}
+	1128
+	{
+		arrow = 1;
+		title = "Waving Flag (Red)";
+		sprite = "CFLGA0";
+		width = 8;
+		height = 208;
+	}
+	1129
+	{
+		arrow = 1;
+		title = "Waving Flag (Blue)";
+		sprite = "CFLGA0";
+		width = 8;
+		height = 208;
+	}
+}
+
+aridcanyon
+{
+	color = 10; // Green
+	title = "Arid Canyon";
+
+	1200
+	{
+		title = "Tumbleweed (Big)";
+		sprite = "BTBLA0";
+		width = 24;
+		height = 48;
+		flags8text = "[8] Moves perpetually";
+	}
+	1201
+	{
+		title = "Tumbleweed (Small)";
+		sprite = "STBLA0";
+		width = 12;
+		height = 24;
+		flags8text = "[8] Moves perpetually";
+	}
+	1202
+	{
+		arrow = 1;
+		title = "Rock Spawner";
+		sprite = "ROIAA0";
+		width = 8;
+		height = 16;
+		angletext = "Tag";
+	}
+	1203
+	{
+		title = "Tiny Red Flower Cactus";
+		sprite = "CACTA0";
+		width = 13;
+		height = 24;
+	}
+	1204
+	{
+		title = "Small Red Flower Cactus";
+		sprite = "CACTB0";
+		width = 15;
+		height = 52;
+	}
+	1205
+	{
+		title = "Tiny Blue Flower Cactus";
+		sprite = "CACTC0";
+		width = 13;
+		height = 24;
+	}
+	1206
+	{
+		title = "Small Blue Flower Cactus";
+		sprite = "CACTD0";
+		width = 15;
+		height = 52;
+	}
+	1207
+	{
+		title = "Prickly Pear";
+		sprite = "CACTE0";
+		width = 32;
+		height = 96;
+	}
+	1208
+	{
+		title = "Barrel Cactus";
+		sprite = "CACTF0";
+		width = 20;
+		height = 128;
+	}
+	1209
+	{
+		title = "Tall Barrel Cactus";
+		sprite = "CACTG0";
+		width = 24;
+		height = 224;
+	}
+	1210
+	{
+		title = "Armed Cactus";
+		sprite = "CACTH0";
+		width = 24;
+		height = 256;
+	}
+	1211
+	{
+		title = "Ball Cactus";
+		sprite = "CACTI0";
+		width = 48;
+		height = 96;
+	}
+	1212
+	{
+		title = "Caution Sign";
+		sprite = "WWSGAR";
+		width = 22;
+		height = 64;
+	}
+	1213
+	{
+		title = "Cacti Sign";
+		sprite = "WWS2AR";
+		width = 22;
+		height = 64;
+	}
+	1214
+	{
+		title = "Sharp Turn Sign";
+		sprite = "WWS3ALAR";
+		width = 16;
+		height = 192;
+	}
+	1215
+	{
+		title = "Mine Oil Lamp";
+		sprite = "OILLA0";
+		width = 22;
+		height = 64;
+		hangs = 1;
+	}
+	1216
+	{
+		title = "TNT Barrel";
+		sprite = "BARRA1";
+		width = 24;
+		height = 63;
+	}
+	1217
+	{
+		title = "TNT Proximity Shell";
+		sprite = "REMTA0";
+		width = 64;
+		height = 40;
+	}
+	1218
+	{
+		title = "Dust Devil";
+		sprite = "TAZDCR";
+		width = 80;
+		height = 416;
+	}
+	1219
+	{
+		title = "Minecart Spawner";
+		sprite = "MCRTCLFR";
+		width = 22;
+		height = 32;
+	}
+	1220
+	{
+		title = "Minecart Stopper";
+		sprite = "MCRTIR";
+		width = 32;
+		height = 32;
+	}
+	1221
+	{
+		title = "Minecart Saloon Door";
+		sprite = "SALDARAL";
+		width = 96;
+		height = 160;
+		flags8text = "[8] Allow non-minecart players";
+	}
+	1222
+	{
+		title = "Train Cameo Spawner";
+		sprite = "TRAEBRBL";
+		width = 28;
+		height = 32;
+	}
+	1223
+	{
+		title = "Train Dust Spawner";
+		sprite = "ADSTA0";
+		width = 4;
+		height = 4;
+	}
+	1224
+	{
+		title = "Train Steam Spawner";
+		sprite = "STEAA0";
+		width = 4;
+		height = 4;
+	}
+	1229
+	{
+		title = "Minecart Switch Point";
+		sprite = "internal:zoom";
+		width = 8;
+		height = 16;
+		flags8text = "[8] Enable switching";
+	}
+	1230
+	{
+		title = "Tiny Cactus";
+		sprite = "CACTJ0";
+		width = 13;
+		height = 28;
+	}
+	1231
+	{
+		title = "Small Cactus";
+		sprite = "CACTK0";
+		width = 15;
+		height = 60;
+	}
+}
+
+redvolcano
+{
+	color = 10; // Green
+	title = "Red Volcano";
+
+	1300
+	{
+		arrow = 1;
+		title = "Flame Jet (Horizontal)";
+		sprite = "internal:flameh";
+		width = 16;
+		height = 40;
+		flags8text = "[8] Waves vertically";
+		angletext = "On/Off time";
+		parametertext = "Strength";
+	}
+	1301
+	{
+		title = "Flame Jet (Vertical)";
+		sprite = "internal:flamev";
+		width = 16;
+		height = 40;
+		flags8text = "[8] Shoot downwards";
+		angletext = "On/Off time";
+		parametertext = "Strength";
+	}
+	1302
+	{
+		title = "Spinning Flame Jet (Counter-Clockwise)";
+		sprite = "internal:flame2";
+		width = 16;
+		height = 24;
+	}
+	1303
+	{
+		title = "Spinning Flame Jet (Clockwise)";
+		sprite = "internal:flame1";
+		width = 16;
+		height = 24;
+	}
+	1304
+	{
+		title = "Lavafall";
+		sprite = "LFALF0";
+		width = 30;
+		height = 32;
+		angletext = "Initial delay";
+		flags8text = "[8] Double size";
+	}
+	1305
+	{
+		title = "Rollout Rock";
+		sprite = "PUMIA1A5";
+		width = 30;
+		height = 60;
+		flags8text = "[8] Non-buoyant";
+	}
+	1306
+	{
+		title = "Big Fern";
+		sprite = "JPLAB0";
+		width = 32;
+		height = 48;
+	}
+	1307
+	{
+		title = "Jungle Palm";
+		sprite = "JPLAC0";
+		width = 32;
+		height = 48;
+	}
+	1308
+	{
+		title = "Torch Flower";
+		sprite = "TFLOA0";
+		width = 14;
+		height = 110;
+	}
+	1309
+	{
+		title = "RVZ1 Wall Vine (Long)";
+		sprite = "WVINALAR";
+		width = 1;
+		height = 288;
+	}
+	1310
+	{
+		title = "RVZ1 Wall Vine (Short)";
+		sprite = "WVINBLBR";
+		width = 1;
+		height = 288;
+	}
+}
+
+botanicserenity
+{
+	color = 10; // Green
+	title = "Botanic Serenity";
+	width = 16;
+	height = 32;
+	sprite = "BSZ1A0";
+	1400
+	{
+		title = "Tall Flower (Red)";
+		sprite = "BSZ1A0";
+	}
+	1401
+	{
+		title = "Tall Flower (Purple)";
+		sprite = "BSZ1B0";
+	}
+	1402
+	{
+		title = "Tall Flower (Blue)";
+		sprite = "BSZ1C0";
+	}
+	1403
+	{
+		title = "Tall Flower (Cyan)";
+		sprite = "BSZ1D0";
+	}
+	1404
+	{
+		title = "Tall Flower (Yellow)";
+		sprite = "BSZ1E0";
+	}
+	1405
+	{
+		title = "Tall Flower (Orange)";
+		sprite = "BSZ1F0";
+	}
+	1410
+	{
+		title = "Medium Flower (Red)";
+		sprite = "BSZ2A0";
+	}
+	1411
+	{
+		title = "Medium Flower (Purple)";
+		sprite = "BSZ2B0";
+	}
+	1412
+	{
+		title = "Medium Flower (Blue)";
+		sprite = "BSZ2C0";
+	}
+	1413
+	{
+		title = "Medium Flower (Cyan)";
+		sprite = "BSZ2D0";
+	}
+	1414
+	{
+		title = "Medium Flower (Yellow)";
+		sprite = "BSZ2E0";
+	}
+	1415
+	{
+		title = "Medium Flower (Orange)";
+		sprite = "BSZ2F0";
+	}
+	1420
+	{
+		title = "Short Flower (Red)";
+		sprite = "BSZ3A0";
+	}
+	1421
+	{
+		title = "Short Flower (Purple)";
+		sprite = "BSZ3B0";
+	}
+	1422
+	{
+		title = "Short Flower (Blue)";
+		sprite = "BSZ3C0";
+	}
+	1423
+	{
+		title = "Short Flower (Cyan)";
+		sprite = "BSZ3D0";
+	}
+	1424
+	{
+		title = "Short Flower (Yellow)";
+		sprite = "BSZ3E0";
+	}
+	1425
+	{
+		title = "Short Flower (Orange)";
+		sprite = "BSZ3F0";
+	}
+	1430
+	{
+		title = "Tulip (Red)";
+		sprite = "BST1A0";
+	}
+	1431
+	{
+		title = "Tulip (Purple)";
+		sprite = "BST2A0";
+	}
+	1432
+	{
+		title = "Tulip (Blue)";
+		sprite = "BST3A0";
+	}
+	1433
+	{
+		title = "Tulip (Cyan)";
+		sprite = "BST4A0";
+	}
+	1434
+	{
+		title = "Tulip (Yellow)";
+		sprite = "BST5A0";
+	}
+	1435
+	{
+		title = "Tulip (Orange)";
+		sprite = "BST6A0";
+	}
+	1440
+	{
+		title = "Cluster (Red)";
+		sprite = "BSZ5A0";
+	}
+	1441
+	{
+		title = "Cluster (Purple)";
+		sprite = "BSZ5B0";
+	}
+	1442
+	{
+		title = "Cluster (Blue)";
+		sprite = "BSZ5C0";
+	}
+	1443
+	{
+		title = "Cluster (Cyan)";
+		sprite = "BSZ5D0";
+	}
+	1444
+	{
+		title = "Cluster (Yellow)";
+		sprite = "BSZ5E0";
+	}
+	1445
+	{
+		title = "Cluster (Orange)";
+		sprite = "BSZ5F0";
+	}
+	1450
+	{
+		title = "Bush (Red)";
+		sprite = "BSZ6A0";
+	}
+	1451
+	{
+		title = "Bush (Purple)";
+		sprite = "BSZ6B0";
+	}
+	1452
+	{
+		title = "Bush (Blue)";
+		sprite = "BSZ6C0";
+	}
+	1453
+	{
+		title = "Bush (Cyan)";
+		sprite = "BSZ6D0";
+	}
+	1454
+	{
+		title = "Bush (Yellow)";
+		sprite = "BSZ6E0";
+	}
+	1455
+	{
+		title = "Bush (Orange)";
+		sprite = "BSZ6F0";
+	}
+	1460
+	{
+		title = "Vine (Red)";
+		sprite = "BSZ7A0";
+	}
+	1461
+	{
+		title = "Vine (Purple)";
+		sprite = "BSZ7B0";
+	}
+	1462
+	{
+		title = "Vine (Blue)";
+		sprite = "BSZ7C0";
+	}
+	1463
+	{
+		title = "Vine (Cyan)";
+		sprite = "BSZ7D0";
+	}
+	1464
+	{
+		title = "Vine (Yellow)";
+		sprite = "BSZ7E0";
+	}
+	1465
+	{
+		title = "Vine (Orange)";
+		sprite = "BSZ7F0";
+	}
+	1470
+	{
+		title = "BSZ Shrub";
+		sprite = "BSZ8A0";
+	}
+	1471
+	{
+		title = "BSZ Clover";
+		sprite = "BSZ8B0";
+	}
+	1473
+	{
+		title = "Palm Tree (Big)";
+		width = 16;
+		height = 160;
+		sprite = "BSZ8D0";
+	}
+	1475
+	{
+		title = "Palm Tree (Small)";
+		width = 16;
+		height = 80;
+		sprite = "BSZ8F0";
+	}
+}
+
+azuretemple
+{
+	color = 10; // Green
+	title = "Azure Temple";
+
+	1500
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Glaregoyle";
+		sprite = "BGARA1";
+		width = 16;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1501
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Glaregoyle (Up)";
+		sprite = "BGARA1";
+		width = 16;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1502
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Glaregoyle (Down)";
+		sprite = "BGARA1";
+		width = 16;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1503
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Glaregoyle (Long)";
+		sprite = "BGARA1";
+		width = 16;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1504
+	{
+		title = "ATZ Target";
+		sprite = "RCRYB0";
+		width = 24;
+		height = 32;
+	}
+	1505
+	{
+		title = "Green Flame";
+		sprite = "CFLMA0E0";
+		width = 8;
+		height = 32;
+	}
+	1506
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Blue Gargoyle";
+		sprite = "BGARD1";
+		width = 16;
+		height = 40;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+}
+
+dreamhill
+{
+	color = 10; // Green
+	title = "Dream Hill";
+
+	1600
+	{
+		title = "Spring Tree";
+		sprite = "TRE6A0";
+		width = 16;
+		height = 32;
+	}
+	1601
+	{
+		title = "Shleep";
+		sprite = "SHLPA0";
+		width = 24;
+		height = 32;
+	}
+	1602
+	{
+		title = "Pian";
+		sprite = "NTPNALAR";
+		width = 16;
+		height = 32;
+	}
+}
+
+nightstrk
+{
+	color = 13; // Pink
+	title = "NiGHTS Track";
+	width = 8;
+	height = 4096;
+	sprite = "UNKNA0";
+
+	1700
+	{
+		title = "Axis";
+		sprite = "internal:axis1";
+		circle = 1;
+		unflippable = true;
+		ignoreZ = true;
+		flagsvaluetext = "Order";
+		angletext = "Radius/Direction";
+		parametertext = "Mare";
+	}
+	1701
+	{
+		title = "Axis Transfer";
+		sprite = "internal:axis2";
+		unflippable = true;
+		ignoreZ = true;
+		flagsvaluetext = "Order";
+		parametertext = "Mare";
+	}
+	1702
+	{
+		title = "Axis Transfer Line";
+		sprite = "internal:axis3";
+		unflippable = true;
+		ignoreZ = true;
+		flagsvaluetext = "Order";
+		parametertext = "Mare";
+	}
+	1710
+	{
+		title = "Ideya Capture";
+		sprite = "CAPSA0";
+		width = 72;
+		height = 144;
+		angletext = "Rings";
+		parametertext = "Mare";
+	}
+}
+
+nights
+{
+	color = 13; // Pink
+	title = "NiGHTS Items";
+	width = 16;
+	height = 32;
+
+	1703
+	{
+		title = "Ideya Drone";
+		sprite = "NDRNA1";
+		width = 16;
+		height = 56;
+		flags1text = "[1] Align player to middle";
+		flags4text = "[4] Align player to top";
+		flags8text = "[8] Die upon time up";
+		angletext = "Time limit";
+		parametertext = "Height";
+	}
+	1704
+	{
+		arrow = 1;
+		title = "NiGHTS Bumper";
+		sprite = "NBMPG3G7";
+		width = 32;
+		height = 64;
+		unflippable = true;
+		flagsvaluetext = "Pitch";
+		angletext = "Yaw";
+	}
+	1705
+	{
+		arrow = 1;
+		title = "Hoop (Generic)";
+		sprite = "HOOPA0";
+		width = 80;
+		height = 160;
+		unflippable = true;
+		centerHitbox = true;
+		flagsvaluetext = "Height";
+		angletext = "Pitch/Yaw";
+	}
+	1706
+	{
+		title = "Blue Sphere";
+		sprite = "SPHRA0";
+		width = 16;
+		height = 24;
+		flags8height = 24;
+		flags8text = "[8] Float";
+		unflippable = true;
+	}
+	1707
+	{
+		title = "Super Paraloop";
+		sprite = "NPRUA0";
+		flags4text = "[4] Bonus time only";
+		flags8text = "[8] Spawn immediately";
+	}
+	1708
+	{
+		title = "Drill Refill";
+		sprite = "NPRUB0";
+		flags4text = "[4] Bonus time only";
+		flags8text = "[8] Spawn immediately";
+	}
+	1709
+	{
+		title = "Nightopian Helper";
+		sprite = "NPRUC0";
+		flags4text = "[4] Bonus time only";
+		flags8text = "[8] Spawn immediately";
+	}
+	1711
+	{
+		title = "Extra Time";
+		sprite = "NPRUD0";
+		flags4text = "[4] Bonus time only";
+		flags8text = "[8] Spawn immediately";
+	}
+	1712
+	{
+		title = "Link Freeze";
+		sprite = "NPRUE0";
+		flags4text = "[4] Bonus time only";
+		flags8text = "[8] Spawn immediately";
+	}
+	1713
+	{
+		arrow = 1;
+		title = "Hoop (Customizable)";
+		flags1text = "[1] Radius +16";
+		flags2text = "[2] Radius +32";
+		flags4text = "[4] Radius +64";
+		flags8text = "[8] Radius +128";
+		sprite = "HOOPA0";
+		width = 80;
+		height = 160;
+		unflippable = true;
+		centerHitbox = true;
+	}
+	1714
+	{
+		title = "Ideya Anchor Point";
+		sprite = "internal:axis1";
+		width = 8;
+		height = 16;
+		parametertext = "Ideya";
+	}
+}
+
+mario
+{
+	color = 6; // Brown
+	title = "Mario";
+
+	1800
+	{
+		title = "Coin";
+		sprite = "COINA0";
+		width = 16;
+		height = 24;
+		flags8height = 24;
+		flags8text = "[8] Float";
+	}
+	1801
+	{
+		arrow = 1;
+		title = "Goomba";
+		sprite = "GOOMA0";
+		width = 24;
+		height = 32;
+	}
+	1802
+	{
+		arrow = 1;
+		title = "Goomba (Blue)";
+		sprite = "BGOMA0";
+		width = 24;
+		height = 32;
+	}
+	1803
+	{
+		title = "Fire Flower";
+		sprite = "FFWRB0";
+		width = 16;
+		height = 32;
+	}
+	1804
+	{
+		title = "Koopa Shell";
+		sprite = "SHLLA1";
+		width = 16;
+		height = 20;
+	}
+	1805
+	{
+		title = "Puma (Jumping Fireball)";
+		sprite = "PUMAA0";
+		width = 8;
+		height = 16;
+		angletext = "Jump strength";
+	}
+	1806
+	{
+		title = "King Bowser";
+		sprite = "KOOPA0";
+		width = 16;
+		height = 48;
+	}
+	1807
+	{
+		title = "Axe";
+		sprite = "MAXEA0";
+		width = 8;
+		height = 16;
+	}
+	1808
+	{
+		title = "Bush (Short)";
+		sprite = "MUS1A0";
+		width = 16;
+		height = 32;
+	}
+	1809
+	{
+		title = "Bush (Tall)";
+		sprite = "MUS2A0";
+		width = 16;
+		height = 32;
+	}
+	1810
+	{
+		title = "Toad";
+		sprite = "TOADA0";
+		width = 8;
+		height = 32;
+	}
+}
+
+christmasdisco
+{
+	color = 10; // Green
+	title = "Christmas & Disco";
+
+	1850
+	{
+		title = "Christmas Pole";
+		sprite = "XMS1A0";
+		width = 16;
+		height = 40;
+	}
+	1851
+	{
+		title = "Candy Cane";
+		sprite = "XMS2A0";
+		width = 8;
+		height = 32;
+	}
+	1852
+	{
+		blocking = 2;
+		title = "Snowman";
+		sprite = "XMS3A0";
+		width = 16;
+		height = 64;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1853
+	{
+		blocking = 2;
+		title = "Snowman (With Hat)";
+		sprite = "XMS3B0";
+		width = 16;
+		height = 80;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+	1854
+	{
+		title = "Lamp Post";
+		sprite = "XMS4A0";
+		width = 8;
+		height = 120;
+	}
+	1855
+	{
+		title = "Lamp Post (Snow)";
+		sprite = "XMS4B0";
+		width = 8;
+		height = 120;
+	}
+	1856
+	{
+		title = "Hanging Star";
+		sprite = "XMS5A0";
+		width = 4;
+		height = 80;
+		hangs = 1;
+	}
+	1857
+	{
+		title = "Berry Bush (Snow)";
+		sprite = "BUS1B0";
+		width = 16;
+		height = 32;
+	}
+	1858
+	{
+		title = "Bush (Snow)";
+		sprite = "BUS2B0";
+		width = 16;
+		height = 32;
+	}
+	1859
+	{
+		title = "Blueberry Bush (Snow)";
+		sprite = "BUS3B0";
+		width = 16;
+		height = 32;
+	}
+	1875
+	{
+		title = "Disco Ball";
+		sprite = "DBALA0";
+		width = 16;
+		height = 54;
+		hangs = 1;
+	}
+	1876
+	{
+		arrow = 1;
+		blocking = 2;
+		title = "Eggman Disco Statue";
+		sprite = "ESTAB1";
+		width = 20;
+		height = 96;
+		flags4text = "[4] Slides when pushed";
+		flags8text = "[8] Not pushable";
+	}
+}
+
+stalagmites
+{
+	color = 10; // Green
+	title = "Stalagmites";
+	width = 16;
+	height = 40;
+
+	1900
+	{
+		title = "Brown Stalagmite (Tall)";
+		sprite = "STLGA0";
+		width = 16;
+		height = 40;
+	}
+	1901
+	{
+		title = "Brown Stalagmite";
+		sprite = "STLGB0";
+		width = 16;
+		height = 40;
+	}
+	1902
+	{
+		title = "Orange Stalagmite (Tall)";
+		sprite = "STLGC0";
+		width = 16;
+		height = 40;
+	}
+	1903
+	{
+		title = "Orange Stalagmite";
+		sprite = "STLGD0";
+		width = 16;
+		height = 40;
+	}
+	1904
+	{
+		title = "Red Stalagmite (Tall)";
+		sprite = "STLGE0";
+		width = 16;
+		height = 40;
+	}
+	1905
+	{
+		title = "Red Stalagmite";
+		sprite = "STLGF0";
+		width = 16;
+		height = 40;
+	}
+	1906
+	{
+		title = "Gray Stalagmite (Tall)";
+		sprite = "STLGG0";
+		width = 24;
+		height = 96;
+	}
+	1907
+	{
+		title = "Gray Stalagmite";
+		sprite = "STLGH0";
+		width = 16;
+		height = 40;
+	}
+	1908
+	{
+		title = "Blue Stalagmite (Tall)";
+		sprite = "STLGI0";
+		width = 16;
+		height = 40;
+	}
+	1909
+	{
+		title = "Blue Stalagmite";
+		sprite = "STLGJ0";
+		width = 16;
+		height = 40;
+	}
+}
+
+hauntedheights
+{
+	color = 10; // Green
+	title = "Haunted Heights";
+
+	2000
+	{
+		title = "Smashing Spikeball";
+		sprite = "FMCEA0";
+		width = 18;
+		height = 28;
+		angletext = "Initial delay";
+	}
+	2001
+	{
+		title = "HHZ Grass";
+		sprite = "HHZMA0";
+		width = 16;
+		height = 40;
+	}
+	2002
+	{
+		title = "HHZ Tentacle 1";
+		sprite = "HHZMB0";
+		width = 16;
+		height = 40;
+	}
+	2003
+	{
+		title = "HHZ Tentacle 2";
+		sprite = "HHZMC0";
+		width = 16;
+		height = 40;
+	}
+	2004
+	{
+		title = "HHZ Stalagmite (Tall)";
+		sprite = "HHZME0";
+		width = 16;
+		height = 40;
+	}
+	2005
+	{
+		title = "HHZ Stalagmite (Short)";
+		sprite = "HHZMF0";
+		width = 16;
+		height = 40;
+	}
+	2006
+	{
+		title = "Jack-o'-lantern 1";
+		sprite = "PUMKA0";
+		width = 16;
+		height = 40;
+		flags1text = "Don't flicker";
+	}
+	2007
+	{
+		title = "Jack-o'-lantern 2";
+		sprite = "PUMKB0";
+		width = 16;
+		height = 40;
+		flags1text = "Don't flicker";
+	}
+	2008
+	{
+		title = "Jack-o'-lantern 3";
+		sprite = "PUMKC0";
+		width = 16;
+		height = 40;
+		flags1text = "Don't flicker";
+	}
+	2009
+	{
+		title = "Purple Mushroom";
+		sprite = "SHRMD0";
+		width = 16;
+		height = 48;
+	}
+	2010
+	{
+		title = "HHZ Tree";
+		sprite = "HHPLC0";
+		width = 12;
+		height = 40;
+	}
+}
+
+frozenhillside
+{
+	color = 10; // Green
+	title = "Frozen Hillside";
+
+	2100
+	{
+		title = "Ice Shard (Small)";
+		sprite = "FHZIA0";
+		width = 8;
+		height = 32;
+	}
+	2101
+	{
+		title = "Ice Shard (Large)";
+		sprite = "FHZIB0";
+		width = 8;
+		height = 32;
+	}
+	2102
+	{
+		title = "Crystal Tree (Aqua)";
+		sprite = "TRE3A0";
+		width = 20;
+		height = 200;
+	}
+	2103
+	{
+		title = "Crystal Tree (Pink)";
+		sprite = "TRE3B0";
+		width = 20;
+		height = 200;
+	}
+	2104
+	{
+		title = "Amy Cameo";
+		sprite = "ROSYA1";
+		width = 16;
+		height = 48;
+		flags1text = "[1] Grayscale mode";
+	}
+	2105
+	{
+		title = "Mistletoe";
+		sprite = "XMS6A0";
+		width = 52;
+		height = 106;
+	}
+}
+
+flickies
+{
+	color = 10; // Green
+	title = "Flickies";
+	width = 8;
+	height = 20;
+	flags1text = "[1] Move aimlessly";
+	flags4text = "[4] No movement";
+	flags8text = "[8] Hop";
+	angletext = "Radius";
+
+	2200
+	{
+		title = "Bluebird";
+		sprite = "FL01A1";
+	}
+	2201
+	{
+		title = "Rabbit";
+		sprite = "FL02A1";
+	}
+	2202
+	{
+		title = "Chicken";
+		sprite = "FL03A1";
+	}
+	2203
+	{
+		title = "Seal";
+		sprite = "FL04A1";
+	}
+	2204
+	{
+		title = "Pig";
+		sprite = "FL05A1";
+	}
+	2205
+	{
+		title = "Chipmunk";
+		sprite = "FL06A1";
+	}
+	2206
+	{
+		title = "Penguin";
+		sprite = "FL07A1";
+	}
+	2207
+	{
+		title = "Fish";
+		sprite = "FL08A1";
+		parametertext = "Color";
+	}
+	2208
+	{
+		title = "Ram";
+		sprite = "FL09A1";
+	}
+	2209
+	{
+		title = "Puffin";
+		sprite = "FL10A1";
+	}
+	2210
+	{
+		title = "Cow";
+		sprite = "FL11A1";
+	}
+	2211
+	{
+		title = "Rat";
+		sprite = "FL12A1";
+	}
+	2212
+	{
+		title = "Bear";
+		sprite = "FL13A1";
+	}
+	2213
+	{
+		title = "Dove";
+		sprite = "FL14A1";
+	}
+	2214
+	{
+		title = "Cat";
+		sprite = "FL15A1";
+	}
+	2215
+	{
+		title = "Canary";
+		sprite = "FL16A1";
+	}
+	2216
+	{
+		title = "Spider";
+		sprite = "FS01A1";
+	}
+	2217
+	{
+		title = "Bat";
+		sprite = "FS02A0";
+	}
+}
\ No newline at end of file
diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index f316cd032713086ebe4f83e9a81445cc43947034..ec318321d75fcb1b100f67c80d93c8750cb06282 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -3,6 +3,7 @@
 	For Sonic Robo Blast 2 Version 2.2
 	Contributors (alphabetical):
 	* Foxboy
+	* FuriousFox
 	* JJames19119
 	* Kalaron
 	* Kristos
@@ -44,27 +45,32 @@ formatinterface = "SRB2MapSetIO";
 //Sky textures for vanilla maps
 defaultskytextures
 {
-	SKY1 = "MAP01,MAP02,MAP03,MAP50,MAPA1,MAPA2,MAPA5,MAPA6,MAPA9,MAPAA,MAPAB,MAPAC,MAPAD,MAPAE,MAPAG,MAPAJ,MAPAK,MAPF0,MAPF1,MAPFA,MAPM0,MAPM8,MAPMA,MAPMB,MAPMC";
-	SKY4 = "MAP04,MAP06,MAP51,MAPF8,MAPM1";
-	SKY6 = "MAP05";
-	SKY7 = "MAP07,MAP08,MAP09,MAP52,MAPM2,MAPM5";
-	SKY10 = "MAP12,MAP53,MAPM3";
-	SKY11 = "MAP10,MAP11,MAP16,MAP55,MAPF2,MAPF5,MAPF6,MAPF9,MAPM7";
-	SKY13 = "MAP13,MAP54,MAPAS";
-	SKY21 = "MAPAF,MAPF7,MAPM4";
-	SKY22 = "MAP22,MAP23,MAP24,MAP25,MAP56,MAPAN,MAPAO,MAPF4,MAPM6";
-	SKY29 = "MAP58,MAPAV";
+	SKY1 = "MAP01,MAP02,MAP03,MAP33,MAP50,MAP60,MAPF0,MAPM0";
+	SKY2 = "MAPM7,MAPMB";
+	SKY4 = "MAP04,MAP06,MAP61,MAPF6,MAPM1";
+	SKY6 = "MAP05,MAP51,MAPMA";
+	SKY7 = "MAPM2,MAPM5";
+	SKY8 = "MAP07,MAP08,MAP09,MAP52,MAP62,MAPF1";
+	SKY10 = "MAP10,MAP12,MAP53,MAP63,MAPM3";
+	SKY11 = "MAP11,MAPF7";
+	SKY13 = "MAP13,MAP64";
+	SKY14 = "MAP14";
+	SKY15 = "MAP15,MAP54";
+	SKY17 = "MAP70";
+	SKY20 = "MAP32,MAP55,MAP65,MAPF2,MAPF5";
+	SKY21 = "MAPM4";
+	SKY22 = "MAP22,MAP23,MAP25,MAP26,MAP27,MAP56,MAP66,MAPF4,MAPM6";
 	SKY30 = "MAP30";
-	SKY35 = "MAP41";
-	SKY40 = "MAP40";
-	SKY55 = "MAPF3,MAPM9";
-	SKY66 = "MAPAT";
-	SKY99 = "MAP57";
-	SKY103 = "MAPA3,MAPA4,MAPAU";
-	SKY107 = "MAPA7,MAPA8";
-	SKY117 = "MAPAH,MAPAI";
-	SKY127 = "MAPAR";
-	SKY132 = "MAPAW";
+	SKY31 = "MAP31";
+	SKY35 = "MAP42";
+	SKY40 = "MAP41,MAP71,MAPM9";
+	SKY55 = "MAPF3,MAPM8";
+	SKY68 = "MAPF8";
+	SKY99 = "MAP57,MAPZ0";
+	SKY159 = "MAP16";
+	SKY172 = "MAP40";
+	SKY300 = "MAP72";
+	SKY301 = "MAP73";
 }
 
 // Default lump name for new map
@@ -90,9 +96,9 @@ skins
 	Sonic;
 	Tails;
 	Knuckles;
-	Metalsonic;
-	Fang;
 	Amy;
+	Fang;
+	Metalsonic;
 }
 
 // Gametypes
@@ -684,9 +690,9 @@ linedeftypes
 			title = "Chain Parameters";
 			prefix = "(9)";
 			flags32text = "[5] Swing instead of spin";
+			flags128text = "[7] Make chain from end item";
 			flags64text = "[6] Player-turnable chain";
-			flags128text = "[7] Make chain from maces";
-			flags256text = "[8] Spawn mace at origin";
+			flags256text = "[8] Spawn link at origin";
 			flags512text = "[9] Don't clip inside ground";
 			flags1024text = "[10] No distance check";
 		}
@@ -3383,14 +3389,14 @@ thingtypes
 		}
 		118
 		{
-			title = "CastleBot FaceStabber";
+			title = "Lance-a-Bot";
 			sprite = "CBFSA1";
 			width = 32;
 			height = 72;
 		}
 		1113
 		{
-			title = "Suspicious FaceStabber Statue";
+			title = "Suspicious Lance-a-Bot Statue";
 			sprite = "CBBSA1";
 			width = 32;
 			height = 72;
@@ -3422,7 +3428,7 @@ thingtypes
 		121
 		{
 			title = "Minus";
-			sprite = "MNUSA1";
+			sprite = "MNUSA0";
 			width = 24;
 			height = 32;
 		}
@@ -3457,6 +3463,13 @@ thingtypes
 			height = 34;
 			flags8text = "[8] Start on fire";
 		}
+		137
+		{
+			title = "Dragonbomber";
+			sprite = "DRABA1";
+			width = 28;
+			height = 48;
+		}
 		105
 		{
 			title = "Jetty-Syn Bomber";
@@ -3642,6 +3655,7 @@ thingtypes
 			sprite = "BRAK01";
 			width = 48;
 			height = 160;
+			flags1text = "[1] No origin-fling death";
 			flags4text = "[4] End level on death";
 			flags8text = "[8] Electric barrier";
 		}
@@ -4899,6 +4913,7 @@ thingtypes
 			sprite = "ESTAA1";
 			width = 32;
 			height = 240;
+			flags1text = "[1] Solid gold";
 			flags4text = "[4] Slides when pushed";
 			flags8text = "[8] Not pushable";
 		}
@@ -4991,7 +5006,7 @@ thingtypes
 		{
 			arrow = 1;
 			blocking = 2;
-			title = "FaceStabber Statue";
+			title = "Lance-a-Bot Statue";
 			sprite = "CBBSA1";
 			width = 32;
 			height = 72;
@@ -5726,6 +5741,24 @@ thingtypes
 			width = 24;
 			height = 32;
 		}
+		1505
+		{
+			title = "Green Flame";
+			sprite = "CFLMA0E0";
+			width = 8;
+			height = 32;
+		}
+		1506
+		{
+			arrow = 1;
+			blocking = 2;
+			title = "Blue Gargoyle";
+			sprite = "BGARD1";
+			width = 16;
+			height = 40;
+			flags4text = "[4] Slides when pushed";
+			flags8text = "[8] Not pushable";
+		}
 	}
 
 	dreamhill
diff --git a/extras/conf/SRB2_22Doom.cfg b/extras/conf/SRB2_22Doom.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..65e49d3871be87df0c3eb20831543a3304191d1d
--- /dev/null
+++ b/extras/conf/SRB2_22Doom.cfg
@@ -0,0 +1,38 @@
+/************************************************************************\
+	Zone Builder Game Configuration for Sonic Robo Blast 2 Version 2.2
+\************************************************************************/
+
+// This is required to prevent accidental use of a different configuration
+type = "Doom Builder 2 Game Configuration";
+
+// This is the title to show for this game
+game = "Sonic Robo Blast 2 - 2.2 (Doom format)";
+
+// This is the simplified game engine/sourceport name
+engine = "zdoom";
+
+// Settings common to all games and all map formats
+include("Includes\\SRB222_common.cfg", "common");
+
+// Settings common to Doom map format
+include("Includes\\SRB222_common.cfg", "mapformat_doom");
+
+include("Includes\\Game_SRB222.cfg");
+
+// Script lumps detection
+scriptlumpnames
+{
+	include("Includes\\SRB222_misc.cfg", "scriptlumpnames");
+}
+
+// THING TYPES
+thingtypes
+{
+	include("Includes\\SRB222_things.cfg");
+}
+
+//Default things filters
+thingsfilters
+{
+	include("Includes\\SRB222_misc.cfg", "thingsfilters");
+}
\ No newline at end of file
diff --git a/extras/conf/SRB2_22UDMF.cfg b/extras/conf/SRB2_22UDMF.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..52104ed090b766914ee69d350c16799b1272d0d2
--- /dev/null
+++ b/extras/conf/SRB2_22UDMF.cfg
@@ -0,0 +1,47 @@
+/************************************************************************\
+	Zone Builder Game Configuration for Sonic Robo Blast 2 Version 2.2
+\************************************************************************/
+
+// This is required to prevent accidental use of a different configuration
+type = "Doom Builder 2 Game Configuration";
+
+// This is the title to show for this game
+game = "Sonic Robo Blast 2 - 2.2 (UDMF)";
+
+// This is the simplified game engine/sourceport name
+engine = "zdoom";
+
+// Settings common to all games and all map formats
+include("Includes\\SRB222_common.cfg", "common");
+
+// Settings common to Doom map format
+include("Includes\\SRB222_common.cfg", "mapformat_udmf");
+
+include("Includes\\Game_SRB222.cfg");
+
+// Script lumps detection
+scriptlumpnames
+{
+	include("Includes\\SRB222_misc.cfg", "scriptlumpnames");
+}
+
+// THING TYPES
+thingtypes
+{
+	include("Includes\\SRB222_things.cfg");
+}
+
+//Default things filters
+thingsfilters
+{
+	include("Includes\\SRB222_misc.cfg", "thingsfilters");
+}
+
+// ENUMERATIONS
+// Each engine has its own additional thing types
+// These are enumerated lists for linedef types and UDMF fields.
+enums
+{
+	// Basic game enums
+	include("Includes\\SRB222_misc.cfg", "enums");
+}
\ No newline at end of file
diff --git a/libs/DLL-README.txt b/libs/DLL-README.txt
index 45680c535667c5b7a32bc4f686991742efa6a551..7a288887eae0ae6f7d936a325616b49d6a1f95ec 100644
--- a/libs/DLL-README.txt
+++ b/libs/DLL-README.txt
@@ -1,12 +1,12 @@
 # SRB2 - Which DLLs do I need to bundle?
 
-Updated 12/4/2018 (v2.1.21)
+Updated 12/6/2019 (v2.2)
 
 Here are the required DLLs, per build. For each architecture, copy all the binaries from these folders:
 
 * libs\dll-binaries\[i686/x86_64]
 * libs\SDL2\[i686/x86_64]...\bin
-* libs\SDL2_mixer\[i686/x86_64]...\bin
+* libs\SDL2mixerX\[i686/x86_64]...\bin
 * libs\libopenmpt\[x86/x86_64]...\bin\mingw
 
 and don't forget to build r_opengl.dll for srb2dd.
@@ -17,7 +17,7 @@ and don't forget to build r_opengl.dll for srb2dd.
 * libs\dll-binaries\i686\libgme.dll
 * libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
 * libs\SDL2\i686-w64-mingw32\bin\SDL2.dll
-* libs\SDL2_mixer\i686-w64-mingw32\bin\*.dll (get everything)
+* libs\SDL2mixerX\i686-w64-mingw32\bin\*.dll (get everything)
 * libs\libopenmpt\x86\bin\mingw\libopenmpt.dll
 
 ## srb2win, 64-bit
@@ -26,21 +26,5 @@ and don't forget to build r_opengl.dll for srb2dd.
 * libs\dll-binaries\x86_64\libgme.dll
 * libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
 * libs\SDL2\x86_64-w64-mingw32\bin\SDL2.dll
-* libs\SDL2_mixer\x86_64-w64-mingw32\bin\*.dll (get everything)
+* libs\SDL2mixerX\x86_64-w64-mingw32\bin\*.dll (get everything)
 * libs\libopenmpt\x86_64\bin\mingw\libopenmpt.dll
-
-## srb2dd, 32-bit
-
-* libs\dll-binaries\i686\exchndl.dll
-* libs\dll-binaries\i686\fmodex.dll
-* libs\dll-binaries\i686\libgme.dll
-* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
-* r_opengl.dll (build this from make)
-
-## srb2dd, 64-bit
-
-* libs\dll-binaries\x86_64\exchndl.dll
-* libs\dll-binaries\x86_64\fmodex.dll
-* libs\dll-binaries\x86_64\libgme.dll
-* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
-* r_opengl.dll (build this from make)
diff --git a/libs/libpng-src/Makefile b/libs/libpng-src/Makefile
deleted file mode 100644
index 4b02abacfe0c2cb164c43fe9b735173f718ab5aa..0000000000000000000000000000000000000000
--- a/libs/libpng-src/Makefile
+++ /dev/null
@@ -1,2428 +0,0 @@
-# Makefile.in generated by automake 1.16.1 from Makefile.am.
-# Makefile.  Generated from Makefile.in by configure.
-
-# Copyright (C) 1994-2018 Free Software Foundation, Inc.
-
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-
-
-# Makefile.am, the source file for Makefile.in (and hence Makefile), is
-#
-# Copyright (c) 2018 Cosmin Truta
-# Copyright (c) 2004-2016 Glenn Randers-Pehrson
-#
-# This code is released under the libpng license.
-# For conditions of distribution and use, see the disclaimer
-# and license in png.h
-
-
-
-
-
-
-am__is_gnu_make = { \
-  if test -z '$(MAKELEVEL)'; then \
-    false; \
-  elif test -n '$(MAKE_HOST)'; then \
-    true; \
-  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
-    true; \
-  else \
-    false; \
-  fi; \
-}
-am__make_running_with_option = \
-  case $${target_option-} in \
-      ?) ;; \
-      *) echo "am__make_running_with_option: internal error: invalid" \
-              "target option '$${target_option-}' specified" >&2; \
-         exit 1;; \
-  esac; \
-  has_opt=no; \
-  sane_makeflags=$$MAKEFLAGS; \
-  if $(am__is_gnu_make); then \
-    sane_makeflags=$$MFLAGS; \
-  else \
-    case $$MAKEFLAGS in \
-      *\\[\ \	]*) \
-        bs=\\; \
-        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
-          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
-    esac; \
-  fi; \
-  skip_next=no; \
-  strip_trailopt () \
-  { \
-    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
-  }; \
-  for flg in $$sane_makeflags; do \
-    test $$skip_next = yes && { skip_next=no; continue; }; \
-    case $$flg in \
-      *=*|--*) continue;; \
-        -*I) strip_trailopt 'I'; skip_next=yes;; \
-      -*I?*) strip_trailopt 'I';; \
-        -*O) strip_trailopt 'O'; skip_next=yes;; \
-      -*O?*) strip_trailopt 'O';; \
-        -*l) strip_trailopt 'l'; skip_next=yes;; \
-      -*l?*) strip_trailopt 'l';; \
-      -[dEDm]) skip_next=yes;; \
-      -[JT]) skip_next=yes;; \
-    esac; \
-    case $$flg in \
-      *$$target_option*) has_opt=yes; break;; \
-    esac; \
-  done; \
-  test $$has_opt = yes
-am__make_dryrun = (target_option=n; $(am__make_running_with_option))
-am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
-pkgdatadir = $(datadir)/libpng
-pkglibdir = $(libdir)/libpng
-pkglibexecdir = $(libexecdir)/libpng
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = i686-w64-mingw32
-host_triplet = i686-w64-mingw32
-check_PROGRAMS = pngtest$(EXEEXT) pngunknown$(EXEEXT) \
-	pngstest$(EXEEXT) pngvalid$(EXEEXT) pngimage$(EXEEXT) \
-	pngcp$(EXEEXT) $(am__EXEEXT_1)
-am__append_1 = timepng
-bin_PROGRAMS = pngfix$(EXEEXT) png-fix-itxt$(EXEEXT)
-#am__append_2 = arm/arm_init.c\
-#	arm/filter_neon.S arm/filter_neon_intrinsics.c \
-#	arm/palette_neon_intrinsics.c
-
-am__append_3 = mips/mips_init.c\
-	mips/filter_msa_intrinsics.c
-
-am__append_4 = intel/intel_init.c\
-	intel/filter_sse2_intrinsics.c
-
-am__append_5 = powerpc/powerpc_init.c\
-        powerpc/filter_vsx_intrinsics.c
-
-
-#   Versioned symbols and restricted exports
-##am__append_6 = -Wl,-M -Wl,libpng.vers
-#am__append_7 = -Wl,--version-script=libpng.vers
-#   Only restricted exports when possible
-am__append_8 = -export-symbols libpng.sym
-#am__append_9 = -DPNG_PREFIX=''
-subdir = .
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/scripts/libtool.m4 \
-	$(top_srcdir)/scripts/ltoptions.m4 \
-	$(top_srcdir)/scripts/ltsugar.m4 \
-	$(top_srcdir)/scripts/ltversion.m4 \
-	$(top_srcdir)/scripts/lt~obsolete.m4 \
-	$(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
-	$(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
-	$(am__configure_deps) $(pkginclude_HEADERS) $(am__DIST_COMMON)
-am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
- configure.lineno config.status.lineno
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = config.h
-CONFIG_CLEAN_FILES = libpng.pc libpng-config
-CONFIG_CLEAN_VPATH_FILES =
-am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \
-	"$(DESTDIR)$(bindir)" "$(DESTDIR)$(man3dir)" \
-	"$(DESTDIR)$(man5dir)" "$(DESTDIR)$(pkgconfigdir)" \
-	"$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)"
-am__EXEEXT_1 = timepng$(EXEEXT)
-PROGRAMS = $(bin_PROGRAMS)
-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
-am__vpath_adj = case $$p in \
-    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
-    *) f=$$p;; \
-  esac;
-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
-am__install_max = 40
-am__nobase_strip_setup = \
-  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
-am__nobase_strip = \
-  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
-am__nobase_list = $(am__nobase_strip_setup); \
-  for p in $$list; do echo "$$p $$p"; done | \
-  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
-  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
-    if (++n[$$2] == $(am__install_max)) \
-      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
-    END { for (dir in files) print dir, files[dir] }'
-am__base_list = \
-  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
-  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
-am__uninstall_files_from_dir = { \
-  test -z "$$files" \
-    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
-    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
-         $(am__cd) "$$dir" && rm -f $$files; }; \
-  }
-LTLIBRARIES = $(lib_LTLIBRARIES)
-libpng16_la_LIBADD =
-am__libpng16_la_SOURCES_DIST = png.c \
-	pngerror.c pngget.c pngmem.c pngpread.c pngread.c pngrio.c \
-	pngrtran.c pngrutil.c pngset.c pngtrans.c pngwio.c pngwrite.c \
-	pngwtran.c pngwutil.c png.h pngconf.h pngdebug.h pnginfo.h \
-	pngpriv.h pngstruct.h pngusr.dfa arm/arm_init.c \
-	arm/filter_neon.S arm/filter_neon_intrinsics.c \
-	arm/palette_neon_intrinsics.c mips/mips_init.c \
-	mips/filter_msa_intrinsics.c intel/intel_init.c \
-	intel/filter_sse2_intrinsics.c powerpc/powerpc_init.c \
-	powerpc/filter_vsx_intrinsics.c
-am__dirstamp = $(am__leading_dot)dirstamp
-#am__objects_1 = arm/arm_init.lo arm/filter_neon.lo \
-#	arm/filter_neon_intrinsics.lo \
-#	arm/palette_neon_intrinsics.lo
-am__objects_2 = mips/mips_init.lo \
-	mips/filter_msa_intrinsics.lo
-am__objects_3 = intel/intel_init.lo \
-	intel/filter_sse2_intrinsics.lo
-am__objects_4 = powerpc/powerpc_init.lo \
-	powerpc/filter_vsx_intrinsics.lo
-am_libpng16_la_OBJECTS = png.lo pngerror.lo \
-	pngget.lo pngmem.lo pngpread.lo pngread.lo pngrio.lo \
-	pngrtran.lo pngrutil.lo pngset.lo pngtrans.lo pngwio.lo \
-	pngwrite.lo pngwtran.lo pngwutil.lo $(am__objects_1) \
-	$(am__objects_2) $(am__objects_3) $(am__objects_4)
-nodist_libpng16_la_OBJECTS =
-libpng16_la_OBJECTS =  \
-	$(am_libpng16_la_OBJECTS) \
-	$(nodist_libpng16_la_OBJECTS)
-AM_V_lt = $(am__v_lt_$(V))
-am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
-am__v_lt_0 = --silent
-am__v_lt_1 = 
-libpng16_la_LINK = $(LIBTOOL) $(AM_V_lt) \
-	--tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \
-	$(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-	$(libpng16_la_LDFLAGS) $(LDFLAGS) -o \
-	$@
-am_png_fix_itxt_OBJECTS = contrib/tools/png-fix-itxt.$(OBJEXT)
-png_fix_itxt_OBJECTS = $(am_png_fix_itxt_OBJECTS)
-png_fix_itxt_LDADD = $(LDADD)
-am_pngcp_OBJECTS = contrib/tools/pngcp.$(OBJEXT)
-pngcp_OBJECTS = $(am_pngcp_OBJECTS)
-pngcp_DEPENDENCIES = libpng16.la
-am_pngfix_OBJECTS = contrib/tools/pngfix.$(OBJEXT)
-pngfix_OBJECTS = $(am_pngfix_OBJECTS)
-pngfix_DEPENDENCIES = libpng16.la
-am_pngimage_OBJECTS = contrib/libtests/pngimage.$(OBJEXT)
-pngimage_OBJECTS = $(am_pngimage_OBJECTS)
-pngimage_DEPENDENCIES = libpng16.la
-am_pngstest_OBJECTS = contrib/libtests/pngstest.$(OBJEXT)
-pngstest_OBJECTS = $(am_pngstest_OBJECTS)
-pngstest_DEPENDENCIES = libpng16.la
-am_pngtest_OBJECTS = pngtest.$(OBJEXT)
-pngtest_OBJECTS = $(am_pngtest_OBJECTS)
-pngtest_DEPENDENCIES = libpng16.la
-am_pngunknown_OBJECTS = contrib/libtests/pngunknown.$(OBJEXT)
-pngunknown_OBJECTS = $(am_pngunknown_OBJECTS)
-pngunknown_DEPENDENCIES = libpng16.la
-am_pngvalid_OBJECTS = contrib/libtests/pngvalid.$(OBJEXT)
-pngvalid_OBJECTS = $(am_pngvalid_OBJECTS)
-pngvalid_DEPENDENCIES = libpng16.la
-am_timepng_OBJECTS = contrib/libtests/timepng.$(OBJEXT)
-timepng_OBJECTS = $(am_timepng_OBJECTS)
-timepng_DEPENDENCIES = libpng16.la
-SCRIPTS = $(bin_SCRIPTS)
-AM_V_P = $(am__v_P_$(V))
-am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY))
-am__v_P_0 = false
-am__v_P_1 = :
-AM_V_GEN = $(am__v_GEN_$(V))
-am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
-am__v_GEN_0 = @echo "  GEN     " $@;
-am__v_GEN_1 = 
-AM_V_at = $(am__v_at_$(V))
-am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
-am__v_at_0 = @
-am__v_at_1 = 
-DEFAULT_INCLUDES = -I.
-depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__maybe_remake_depfiles = depfiles
-am__depfiles_remade = ./$(DEPDIR)/png.Plo ./$(DEPDIR)/pngerror.Plo \
-	./$(DEPDIR)/pngget.Plo ./$(DEPDIR)/pngmem.Plo \
-	./$(DEPDIR)/pngpread.Plo ./$(DEPDIR)/pngread.Plo \
-	./$(DEPDIR)/pngrio.Plo ./$(DEPDIR)/pngrtran.Plo \
-	./$(DEPDIR)/pngrutil.Plo ./$(DEPDIR)/pngset.Plo \
-	./$(DEPDIR)/pngtest.Po ./$(DEPDIR)/pngtrans.Plo \
-	./$(DEPDIR)/pngwio.Plo ./$(DEPDIR)/pngwrite.Plo \
-	./$(DEPDIR)/pngwtran.Plo ./$(DEPDIR)/pngwutil.Plo \
-	arm/$(DEPDIR)/arm_init.Plo arm/$(DEPDIR)/filter_neon.Plo \
-	arm/$(DEPDIR)/filter_neon_intrinsics.Plo \
-	arm/$(DEPDIR)/palette_neon_intrinsics.Plo \
-	contrib/libtests/$(DEPDIR)/pngimage.Po \
-	contrib/libtests/$(DEPDIR)/pngstest.Po \
-	contrib/libtests/$(DEPDIR)/pngunknown.Po \
-	contrib/libtests/$(DEPDIR)/pngvalid.Po \
-	contrib/libtests/$(DEPDIR)/timepng.Po \
-	contrib/tools/$(DEPDIR)/png-fix-itxt.Po \
-	contrib/tools/$(DEPDIR)/pngcp.Po \
-	contrib/tools/$(DEPDIR)/pngfix.Po \
-	intel/$(DEPDIR)/filter_sse2_intrinsics.Plo \
-	intel/$(DEPDIR)/intel_init.Plo \
-	mips/$(DEPDIR)/filter_msa_intrinsics.Plo \
-	mips/$(DEPDIR)/mips_init.Plo \
-	powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo \
-	powerpc/$(DEPDIR)/powerpc_init.Plo
-am__mv = mv -f
-CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-	$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
-LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
-	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
-	$(AM_CCASFLAGS) $(CCASFLAGS)
-AM_V_CPPAS = $(am__v_CPPAS_$(V))
-am__v_CPPAS_ = $(am__v_CPPAS_$(AM_DEFAULT_VERBOSITY))
-am__v_CPPAS_0 = @echo "  CPPAS   " $@;
-am__v_CPPAS_1 = 
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
-	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
-	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
-	$(AM_CFLAGS) $(CFLAGS)
-AM_V_CC = $(am__v_CC_$(V))
-am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
-am__v_CC_0 = @echo "  CC      " $@;
-am__v_CC_1 = 
-CCLD = $(CC)
-LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-	$(AM_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_CCLD = $(am__v_CCLD_$(V))
-am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
-am__v_CCLD_0 = @echo "  CCLD    " $@;
-am__v_CCLD_1 = 
-SOURCES = $(libpng16_la_SOURCES) \
-	$(nodist_libpng16_la_SOURCES) \
-	$(png_fix_itxt_SOURCES) $(pngcp_SOURCES) $(pngfix_SOURCES) \
-	$(pngimage_SOURCES) $(pngstest_SOURCES) $(pngtest_SOURCES) \
-	$(pngunknown_SOURCES) $(pngvalid_SOURCES) $(timepng_SOURCES)
-DIST_SOURCES =  \
-	$(am__libpng16_la_SOURCES_DIST) \
-	$(png_fix_itxt_SOURCES) $(pngcp_SOURCES) $(pngfix_SOURCES) \
-	$(pngimage_SOURCES) $(pngstest_SOURCES) $(pngtest_SOURCES) \
-	$(pngunknown_SOURCES) $(pngvalid_SOURCES) $(timepng_SOURCES)
-am__can_run_installinfo = \
-  case $$AM_UPDATE_INFO_DIR in \
-    n|no|NO) false;; \
-    *) (install-info --version) >/dev/null 2>&1;; \
-  esac
-man3dir = $(mandir)/man3
-man5dir = $(mandir)/man5
-NROFF = nroff
-MANS = $(dist_man_MANS)
-DATA = $(pkgconfig_DATA)
-HEADERS = $(nodist_pkginclude_HEADERS) $(pkginclude_HEADERS)
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
-	$(LISP)config.h.in
-# Read a list of newline-separated strings from the standard input,
-# and print each of them once, without duplicates.  Input order is
-# *not* preserved.
-am__uniquify_input = $(AWK) '\
-  BEGIN { nonempty = 0; } \
-  { items[$$0] = 1; nonempty = 1; } \
-  END { if (nonempty) { for (i in items) print i; }; } \
-'
-# Make sure the list of sources is unique.  This is necessary because,
-# e.g., the same source file might be shared among _SOURCES variables
-# for different programs/libraries.
-am__define_uniq_tagged_files = \
-  list='$(am__tagged_files)'; \
-  unique=`for i in $$list; do \
-    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-  done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
-CSCOPE = cscope
-AM_RECURSIVE_TARGETS = cscope check recheck
-am__tty_colors_dummy = \
-  mgn= red= grn= lgn= blu= brg= std=; \
-  am__color_tests=no
-am__tty_colors = { \
-  $(am__tty_colors_dummy); \
-  if test "X$(AM_COLOR_TESTS)" = Xno; then \
-    am__color_tests=no; \
-  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
-    am__color_tests=yes; \
-  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
-    am__color_tests=yes; \
-  fi; \
-  if test $$am__color_tests = yes; then \
-    red=''; \
-    grn=''; \
-    lgn=''; \
-    blu=''; \
-    mgn=''; \
-    brg=''; \
-    std=''; \
-  fi; \
-}
-am__recheck_rx = ^[ 	]*:recheck:[ 	]*
-am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
-am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
-# A command that, given a newline-separated list of test names on the
-# standard input, print the name of the tests that are to be re-run
-# upon "make recheck".
-am__list_recheck_tests = $(AWK) '{ \
-  recheck = 1; \
-  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
-    { \
-      if (rc < 0) \
-        { \
-          if ((getline line2 < ($$0 ".log")) < 0) \
-	    recheck = 0; \
-          break; \
-        } \
-      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
-        { \
-          recheck = 0; \
-          break; \
-        } \
-      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
-        { \
-          break; \
-        } \
-    }; \
-  if (recheck) \
-    print $$0; \
-  close ($$0 ".trs"); \
-  close ($$0 ".log"); \
-}'
-# A command that, given a newline-separated list of test names on the
-# standard input, create the global log from their .trs and .log files.
-am__create_global_log = $(AWK) ' \
-function fatal(msg) \
-{ \
-  print "fatal: making $@: " msg | "cat >&2"; \
-  exit 1; \
-} \
-function rst_section(header) \
-{ \
-  print header; \
-  len = length(header); \
-  for (i = 1; i <= len; i = i + 1) \
-    printf "="; \
-  printf "\n\n"; \
-} \
-{ \
-  copy_in_global_log = 1; \
-  global_test_result = "RUN"; \
-  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
-    { \
-      if (rc < 0) \
-         fatal("failed to read from " $$0 ".trs"); \
-      if (line ~ /$(am__global_test_result_rx)/) \
-        { \
-          sub("$(am__global_test_result_rx)", "", line); \
-          sub("[ 	]*$$", "", line); \
-          global_test_result = line; \
-        } \
-      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
-        copy_in_global_log = 0; \
-    }; \
-  if (copy_in_global_log) \
-    { \
-      rst_section(global_test_result ": " $$0); \
-      while ((rc = (getline line < ($$0 ".log"))) != 0) \
-      { \
-        if (rc < 0) \
-          fatal("failed to read from " $$0 ".log"); \
-        print line; \
-      }; \
-      printf "\n"; \
-    }; \
-  close ($$0 ".trs"); \
-  close ($$0 ".log"); \
-}'
-# Restructured Text title.
-am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
-# Solaris 10 'make', and several other traditional 'make' implementations,
-# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
-# by disabling -e (using the XSI extension "set +e") if it's set.
-am__sh_e_setup = case $$- in *e*) set +e;; esac
-# Default flags passed to test drivers.
-am__common_driver_flags = \
-  --color-tests "$$am__color_tests" \
-  --enable-hard-errors "$$am__enable_hard_errors" \
-  --expect-failure "$$am__expect_failure"
-# To be inserted before the command running the test.  Creates the
-# directory for the log if needed.  Stores in $dir the directory
-# containing $f, in $tst the test, in $log the log.  Executes the
-# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
-# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
-# will run the test scripts (or their associated LOG_COMPILER, if
-# thy have one).
-am__check_pre = \
-$(am__sh_e_setup);					\
-$(am__vpath_adj_setup) $(am__vpath_adj)			\
-$(am__tty_colors);					\
-srcdir=$(srcdir); export srcdir;			\
-case "$@" in						\
-  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
-    *) am__odir=.;; 					\
-esac;							\
-test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
-  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
-if test -f "./$$f"; then dir=./;			\
-elif test -f "$$f"; then dir=;				\
-else dir="$(srcdir)/"; fi;				\
-tst=$$dir$$f; log='$@'; 				\
-if test -n '$(DISABLE_HARD_ERRORS)'; then		\
-  am__enable_hard_errors=no; 				\
-else							\
-  am__enable_hard_errors=yes; 				\
-fi; 							\
-case " $(XFAIL_TESTS) " in				\
-  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
-    am__expect_failure=yes;;				\
-  *)							\
-    am__expect_failure=no;;				\
-esac; 							\
-$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
-# A shell command to get the names of the tests scripts with any registered
-# extension removed (i.e., equivalently, the names of the test logs, with
-# the '.log' extension removed).  The result is saved in the shell variable
-# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
-# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
-# since that might cause problem with VPATH rewrites for suffix-less tests.
-# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
-am__set_TESTS_bases = \
-  bases='$(TEST_LOGS)'; \
-  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
-  bases=`echo $$bases`
-RECHECK_LOGS = $(TEST_LOGS)
-TEST_SUITE_LOG = test-suite.log
-TEST_EXTENSIONS = .exe .test
-LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
-LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
-am__set_b = \
-  case '$@' in \
-    */*) \
-      case '$*' in \
-        */*) b='$*';; \
-          *) b=`echo '$@' | sed 's/\.log$$//'`; \
-       esac;; \
-    *) \
-      b='$*';; \
-  esac
-am__test_logs1 = $(TESTS:=.log)
-am__test_logs2 = $(am__test_logs1:.exe.log=.log)
-TEST_LOGS = $(am__test_logs2:.test.log=.log)
-TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
-TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
-	$(TEST_LOG_FLAGS)
-am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in \
-	$(srcdir)/config.h.in $(srcdir)/libpng-config.in \
-	$(srcdir)/libpng.pc.in AUTHORS INSTALL README TODO compile \
-	config.guess config.sub depcomp install-sh ltmain.sh missing \
-	test-driver
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-distdir = $(PACKAGE)-$(VERSION)
-top_distdir = $(distdir)
-am__remove_distdir = \
-  if test -d "$(distdir)"; then \
-    find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
-      && rm -rf "$(distdir)" \
-      || { sleep 5 && rm -rf "$(distdir)"; }; \
-  else :; fi
-am__post_remove_distdir = $(am__remove_distdir)
-DIST_ARCHIVES = $(distdir).tar.gz $(distdir).tar.xz
-GZIP_ENV = --best
-DIST_TARGETS = dist-xz dist-gzip
-distuninstallcheck_listfiles = find . -type f -print
-am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
-  | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
-distcleancheck_listfiles = find . -type f -print
-
-#distribute headers in /usr/include/libpng/*
-pkgincludedir = $(includedir)/$(PNGLIB_BASENAME)
-ACLOCAL = ${SHELL} /f/lpng1637/missing aclocal-1.16
-AMTAR = $${TAR-tar}
-AM_DEFAULT_VERBOSITY = 1
-AR = ar
-AS = as
-AUTOCONF = ${SHELL} /f/lpng1637/missing autoconf
-AUTOHEADER = ${SHELL} /f/lpng1637/missing autoheader
-AUTOMAKE = ${SHELL} /f/lpng1637/missing automake-1.16
-AWK = gawk
-CC = gcc
-CCAS = gcc
-CCASDEPMODE = depmode=gcc3
-CCASFLAGS = -g -O2
-CCDEPMODE = depmode=gcc3
-CFLAGS = -g -O2
-CPP = gcc -E
-CPPFLAGS = 
-CYGPATH_W = cygpath -w
-DEFS = -DHAVE_CONFIG_H
-DEPDIR = .deps
-
-# DFNCPP is normally just CPP - the C preprocessor - but on Solaris and maybe
-# other operating systems (NeXT?) the C preprocessor selected by configure
-# checks input tokens for validity - effectively it performs part of the ANSI-C
-# parsing - and therefore fails with the .df files.  configure.ac has special
-# checks for this and sets DFNCPP appropriately.
-DFNCPP = gcc -E
-DLLTOOL = dlltool
-DSYMUTIL = 
-DUMPBIN = 
-ECHO_C = 
-ECHO_N = -n
-ECHO_T = 
-EGREP = /usr/bin/grep -E
-EXEEXT = .exe
-FGREP = /usr/bin/grep -F
-GREP = /usr/bin/grep
-INSTALL = /usr/bin/install -c
-INSTALL_DATA = ${INSTALL} -m 644
-INSTALL_PROGRAM = ${INSTALL}
-INSTALL_SCRIPT = ${INSTALL}
-INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
-LD = C:/msys64/mingw32/i686-w64-mingw32/bin/ld.exe
-LDFLAGS = 
-LIBOBJS = 
-LIBS = -lz 
-LIBTOOL = $(SHELL) $(top_builddir)/libtool
-LIPO = 
-LN_S = cp -pR
-LTLIBOBJS = 
-LT_SYS_LIBRARY_PATH = 
-MAINT = #
-MAKEINFO = ${SHELL} /f/lpng1637/missing makeinfo
-MANIFEST_TOOL = :
-MKDIR_P = /usr/bin/mkdir -p
-NM = /mingw32/bin/nm -B
-NMEDIT = 
-OBJDUMP = objdump
-OBJEXT = o
-OTOOL = 
-OTOOL64 = 
-PACKAGE = libpng
-PACKAGE_BUGREPORT = png-mng-implement@lists.sourceforge.net
-PACKAGE_NAME = libpng
-PACKAGE_STRING = libpng 1.6.37
-PACKAGE_TARNAME = libpng
-PACKAGE_URL = 
-PACKAGE_VERSION = 1.6.37
-PATH_SEPARATOR = :
-PNGLIB_MAJOR = 1
-PNGLIB_MINOR = 6
-PNGLIB_RELEASE = 37
-PNGLIB_VERSION = 1.6.37
-
-# PNG_COPTS give extra options for the C compiler to be used on all compilation
-# steps (unless targe_CFLAGS is specified; that will take precedence over
-# AM_CFLAGS)
-PNG_COPTS = 
-PNG_PREFIX = 
-POW_LIB = 
-RANLIB = ranlib
-SED = /usr/bin/sed
-SET_MAKE = 
-SHELL = /bin/sh
-STRIP = strip
-SYMBOL_PREFIX = 
-VERSION = 1.6.37
-abs_builddir = /f/lpng1637
-abs_srcdir = /f/lpng1637
-abs_top_builddir = /f/lpng1637
-abs_top_srcdir = /f/lpng1637
-ac_ct_AR = ar
-ac_ct_CC = gcc
-ac_ct_DUMPBIN = 
-am__include = include
-am__leading_dot = .
-am__quote = 
-am__tar = $${TAR-tar} chof - "$$tardir"
-am__untar = $${TAR-tar} xf -
-
-# generate the -config scripts if required
-binconfigs = libpng16-config
-bindir = ${exec_prefix}/bin
-build = i686-w64-mingw32
-build_alias = i686-w64-mingw32
-build_cpu = i686
-build_os = mingw32
-build_vendor = w64
-builddir = .
-datadir = ${datarootdir}
-datarootdir = ${prefix}/share
-docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
-dvidir = ${docdir}
-exec_prefix = ${prefix}
-host = i686-w64-mingw32
-host_alias = 
-host_cpu = i686
-host_os = mingw32
-host_vendor = w64
-htmldir = ${docdir}
-includedir = ${prefix}/include
-infodir = ${datarootdir}/info
-install_sh = ${SHELL} /f/lpng1637/install-sh
-libdir = ${exec_prefix}/lib
-libexecdir = ${exec_prefix}/libexec
-localedir = ${datarootdir}/locale
-localstatedir = ${prefix}/var
-mandir = ${datarootdir}/man
-mkdir_p = $(MKDIR_P)
-oldincludedir = /usr/include
-pdfdir = ${docdir}
-
-# pkg-config stuff, note that libpng.pc is always required in order
-# to get the correct library
-pkgconfigdir = ${libdir}/pkgconfig
-prefix = /mingw32
-program_transform_name = s,x,x,
-psdir = ${docdir}
-sbindir = ${exec_prefix}/sbin
-sharedstatedir = ${prefix}/com
-srcdir = .
-sysconfdir = ${prefix}/etc
-target_alias = 
-top_build_prefix = 
-top_builddir = .
-top_srcdir = .
-PNGLIB_BASENAME = libpng16
-ACLOCAL_AMFLAGS = -I scripts
-
-# This ensures that pnglibconf.h gets built at the start of 'make all' or
-# 'make check', but it does not add dependencies to the individual programs,
-# this is done below.
-#
-# IMPORTANT: always add the object modules of new programs to the list below
-# because otherwise the sequence 'configure; make new-program' will *sometimes*
-# result in the installed (system) pnglibconf.h being used and the result is
-# always wrong and always very confusing.
-BUILT_SOURCES = pnglibconf.h
-pngtest_SOURCES = pngtest.c
-pngtest_LDADD = libpng16.la
-pngvalid_SOURCES = contrib/libtests/pngvalid.c
-pngvalid_LDADD = libpng16.la
-pngstest_SOURCES = contrib/libtests/pngstest.c
-pngstest_LDADD = libpng16.la
-pngunknown_SOURCES = contrib/libtests/pngunknown.c
-pngunknown_LDADD = libpng16.la
-pngimage_SOURCES = contrib/libtests/pngimage.c
-pngimage_LDADD = libpng16.la
-timepng_SOURCES = contrib/libtests/timepng.c
-timepng_LDADD = libpng16.la
-pngfix_SOURCES = contrib/tools/pngfix.c
-pngfix_LDADD = libpng16.la
-png_fix_itxt_SOURCES = contrib/tools/png-fix-itxt.c
-pngcp_SOURCES = contrib/tools/pngcp.c
-pngcp_LDADD = libpng16.la
-
-# Generally these are single line shell scripts to run a test with a particular
-# set of parameters:
-TESTS = \
-   tests/pngtest\
-   tests/pngtest-badpngs\
-   tests/pngvalid-gamma-16-to-8 tests/pngvalid-gamma-alpha-mode\
-   tests/pngvalid-gamma-background tests/pngvalid-gamma-expand16-alpha-mode\
-   tests/pngvalid-gamma-expand16-background\
-   tests/pngvalid-gamma-expand16-transform tests/pngvalid-gamma-sbit\
-   tests/pngvalid-gamma-threshold tests/pngvalid-gamma-transform\
-   tests/pngvalid-progressive-size\
-   tests/pngvalid-progressive-interlace-standard\
-   tests/pngvalid-transform\
-   tests/pngvalid-progressive-standard tests/pngvalid-standard\
-   tests/pngstest-1.8 tests/pngstest-1.8-alpha tests/pngstest-linear\
-   tests/pngstest-linear-alpha tests/pngstest-none tests/pngstest-none-alpha\
-   tests/pngstest-sRGB tests/pngstest-sRGB-alpha tests/pngunknown-IDAT\
-   tests/pngunknown-discard tests/pngunknown-if-safe tests/pngunknown-sAPI\
-   tests/pngunknown-sTER tests/pngunknown-save tests/pngunknown-vpAg\
-   tests/pngimage-quick tests/pngimage-full
-
-
-# man pages
-dist_man_MANS = libpng.3 libpngpf.3 png.5
-EXTRA_SCRIPTS = libpng-config libpng16-config
-bin_SCRIPTS = ${binconfigs}
-
-# rules to build libpng, only build the old library on request
-lib_LTLIBRARIES = libpng16.la
-# EXTRA_LTLIBRARIES= libpng.la
-libpng16_la_SOURCES = png.c pngerror.c \
-	pngget.c pngmem.c pngpread.c pngread.c pngrio.c pngrtran.c \
-	pngrutil.c pngset.c pngtrans.c pngwio.c pngwrite.c pngwtran.c \
-	pngwutil.c png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h \
-	pngstruct.h pngusr.dfa $(am__append_2) $(am__append_3) \
-	$(am__append_4) $(am__append_5)
-nodist_libpng16_la_SOURCES = pnglibconf.h
-libpng16_la_LDFLAGS = -no-undefined \
-	-export-dynamic -version-number \
-	16:37:0 \
-	$(am__append_6) $(am__append_7) $(am__append_8)
-libpng16_la_DEPENDENCIES = libpng.sym
-#libpng16_la_DEPENDENCIES = libpng.vers
-pkginclude_HEADERS = png.h pngconf.h
-nodist_pkginclude_HEADERS = pnglibconf.h
-pkgconfig_DATA = libpng16.pc
-
-# Extra source distribution files, '${srcdir}' is used below to stop build files
-# from those directories being included.  This only works if the configure is
-# not done in the source directory!
-EXTRA_DIST = \
-	ANNOUNCE AUTHORS CHANGES INSTALL LICENSE README TODO TRADEMARK \
-	pngtest.png pngbar.png pngnow.png pngbar.jpg autogen.sh \
-	${srcdir}/contrib ${srcdir}/projects ${srcdir}/scripts \
-	$(TESTS) $(XFAIL_TESTS) tests/pngstest \
-	CMakeLists.txt example.c libpng-manual.txt
-
-SCRIPT_CLEANFILES = scripts/*.out scripts/*.chk
-CLEANFILES = *.tf? pngout.png libpng16.pc \
-	libpng16-config libpng.vers libpng.sym \
-	check.new pnglibconf.h pngprefix.h symbols.new pngtest-log.txt \
-	pnglibconf.out pnglibconf.c pnglibconf.pre pnglibconf.dfn \
-	$(SCRIPT_CLEANFILES)
-
-MAINTAINERCLEANFILES = Makefile.in aclocal.m4 config.guess config.h.in \
-config.sub configure depcomp install-sh ltmain.sh missing
-
-AM_CFLAGS = ${PNG_COPTS}
-SUFFIXES = .chk .out
-
-# We must use -DPNG_NO_USE_READ_MACROS here even when the library may actually
-# be built with PNG_USE_READ_MACROS; this prevents the read macros from
-# interfering with the symbol file format.
-SYMBOL_CFLAGS = -DPNGLIB_LIBNAME='PNG16_0' \
-	-DPNGLIB_VERSION='1.6.37' \
-	-DSYMBOL_PREFIX='$(SYMBOL_PREFIX)' -DPNG_NO_USE_READ_MACROS \
-	-DPNG_BUILDING_SYMBOL_TABLE $(am__append_9)
-
-# EXT_LIST is a list of the possibly library directory extensions, this exists
-# because we can't find a good way of discovering the file extensions that are
-# actually installed on a given system, so instead we check for every extension
-# we have seen.
-EXT_LIST = a dll.a so so.16.37 la sl dylib
-all: $(BUILT_SOURCES) config.h
-	$(MAKE) $(AM_MAKEFLAGS) all-am
-
-.SUFFIXES:
-.SUFFIXES: .chk .out .S .c .lo .log .o .obj .test .test$(EXEEXT) .trs
-am--refresh: Makefile
-	@:
-$(srcdir)/Makefile.in: # $(srcdir)/Makefile.am  $(am__configure_deps)
-	@for dep in $?; do \
-	  case '$(am__configure_deps)' in \
-	    *$$dep*) \
-	      echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
-	      $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
-		&& exit 0; \
-	      exit 1;; \
-	  esac; \
-	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
-	$(am__cd) $(top_srcdir) && \
-	  $(AUTOMAKE) --foreign Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
-	@case '$?' in \
-	  *config.status*) \
-	    echo ' $(SHELL) ./config.status'; \
-	    $(SHELL) ./config.status;; \
-	  *) \
-	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
-	    cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
-	esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
-	$(SHELL) ./config.status --recheck
-
-$(top_srcdir)/configure: # $(am__configure_deps)
-	$(am__cd) $(srcdir) && $(AUTOCONF)
-$(ACLOCAL_M4): # $(am__aclocal_m4_deps)
-	$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
-$(am__aclocal_m4_deps):
-
-config.h: stamp-h1
-	@test -f $@ || rm -f stamp-h1
-	@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
-
-stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
-	@rm -f stamp-h1
-	cd $(top_builddir) && $(SHELL) ./config.status config.h
-$(srcdir)/config.h.in: # $(am__configure_deps) 
-	($(am__cd) $(top_srcdir) && $(AUTOHEADER))
-	rm -f stamp-h1
-	touch $@
-
-distclean-hdr:
-	-rm -f config.h stamp-h1
-libpng.pc: $(top_builddir)/config.status $(srcdir)/libpng.pc.in
-	cd $(top_builddir) && $(SHELL) ./config.status $@
-libpng-config: $(top_builddir)/config.status $(srcdir)/libpng-config.in
-	cd $(top_builddir) && $(SHELL) ./config.status $@
-install-binPROGRAMS: $(bin_PROGRAMS)
-	@$(NORMAL_INSTALL)
-	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
-	if test -n "$$list"; then \
-	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
-	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
-	fi; \
-	for p in $$list; do echo "$$p $$p"; done | \
-	sed 's/$(EXEEXT)$$//' | \
-	while read p p1; do if test -f $$p \
-	 || test -f $$p1 \
-	  ; then echo "$$p"; echo "$$p"; else :; fi; \
-	done | \
-	sed -e 'p;s,.*/,,;n;h' \
-	    -e 's|.*|.|' \
-	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
-	sed 'N;N;N;s,\n, ,g' | \
-	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
-	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
-	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
-	    else { print "f", $$3 "/" $$4, $$1; } } \
-	  END { for (d in files) print "f", d, files[d] }' | \
-	while read type dir files; do \
-	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
-	    test -z "$$files" || { \
-	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
-	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
-	    } \
-	; done
-
-uninstall-binPROGRAMS:
-	@$(NORMAL_UNINSTALL)
-	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
-	files=`for p in $$list; do echo "$$p"; done | \
-	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
-	      -e 's/$$/$(EXEEXT)/' \
-	`; \
-	test -n "$$list" || exit 0; \
-	echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
-	cd "$(DESTDIR)$(bindir)" && rm -f $$files
-
-clean-binPROGRAMS:
-	@list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
-	echo " rm -f" $$list; \
-	rm -f $$list || exit $$?; \
-	test -n "$(EXEEXT)" || exit 0; \
-	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
-	echo " rm -f" $$list; \
-	rm -f $$list
-
-clean-checkPROGRAMS:
-	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
-	echo " rm -f" $$list; \
-	rm -f $$list || exit $$?; \
-	test -n "$(EXEEXT)" || exit 0; \
-	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
-	echo " rm -f" $$list; \
-	rm -f $$list
-
-install-libLTLIBRARIES: $(lib_LTLIBRARIES)
-	@$(NORMAL_INSTALL)
-	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
-	list2=; for p in $$list; do \
-	  if test -f $$p; then \
-	    list2="$$list2 $$p"; \
-	  else :; fi; \
-	done; \
-	test -z "$$list2" || { \
-	  echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
-	  $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
-	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
-	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
-	}
-
-uninstall-libLTLIBRARIES:
-	@$(NORMAL_UNINSTALL)
-	@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
-	for p in $$list; do \
-	  $(am__strip_dir) \
-	  echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
-	  $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
-	done
-
-clean-libLTLIBRARIES:
-	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
-	@list='$(lib_LTLIBRARIES)'; \
-	locs=`for p in $$list; do echo $$p; done | \
-	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
-	      sort -u`; \
-	test -z "$$locs" || { \
-	  echo rm -f $${locs}; \
-	  rm -f $${locs}; \
-	}
-arm/$(am__dirstamp):
-	@$(MKDIR_P) arm
-	@: > arm/$(am__dirstamp)
-arm/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) arm/$(DEPDIR)
-	@: > arm/$(DEPDIR)/$(am__dirstamp)
-arm/arm_init.lo: arm/$(am__dirstamp) arm/$(DEPDIR)/$(am__dirstamp)
-arm/filter_neon.lo: arm/$(am__dirstamp) arm/$(DEPDIR)/$(am__dirstamp)
-arm/filter_neon_intrinsics.lo: arm/$(am__dirstamp) \
-	arm/$(DEPDIR)/$(am__dirstamp)
-arm/palette_neon_intrinsics.lo: arm/$(am__dirstamp) \
-	arm/$(DEPDIR)/$(am__dirstamp)
-mips/$(am__dirstamp):
-	@$(MKDIR_P) mips
-	@: > mips/$(am__dirstamp)
-mips/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) mips/$(DEPDIR)
-	@: > mips/$(DEPDIR)/$(am__dirstamp)
-mips/mips_init.lo: mips/$(am__dirstamp) mips/$(DEPDIR)/$(am__dirstamp)
-mips/filter_msa_intrinsics.lo: mips/$(am__dirstamp) \
-	mips/$(DEPDIR)/$(am__dirstamp)
-intel/$(am__dirstamp):
-	@$(MKDIR_P) intel
-	@: > intel/$(am__dirstamp)
-intel/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) intel/$(DEPDIR)
-	@: > intel/$(DEPDIR)/$(am__dirstamp)
-intel/intel_init.lo: intel/$(am__dirstamp) \
-	intel/$(DEPDIR)/$(am__dirstamp)
-intel/filter_sse2_intrinsics.lo: intel/$(am__dirstamp) \
-	intel/$(DEPDIR)/$(am__dirstamp)
-powerpc/$(am__dirstamp):
-	@$(MKDIR_P) powerpc
-	@: > powerpc/$(am__dirstamp)
-powerpc/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) powerpc/$(DEPDIR)
-	@: > powerpc/$(DEPDIR)/$(am__dirstamp)
-powerpc/powerpc_init.lo: powerpc/$(am__dirstamp) \
-	powerpc/$(DEPDIR)/$(am__dirstamp)
-powerpc/filter_vsx_intrinsics.lo: powerpc/$(am__dirstamp) \
-	powerpc/$(DEPDIR)/$(am__dirstamp)
-
-libpng16.la: $(libpng16_la_OBJECTS) $(libpng16_la_DEPENDENCIES) $(EXTRA_libpng16_la_DEPENDENCIES) 
-	$(AM_V_CCLD)$(libpng16_la_LINK) -rpath $(libdir) $(libpng16_la_OBJECTS) $(libpng16_la_LIBADD) $(LIBS)
-contrib/tools/$(am__dirstamp):
-	@$(MKDIR_P) contrib/tools
-	@: > contrib/tools/$(am__dirstamp)
-contrib/tools/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) contrib/tools/$(DEPDIR)
-	@: > contrib/tools/$(DEPDIR)/$(am__dirstamp)
-contrib/tools/png-fix-itxt.$(OBJEXT): contrib/tools/$(am__dirstamp) \
-	contrib/tools/$(DEPDIR)/$(am__dirstamp)
-
-png-fix-itxt$(EXEEXT): $(png_fix_itxt_OBJECTS) $(png_fix_itxt_DEPENDENCIES) $(EXTRA_png_fix_itxt_DEPENDENCIES) 
-	@rm -f png-fix-itxt$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(png_fix_itxt_OBJECTS) $(png_fix_itxt_LDADD) $(LIBS)
-contrib/tools/pngcp.$(OBJEXT): contrib/tools/$(am__dirstamp) \
-	contrib/tools/$(DEPDIR)/$(am__dirstamp)
-
-pngcp$(EXEEXT): $(pngcp_OBJECTS) $(pngcp_DEPENDENCIES) $(EXTRA_pngcp_DEPENDENCIES) 
-	@rm -f pngcp$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(pngcp_OBJECTS) $(pngcp_LDADD) $(LIBS)
-contrib/tools/pngfix.$(OBJEXT): contrib/tools/$(am__dirstamp) \
-	contrib/tools/$(DEPDIR)/$(am__dirstamp)
-
-pngfix$(EXEEXT): $(pngfix_OBJECTS) $(pngfix_DEPENDENCIES) $(EXTRA_pngfix_DEPENDENCIES) 
-	@rm -f pngfix$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(pngfix_OBJECTS) $(pngfix_LDADD) $(LIBS)
-contrib/libtests/$(am__dirstamp):
-	@$(MKDIR_P) contrib/libtests
-	@: > contrib/libtests/$(am__dirstamp)
-contrib/libtests/$(DEPDIR)/$(am__dirstamp):
-	@$(MKDIR_P) contrib/libtests/$(DEPDIR)
-	@: > contrib/libtests/$(DEPDIR)/$(am__dirstamp)
-contrib/libtests/pngimage.$(OBJEXT): contrib/libtests/$(am__dirstamp) \
-	contrib/libtests/$(DEPDIR)/$(am__dirstamp)
-
-pngimage$(EXEEXT): $(pngimage_OBJECTS) $(pngimage_DEPENDENCIES) $(EXTRA_pngimage_DEPENDENCIES) 
-	@rm -f pngimage$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(pngimage_OBJECTS) $(pngimage_LDADD) $(LIBS)
-contrib/libtests/pngstest.$(OBJEXT): contrib/libtests/$(am__dirstamp) \
-	contrib/libtests/$(DEPDIR)/$(am__dirstamp)
-
-pngstest$(EXEEXT): $(pngstest_OBJECTS) $(pngstest_DEPENDENCIES) $(EXTRA_pngstest_DEPENDENCIES) 
-	@rm -f pngstest$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(pngstest_OBJECTS) $(pngstest_LDADD) $(LIBS)
-
-pngtest$(EXEEXT): $(pngtest_OBJECTS) $(pngtest_DEPENDENCIES) $(EXTRA_pngtest_DEPENDENCIES) 
-	@rm -f pngtest$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(pngtest_OBJECTS) $(pngtest_LDADD) $(LIBS)
-contrib/libtests/pngunknown.$(OBJEXT):  \
-	contrib/libtests/$(am__dirstamp) \
-	contrib/libtests/$(DEPDIR)/$(am__dirstamp)
-
-pngunknown$(EXEEXT): $(pngunknown_OBJECTS) $(pngunknown_DEPENDENCIES) $(EXTRA_pngunknown_DEPENDENCIES) 
-	@rm -f pngunknown$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(pngunknown_OBJECTS) $(pngunknown_LDADD) $(LIBS)
-contrib/libtests/pngvalid.$(OBJEXT): contrib/libtests/$(am__dirstamp) \
-	contrib/libtests/$(DEPDIR)/$(am__dirstamp)
-
-pngvalid$(EXEEXT): $(pngvalid_OBJECTS) $(pngvalid_DEPENDENCIES) $(EXTRA_pngvalid_DEPENDENCIES) 
-	@rm -f pngvalid$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(pngvalid_OBJECTS) $(pngvalid_LDADD) $(LIBS)
-contrib/libtests/timepng.$(OBJEXT): contrib/libtests/$(am__dirstamp) \
-	contrib/libtests/$(DEPDIR)/$(am__dirstamp)
-
-timepng$(EXEEXT): $(timepng_OBJECTS) $(timepng_DEPENDENCIES) $(EXTRA_timepng_DEPENDENCIES) 
-	@rm -f timepng$(EXEEXT)
-	$(AM_V_CCLD)$(LINK) $(timepng_OBJECTS) $(timepng_LDADD) $(LIBS)
-install-binSCRIPTS: $(bin_SCRIPTS)
-	@$(NORMAL_INSTALL)
-	@list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \
-	if test -n "$$list"; then \
-	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
-	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
-	fi; \
-	for p in $$list; do \
-	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-	  if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
-	done | \
-	sed -e 'p;s,.*/,,;n' \
-	    -e 'h;s|.*|.|' \
-	    -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
-	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
-	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
-	    if ($$2 == $$4) { files[d] = files[d] " " $$1; \
-	      if (++n[d] == $(am__install_max)) { \
-		print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
-	    else { print "f", d "/" $$4, $$1 } } \
-	  END { for (d in files) print "f", d, files[d] }' | \
-	while read type dir files; do \
-	     if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
-	     test -z "$$files" || { \
-	       echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \
-	       $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
-	     } \
-	; done
-
-uninstall-binSCRIPTS:
-	@$(NORMAL_UNINSTALL)
-	@list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \
-	files=`for p in $$list; do echo "$$p"; done | \
-	       sed -e 's,.*/,,;$(transform)'`; \
-	dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir)
-
-mostlyclean-compile:
-	-rm -f *.$(OBJEXT)
-	-rm -f arm/*.$(OBJEXT)
-	-rm -f arm/*.lo
-	-rm -f contrib/libtests/*.$(OBJEXT)
-	-rm -f contrib/tools/*.$(OBJEXT)
-	-rm -f intel/*.$(OBJEXT)
-	-rm -f intel/*.lo
-	-rm -f mips/*.$(OBJEXT)
-	-rm -f mips/*.lo
-	-rm -f powerpc/*.$(OBJEXT)
-	-rm -f powerpc/*.lo
-
-distclean-compile:
-	-rm -f *.tab.c
-
-include ./$(DEPDIR)/png.Plo # am--include-marker
-include ./$(DEPDIR)/pngerror.Plo # am--include-marker
-include ./$(DEPDIR)/pngget.Plo # am--include-marker
-include ./$(DEPDIR)/pngmem.Plo # am--include-marker
-include ./$(DEPDIR)/pngpread.Plo # am--include-marker
-include ./$(DEPDIR)/pngread.Plo # am--include-marker
-include ./$(DEPDIR)/pngrio.Plo # am--include-marker
-include ./$(DEPDIR)/pngrtran.Plo # am--include-marker
-include ./$(DEPDIR)/pngrutil.Plo # am--include-marker
-include ./$(DEPDIR)/pngset.Plo # am--include-marker
-include ./$(DEPDIR)/pngtest.Po # am--include-marker
-include ./$(DEPDIR)/pngtrans.Plo # am--include-marker
-include ./$(DEPDIR)/pngwio.Plo # am--include-marker
-include ./$(DEPDIR)/pngwrite.Plo # am--include-marker
-include ./$(DEPDIR)/pngwtran.Plo # am--include-marker
-include ./$(DEPDIR)/pngwutil.Plo # am--include-marker
-include arm/$(DEPDIR)/arm_init.Plo # am--include-marker
-include arm/$(DEPDIR)/filter_neon.Plo # am--include-marker
-include arm/$(DEPDIR)/filter_neon_intrinsics.Plo # am--include-marker
-include arm/$(DEPDIR)/palette_neon_intrinsics.Plo # am--include-marker
-include contrib/libtests/$(DEPDIR)/pngimage.Po # am--include-marker
-include contrib/libtests/$(DEPDIR)/pngstest.Po # am--include-marker
-include contrib/libtests/$(DEPDIR)/pngunknown.Po # am--include-marker
-include contrib/libtests/$(DEPDIR)/pngvalid.Po # am--include-marker
-include contrib/libtests/$(DEPDIR)/timepng.Po # am--include-marker
-include contrib/tools/$(DEPDIR)/png-fix-itxt.Po # am--include-marker
-include contrib/tools/$(DEPDIR)/pngcp.Po # am--include-marker
-include contrib/tools/$(DEPDIR)/pngfix.Po # am--include-marker
-include intel/$(DEPDIR)/filter_sse2_intrinsics.Plo # am--include-marker
-include intel/$(DEPDIR)/intel_init.Plo # am--include-marker
-include mips/$(DEPDIR)/filter_msa_intrinsics.Plo # am--include-marker
-include mips/$(DEPDIR)/mips_init.Plo # am--include-marker
-include powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo # am--include-marker
-include powerpc/$(DEPDIR)/powerpc_init.Plo # am--include-marker
-
-$(am__depfiles_remade):
-	@$(MKDIR_P) $(@D)
-	@echo '# dummy' >$@-t && $(am__mv) $@-t $@
-
-am--depfiles: $(am__depfiles_remade)
-
-.S.o:
-	$(AM_V_CPPAS)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
-	$(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-	$(am__mv) $$depbase.Tpo $$depbase.Po
-#	$(AM_V_CPPAS)source='$<' object='$@' libtool=no \
-#	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) \
-#	$(AM_V_CPPAS_no)$(CPPASCOMPILE) -c -o $@ $<
-
-.S.obj:
-	$(AM_V_CPPAS)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
-	$(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
-	$(am__mv) $$depbase.Tpo $$depbase.Po
-#	$(AM_V_CPPAS)source='$<' object='$@' libtool=no \
-#	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) \
-#	$(AM_V_CPPAS_no)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-
-.S.lo:
-	$(AM_V_CPPAS)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
-	$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-	$(am__mv) $$depbase.Tpo $$depbase.Plo
-#	$(AM_V_CPPAS)source='$<' object='$@' libtool=yes \
-#	DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) \
-#	$(AM_V_CPPAS_no)$(LTCPPASCOMPILE) -c -o $@ $<
-
-.c.o:
-	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
-	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-	$(am__mv) $$depbase.Tpo $$depbase.Po
-#	$(AM_V_CC)source='$<' object='$@' libtool=no \
-#	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
-#	$(AM_V_CC_no)$(COMPILE) -c -o $@ $<
-
-.c.obj:
-	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
-	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
-	$(am__mv) $$depbase.Tpo $$depbase.Po
-#	$(AM_V_CC)source='$<' object='$@' libtool=no \
-#	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
-#	$(AM_V_CC_no)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-
-.c.lo:
-	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
-	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-	$(am__mv) $$depbase.Tpo $$depbase.Plo
-#	$(AM_V_CC)source='$<' object='$@' libtool=yes \
-#	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
-#	$(AM_V_CC_no)$(LTCOMPILE) -c -o $@ $<
-
-mostlyclean-libtool:
-	-rm -f *.lo
-
-clean-libtool:
-	-rm -rf .libs _libs
-	-rm -rf arm/.libs arm/_libs
-	-rm -rf intel/.libs intel/_libs
-	-rm -rf mips/.libs mips/_libs
-	-rm -rf powerpc/.libs powerpc/_libs
-
-distclean-libtool:
-	-rm -f libtool config.lt
-install-man3: $(dist_man_MANS)
-	@$(NORMAL_INSTALL)
-	@list1=''; \
-	list2='$(dist_man_MANS)'; \
-	test -n "$(man3dir)" \
-	  && test -n "`echo $$list1$$list2`" \
-	  || exit 0; \
-	echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
-	$(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
-	{ for i in $$list1; do echo "$$i"; done;  \
-	if test -n "$$list2"; then \
-	  for i in $$list2; do echo "$$i"; done \
-	    | sed -n '/\.3[a-z]*$$/p'; \
-	fi; \
-	} | while read p; do \
-	  if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
-	  echo "$$d$$p"; echo "$$p"; \
-	done | \
-	sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
-	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
-	sed 'N;N;s,\n, ,g' | { \
-	list=; while read file base inst; do \
-	  if test "$$base" = "$$inst"; then list="$$list $$file"; else \
-	    echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \
-	    $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst" || exit $$?; \
-	  fi; \
-	done; \
-	for i in $$list; do echo "$$i"; done | $(am__base_list) | \
-	while read files; do \
-	  test -z "$$files" || { \
-	    echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man3dir)'"; \
-	    $(INSTALL_DATA) $$files "$(DESTDIR)$(man3dir)" || exit $$?; }; \
-	done; }
-
-uninstall-man3:
-	@$(NORMAL_UNINSTALL)
-	@list=''; test -n "$(man3dir)" || exit 0; \
-	files=`{ for i in $$list; do echo "$$i"; done; \
-	l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
-	  sed -n '/\.3[a-z]*$$/p'; \
-	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
-	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
-	dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
-install-man5: $(dist_man_MANS)
-	@$(NORMAL_INSTALL)
-	@list1=''; \
-	list2='$(dist_man_MANS)'; \
-	test -n "$(man5dir)" \
-	  && test -n "`echo $$list1$$list2`" \
-	  || exit 0; \
-	echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
-	$(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
-	{ for i in $$list1; do echo "$$i"; done;  \
-	if test -n "$$list2"; then \
-	  for i in $$list2; do echo "$$i"; done \
-	    | sed -n '/\.5[a-z]*$$/p'; \
-	fi; \
-	} | while read p; do \
-	  if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
-	  echo "$$d$$p"; echo "$$p"; \
-	done | \
-	sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
-	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
-	sed 'N;N;s,\n, ,g' | { \
-	list=; while read file base inst; do \
-	  if test "$$base" = "$$inst"; then list="$$list $$file"; else \
-	    echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
-	    $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \
-	  fi; \
-	done; \
-	for i in $$list; do echo "$$i"; done | $(am__base_list) | \
-	while read files; do \
-	  test -z "$$files" || { \
-	    echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \
-	    $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
-	done; }
-
-uninstall-man5:
-	@$(NORMAL_UNINSTALL)
-	@list=''; test -n "$(man5dir)" || exit 0; \
-	files=`{ for i in $$list; do echo "$$i"; done; \
-	l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
-	  sed -n '/\.5[a-z]*$$/p'; \
-	} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
-	      -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
-	dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
-install-pkgconfigDATA: $(pkgconfig_DATA)
-	@$(NORMAL_INSTALL)
-	@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
-	if test -n "$$list"; then \
-	  echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
-	  $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
-	fi; \
-	for p in $$list; do \
-	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-	  echo "$$d$$p"; \
-	done | $(am__base_list) | \
-	while read files; do \
-	  echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
-	  $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
-	done
-
-uninstall-pkgconfigDATA:
-	@$(NORMAL_UNINSTALL)
-	@list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
-	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
-install-nodist_pkgincludeHEADERS: $(nodist_pkginclude_HEADERS)
-	@$(NORMAL_INSTALL)
-	@list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
-	if test -n "$$list"; then \
-	  echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \
-	  $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \
-	fi; \
-	for p in $$list; do \
-	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-	  echo "$$d$$p"; \
-	done | $(am__base_list) | \
-	while read files; do \
-	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \
-	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \
-	done
-
-uninstall-nodist_pkgincludeHEADERS:
-	@$(NORMAL_UNINSTALL)
-	@list='$(nodist_pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
-	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir)
-install-pkgincludeHEADERS: $(pkginclude_HEADERS)
-	@$(NORMAL_INSTALL)
-	@list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
-	if test -n "$$list"; then \
-	  echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \
-	  $(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \
-	fi; \
-	for p in $$list; do \
-	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-	  echo "$$d$$p"; \
-	done | $(am__base_list) | \
-	while read files; do \
-	  echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \
-	  $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \
-	done
-
-uninstall-pkgincludeHEADERS:
-	@$(NORMAL_UNINSTALL)
-	@list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
-	files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
-	dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir)
-
-ID: $(am__tagged_files)
-	$(am__define_uniq_tagged_files); mkid -fID $$unique
-tags: tags-am
-TAGS: tags
-
-tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
-	set x; \
-	here=`pwd`; \
-	$(am__define_uniq_tagged_files); \
-	shift; \
-	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
-	  test -n "$$unique" || unique=$$empty_fix; \
-	  if test $$# -gt 0; then \
-	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-	      "$$@" $$unique; \
-	  else \
-	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-	      $$unique; \
-	  fi; \
-	fi
-ctags: ctags-am
-
-CTAGS: ctags
-ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
-	$(am__define_uniq_tagged_files); \
-	test -z "$(CTAGS_ARGS)$$unique" \
-	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
-	     $$unique
-
-GTAGS:
-	here=`$(am__cd) $(top_builddir) && pwd` \
-	  && $(am__cd) $(top_srcdir) \
-	  && gtags -i $(GTAGS_ARGS) "$$here"
-cscope: cscope.files
-	test ! -s cscope.files \
-	  || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
-clean-cscope:
-	-rm -f cscope.files
-cscope.files: clean-cscope cscopelist
-cscopelist: cscopelist-am
-
-cscopelist-am: $(am__tagged_files)
-	list='$(am__tagged_files)'; \
-	case "$(srcdir)" in \
-	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
-	  *) sdir=$(subdir)/$(srcdir) ;; \
-	esac; \
-	for i in $$list; do \
-	  if test -f "$$i"; then \
-	    echo "$(subdir)/$$i"; \
-	  else \
-	    echo "$$sdir/$$i"; \
-	  fi; \
-	done >> $(top_builddir)/cscope.files
-
-distclean-tags:
-	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
-
-# Recover from deleted '.trs' file; this should ensure that
-# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
-# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
-# to avoid problems with "make -n".
-.log.trs:
-	rm -f $< $@
-	$(MAKE) $(AM_MAKEFLAGS) $<
-
-# Leading 'am--fnord' is there to ensure the list of targets does not
-# expand to empty, as could happen e.g. with make check TESTS=''.
-am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
-am--force-recheck:
-	@:
-
-$(TEST_SUITE_LOG): $(TEST_LOGS)
-	@$(am__set_TESTS_bases); \
-	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
-	redo_bases=`for i in $$bases; do \
-	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
-	            done`; \
-	if test -n "$$redo_bases"; then \
-	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
-	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
-	  if $(am__make_dryrun); then :; else \
-	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
-	  fi; \
-	fi; \
-	if test -n "$$am__remaking_logs"; then \
-	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
-	       "recursion detected" >&2; \
-	elif test -n "$$redo_logs"; then \
-	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
-	fi; \
-	if $(am__make_dryrun); then :; else \
-	  st=0;  \
-	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
-	  for i in $$redo_bases; do \
-	    test -f $$i.trs && test -r $$i.trs \
-	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
-	    test -f $$i.log && test -r $$i.log \
-	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
-	  done; \
-	  test $$st -eq 0 || exit 1; \
-	fi
-	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
-	ws='[ 	]'; \
-	results=`for b in $$bases; do echo $$b.trs; done`; \
-	test -n "$$results" || results=/dev/null; \
-	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
-	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
-	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
-	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
-	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
-	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
-	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
-	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
-	  success=true; \
-	else \
-	  success=false; \
-	fi; \
-	br='==================='; br=$$br$$br$$br$$br; \
-	result_count () \
-	{ \
-	    if test x"$$1" = x"--maybe-color"; then \
-	      maybe_colorize=yes; \
-	    elif test x"$$1" = x"--no-color"; then \
-	      maybe_colorize=no; \
-	    else \
-	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
-	    fi; \
-	    shift; \
-	    desc=$$1 count=$$2; \
-	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
-	      color_start=$$3 color_end=$$std; \
-	    else \
-	      color_start= color_end=; \
-	    fi; \
-	    echo "$${color_start}# $$desc $$count$${color_end}"; \
-	}; \
-	create_testsuite_report () \
-	{ \
-	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
-	  result_count $$1 "PASS: " $$pass  "$$grn"; \
-	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
-	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
-	  result_count $$1 "FAIL: " $$fail  "$$red"; \
-	  result_count $$1 "XPASS:" $$xpass "$$red"; \
-	  result_count $$1 "ERROR:" $$error "$$mgn"; \
-	}; \
-	{								\
-	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
-	    $(am__rst_title);						\
-	  create_testsuite_report --no-color;				\
-	  echo;								\
-	  echo ".. contents:: :depth: 2";				\
-	  echo;								\
-	  for b in $$bases; do echo $$b; done				\
-	    | $(am__create_global_log);					\
-	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
-	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
-	if $$success; then						\
-	  col="$$grn";							\
-	 else								\
-	  col="$$red";							\
-	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
-	fi;								\
-	echo "$${col}$$br$${std}"; 					\
-	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
-	echo "$${col}$$br$${std}"; 					\
-	create_testsuite_report --maybe-color;				\
-	echo "$$col$$br$$std";						\
-	if $$success; then :; else					\
-	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
-	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
-	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
-	  fi;								\
-	  echo "$$col$$br$$std";					\
-	fi;								\
-	$$success || exit 1
-
-check-TESTS: $(check_PROGRAMS)
-	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
-	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
-	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
-	@set +e; $(am__set_TESTS_bases); \
-	log_list=`for i in $$bases; do echo $$i.log; done`; \
-	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
-	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
-	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
-	exit $$?;
-recheck: all $(check_PROGRAMS)
-	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
-	@set +e; $(am__set_TESTS_bases); \
-	bases=`for i in $$bases; do echo $$i; done \
-	         | $(am__list_recheck_tests)` || exit 1; \
-	log_list=`for i in $$bases; do echo $$i.log; done`; \
-	log_list=`echo $$log_list`; \
-	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
-	        am__force_recheck=am--force-recheck \
-	        TEST_LOGS="$$log_list"; \
-	exit $$?
-tests/pngtest.log: tests/pngtest
-	@p='tests/pngtest'; \
-	b='tests/pngtest'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngtest-badpngs.log: tests/pngtest-badpngs
-	@p='tests/pngtest-badpngs'; \
-	b='tests/pngtest-badpngs'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-16-to-8.log: tests/pngvalid-gamma-16-to-8
-	@p='tests/pngvalid-gamma-16-to-8'; \
-	b='tests/pngvalid-gamma-16-to-8'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-alpha-mode.log: tests/pngvalid-gamma-alpha-mode
-	@p='tests/pngvalid-gamma-alpha-mode'; \
-	b='tests/pngvalid-gamma-alpha-mode'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-background.log: tests/pngvalid-gamma-background
-	@p='tests/pngvalid-gamma-background'; \
-	b='tests/pngvalid-gamma-background'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-expand16-alpha-mode.log: tests/pngvalid-gamma-expand16-alpha-mode
-	@p='tests/pngvalid-gamma-expand16-alpha-mode'; \
-	b='tests/pngvalid-gamma-expand16-alpha-mode'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-expand16-background.log: tests/pngvalid-gamma-expand16-background
-	@p='tests/pngvalid-gamma-expand16-background'; \
-	b='tests/pngvalid-gamma-expand16-background'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-expand16-transform.log: tests/pngvalid-gamma-expand16-transform
-	@p='tests/pngvalid-gamma-expand16-transform'; \
-	b='tests/pngvalid-gamma-expand16-transform'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-sbit.log: tests/pngvalid-gamma-sbit
-	@p='tests/pngvalid-gamma-sbit'; \
-	b='tests/pngvalid-gamma-sbit'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-threshold.log: tests/pngvalid-gamma-threshold
-	@p='tests/pngvalid-gamma-threshold'; \
-	b='tests/pngvalid-gamma-threshold'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-gamma-transform.log: tests/pngvalid-gamma-transform
-	@p='tests/pngvalid-gamma-transform'; \
-	b='tests/pngvalid-gamma-transform'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-progressive-size.log: tests/pngvalid-progressive-size
-	@p='tests/pngvalid-progressive-size'; \
-	b='tests/pngvalid-progressive-size'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-progressive-interlace-standard.log: tests/pngvalid-progressive-interlace-standard
-	@p='tests/pngvalid-progressive-interlace-standard'; \
-	b='tests/pngvalid-progressive-interlace-standard'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-transform.log: tests/pngvalid-transform
-	@p='tests/pngvalid-transform'; \
-	b='tests/pngvalid-transform'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-progressive-standard.log: tests/pngvalid-progressive-standard
-	@p='tests/pngvalid-progressive-standard'; \
-	b='tests/pngvalid-progressive-standard'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngvalid-standard.log: tests/pngvalid-standard
-	@p='tests/pngvalid-standard'; \
-	b='tests/pngvalid-standard'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-1.8.log: tests/pngstest-1.8
-	@p='tests/pngstest-1.8'; \
-	b='tests/pngstest-1.8'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-1.8-alpha.log: tests/pngstest-1.8-alpha
-	@p='tests/pngstest-1.8-alpha'; \
-	b='tests/pngstest-1.8-alpha'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-linear.log: tests/pngstest-linear
-	@p='tests/pngstest-linear'; \
-	b='tests/pngstest-linear'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-linear-alpha.log: tests/pngstest-linear-alpha
-	@p='tests/pngstest-linear-alpha'; \
-	b='tests/pngstest-linear-alpha'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-none.log: tests/pngstest-none
-	@p='tests/pngstest-none'; \
-	b='tests/pngstest-none'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-none-alpha.log: tests/pngstest-none-alpha
-	@p='tests/pngstest-none-alpha'; \
-	b='tests/pngstest-none-alpha'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-sRGB.log: tests/pngstest-sRGB
-	@p='tests/pngstest-sRGB'; \
-	b='tests/pngstest-sRGB'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngstest-sRGB-alpha.log: tests/pngstest-sRGB-alpha
-	@p='tests/pngstest-sRGB-alpha'; \
-	b='tests/pngstest-sRGB-alpha'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngunknown-IDAT.log: tests/pngunknown-IDAT
-	@p='tests/pngunknown-IDAT'; \
-	b='tests/pngunknown-IDAT'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngunknown-discard.log: tests/pngunknown-discard
-	@p='tests/pngunknown-discard'; \
-	b='tests/pngunknown-discard'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngunknown-if-safe.log: tests/pngunknown-if-safe
-	@p='tests/pngunknown-if-safe'; \
-	b='tests/pngunknown-if-safe'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngunknown-sAPI.log: tests/pngunknown-sAPI
-	@p='tests/pngunknown-sAPI'; \
-	b='tests/pngunknown-sAPI'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngunknown-sTER.log: tests/pngunknown-sTER
-	@p='tests/pngunknown-sTER'; \
-	b='tests/pngunknown-sTER'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngunknown-save.log: tests/pngunknown-save
-	@p='tests/pngunknown-save'; \
-	b='tests/pngunknown-save'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngunknown-vpAg.log: tests/pngunknown-vpAg
-	@p='tests/pngunknown-vpAg'; \
-	b='tests/pngunknown-vpAg'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngimage-quick.log: tests/pngimage-quick
-	@p='tests/pngimage-quick'; \
-	b='tests/pngimage-quick'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-tests/pngimage-full.log: tests/pngimage-full
-	@p='tests/pngimage-full'; \
-	b='tests/pngimage-full'; \
-	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-.test.log:
-	@p='$<'; \
-	$(am__set_b); \
-	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-.test$(EXEEXT).log:
-	@p='$<'; \
-	$(am__set_b); \
-	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
-	--log-file $$b.log --trs-file $$b.trs \
-	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
-	"$$tst" $(AM_TESTS_FD_REDIRECT)
-
-distdir: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) distdir-am
-
-distdir-am: $(DISTFILES)
-	$(am__remove_distdir)
-	test -d "$(distdir)" || mkdir "$(distdir)"
-	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	list='$(DISTFILES)'; \
-	  dist_files=`for file in $$list; do echo $$file; done | \
-	  sed -e "s|^$$srcdirstrip/||;t" \
-	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
-	case $$dist_files in \
-	  */*) $(MKDIR_P) `echo "$$dist_files" | \
-			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
-			   sort -u` ;; \
-	esac; \
-	for file in $$dist_files; do \
-	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-	  if test -d $$d/$$file; then \
-	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
-	    if test -d "$(distdir)/$$file"; then \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
-	  else \
-	    test -f "$(distdir)/$$file" \
-	    || cp -p $$d/$$file "$(distdir)/$$file" \
-	    || exit 1; \
-	  fi; \
-	done
-	$(MAKE) $(AM_MAKEFLAGS) \
-	  top_distdir="$(top_distdir)" distdir="$(distdir)" \
-	  dist-hook
-	-test -n "$(am__skip_mode_fix)" \
-	|| find "$(distdir)" -type d ! -perm -755 \
-		-exec chmod u+rwx,go+rx {} \; -o \
-	  ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
-	  ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
-	  ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
-	|| chmod -R a+r "$(distdir)"
-dist-gzip: distdir
-	tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
-	$(am__post_remove_distdir)
-
-dist-bzip2: distdir
-	tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
-	$(am__post_remove_distdir)
-
-dist-lzip: distdir
-	tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
-	$(am__post_remove_distdir)
-dist-xz: distdir
-	tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
-	$(am__post_remove_distdir)
-
-dist-tarZ: distdir
-	@echo WARNING: "Support for distribution archives compressed with" \
-		       "legacy program 'compress' is deprecated." >&2
-	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
-	tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
-	$(am__post_remove_distdir)
-
-dist-shar: distdir
-	@echo WARNING: "Support for shar distribution archives is" \
-	               "deprecated." >&2
-	@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
-	shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
-	$(am__post_remove_distdir)
-
-dist-zip: distdir
-	-rm -f $(distdir).zip
-	zip -rq $(distdir).zip $(distdir)
-	$(am__post_remove_distdir)
-
-dist dist-all:
-	$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
-	$(am__post_remove_distdir)
-
-# This target untars the dist file and tries a VPATH configuration.  Then
-# it guarantees that the distribution is self-contained by making another
-# tarfile.
-distcheck: dist
-	case '$(DIST_ARCHIVES)' in \
-	*.tar.gz*) \
-	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
-	*.tar.bz2*) \
-	  bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
-	*.tar.lz*) \
-	  lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
-	*.tar.xz*) \
-	  xz -dc $(distdir).tar.xz | $(am__untar) ;;\
-	*.tar.Z*) \
-	  uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
-	*.shar.gz*) \
-	  eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
-	*.zip*) \
-	  unzip $(distdir).zip ;;\
-	esac
-	chmod -R a-w $(distdir)
-	chmod u+w $(distdir)
-	mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
-	chmod a-w $(distdir)
-	test -d $(distdir)/_build || exit 0; \
-	dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
-	  && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
-	  && am__cwd=`pwd` \
-	  && $(am__cd) $(distdir)/_build/sub \
-	  && ../../configure \
-	    $(AM_DISTCHECK_CONFIGURE_FLAGS) \
-	    $(DISTCHECK_CONFIGURE_FLAGS) \
-	    --srcdir=../.. --prefix="$$dc_install_base" \
-	  && $(MAKE) $(AM_MAKEFLAGS) \
-	  && $(MAKE) $(AM_MAKEFLAGS) dvi \
-	  && $(MAKE) $(AM_MAKEFLAGS) check \
-	  && $(MAKE) $(AM_MAKEFLAGS) install \
-	  && $(MAKE) $(AM_MAKEFLAGS) installcheck \
-	  && $(MAKE) $(AM_MAKEFLAGS) uninstall \
-	  && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
-	        distuninstallcheck \
-	  && chmod -R a-w "$$dc_install_base" \
-	  && ({ \
-	       (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
-	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
-	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
-	       && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
-	            distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
-	      } || { rm -rf "$$dc_destdir"; exit 1; }) \
-	  && rm -rf "$$dc_destdir" \
-	  && $(MAKE) $(AM_MAKEFLAGS) dist \
-	  && rm -rf $(DIST_ARCHIVES) \
-	  && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
-	  && cd "$$am__cwd" \
-	  || exit 1
-	$(am__post_remove_distdir)
-	@(echo "$(distdir) archives ready for distribution: "; \
-	  list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
-	  sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
-distuninstallcheck:
-	@test -n '$(distuninstallcheck_dir)' || { \
-	  echo 'ERROR: trying to run $@ with an empty' \
-	       '$$(distuninstallcheck_dir)' >&2; \
-	  exit 1; \
-	}; \
-	$(am__cd) '$(distuninstallcheck_dir)' || { \
-	  echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
-	  exit 1; \
-	}; \
-	test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
-	   || { echo "ERROR: files left after uninstall:" ; \
-	        if test -n "$(DESTDIR)"; then \
-	          echo "  (check DESTDIR support)"; \
-	        fi ; \
-	        $(distuninstallcheck_listfiles) ; \
-	        exit 1; } >&2
-distcleancheck: distclean
-	@if test '$(srcdir)' = . ; then \
-	  echo "ERROR: distcleancheck can only run from a VPATH build" ; \
-	  exit 1 ; \
-	fi
-	@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
-	  || { echo "ERROR: files left in build directory after distclean:" ; \
-	       $(distcleancheck_listfiles) ; \
-	       exit 1; } >&2
-check-am: all-am
-	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
-	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
-check: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) check-am
-all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(SCRIPTS) $(MANS) $(DATA) \
-		$(HEADERS) config.h
-install-binPROGRAMS: install-libLTLIBRARIES
-
-installdirs:
-	for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(pkgincludedir)" "$(DESTDIR)$(pkgincludedir)"; do \
-	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
-	done
-install: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
-	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
-	if test -z '$(STRIP)'; then \
-	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-	      install; \
-	else \
-	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
-	fi
-mostlyclean-generic:
-	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
-	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
-	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
-
-clean-generic:
-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
-
-distclean-generic:
-	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-	-rm -f arm/$(DEPDIR)/$(am__dirstamp)
-	-rm -f arm/$(am__dirstamp)
-	-rm -f contrib/libtests/$(DEPDIR)/$(am__dirstamp)
-	-rm -f contrib/libtests/$(am__dirstamp)
-	-rm -f contrib/tools/$(DEPDIR)/$(am__dirstamp)
-	-rm -f contrib/tools/$(am__dirstamp)
-	-rm -f intel/$(DEPDIR)/$(am__dirstamp)
-	-rm -f intel/$(am__dirstamp)
-	-rm -f mips/$(DEPDIR)/$(am__dirstamp)
-	-rm -f mips/$(am__dirstamp)
-	-rm -f powerpc/$(DEPDIR)/$(am__dirstamp)
-	-rm -f powerpc/$(am__dirstamp)
-
-maintainer-clean-generic:
-	@echo "This command is intended for maintainers to use"
-	@echo "it deletes files that may require special tools to rebuild."
-	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
-	-test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-##install-exec-hook:
-##install-data-hook:
-###uninstall-hook:
-clean: clean-am
-
-clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
-	clean-libLTLIBRARIES clean-libtool mostlyclean-am
-
-distclean: distclean-am
-	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-		-rm -f ./$(DEPDIR)/png.Plo
-	-rm -f ./$(DEPDIR)/pngerror.Plo
-	-rm -f ./$(DEPDIR)/pngget.Plo
-	-rm -f ./$(DEPDIR)/pngmem.Plo
-	-rm -f ./$(DEPDIR)/pngpread.Plo
-	-rm -f ./$(DEPDIR)/pngread.Plo
-	-rm -f ./$(DEPDIR)/pngrio.Plo
-	-rm -f ./$(DEPDIR)/pngrtran.Plo
-	-rm -f ./$(DEPDIR)/pngrutil.Plo
-	-rm -f ./$(DEPDIR)/pngset.Plo
-	-rm -f ./$(DEPDIR)/pngtest.Po
-	-rm -f ./$(DEPDIR)/pngtrans.Plo
-	-rm -f ./$(DEPDIR)/pngwio.Plo
-	-rm -f ./$(DEPDIR)/pngwrite.Plo
-	-rm -f ./$(DEPDIR)/pngwtran.Plo
-	-rm -f ./$(DEPDIR)/pngwutil.Plo
-	-rm -f arm/$(DEPDIR)/arm_init.Plo
-	-rm -f arm/$(DEPDIR)/filter_neon.Plo
-	-rm -f arm/$(DEPDIR)/filter_neon_intrinsics.Plo
-	-rm -f arm/$(DEPDIR)/palette_neon_intrinsics.Plo
-	-rm -f contrib/libtests/$(DEPDIR)/pngimage.Po
-	-rm -f contrib/libtests/$(DEPDIR)/pngstest.Po
-	-rm -f contrib/libtests/$(DEPDIR)/pngunknown.Po
-	-rm -f contrib/libtests/$(DEPDIR)/pngvalid.Po
-	-rm -f contrib/libtests/$(DEPDIR)/timepng.Po
-	-rm -f contrib/tools/$(DEPDIR)/png-fix-itxt.Po
-	-rm -f contrib/tools/$(DEPDIR)/pngcp.Po
-	-rm -f contrib/tools/$(DEPDIR)/pngfix.Po
-	-rm -f intel/$(DEPDIR)/filter_sse2_intrinsics.Plo
-	-rm -f intel/$(DEPDIR)/intel_init.Plo
-	-rm -f mips/$(DEPDIR)/filter_msa_intrinsics.Plo
-	-rm -f mips/$(DEPDIR)/mips_init.Plo
-	-rm -f powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo
-	-rm -f powerpc/$(DEPDIR)/powerpc_init.Plo
-	-rm -f Makefile
-distclean-am: clean-am distclean-compile distclean-generic \
-	distclean-hdr distclean-libtool distclean-tags
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-html-am:
-
-info: info-am
-
-info-am:
-
-install-data-am: install-man install-nodist_pkgincludeHEADERS \
-	install-pkgconfigDATA install-pkgincludeHEADERS
-	@$(NORMAL_INSTALL)
-	$(MAKE) $(AM_MAKEFLAGS) install-data-hook
-install-dvi: install-dvi-am
-
-install-dvi-am:
-
-install-exec-am: install-binPROGRAMS install-binSCRIPTS \
-	install-libLTLIBRARIES
-	@$(NORMAL_INSTALL)
-	$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
-install-html: install-html-am
-
-install-html-am:
-
-install-info: install-info-am
-
-install-info-am:
-
-install-man: install-man3 install-man5
-
-install-pdf: install-pdf-am
-
-install-pdf-am:
-
-install-ps: install-ps-am
-
-install-ps-am:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
-	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-	-rm -rf $(top_srcdir)/autom4te.cache
-		-rm -f ./$(DEPDIR)/png.Plo
-	-rm -f ./$(DEPDIR)/pngerror.Plo
-	-rm -f ./$(DEPDIR)/pngget.Plo
-	-rm -f ./$(DEPDIR)/pngmem.Plo
-	-rm -f ./$(DEPDIR)/pngpread.Plo
-	-rm -f ./$(DEPDIR)/pngread.Plo
-	-rm -f ./$(DEPDIR)/pngrio.Plo
-	-rm -f ./$(DEPDIR)/pngrtran.Plo
-	-rm -f ./$(DEPDIR)/pngrutil.Plo
-	-rm -f ./$(DEPDIR)/pngset.Plo
-	-rm -f ./$(DEPDIR)/pngtest.Po
-	-rm -f ./$(DEPDIR)/pngtrans.Plo
-	-rm -f ./$(DEPDIR)/pngwio.Plo
-	-rm -f ./$(DEPDIR)/pngwrite.Plo
-	-rm -f ./$(DEPDIR)/pngwtran.Plo
-	-rm -f ./$(DEPDIR)/pngwutil.Plo
-	-rm -f arm/$(DEPDIR)/arm_init.Plo
-	-rm -f arm/$(DEPDIR)/filter_neon.Plo
-	-rm -f arm/$(DEPDIR)/filter_neon_intrinsics.Plo
-	-rm -f arm/$(DEPDIR)/palette_neon_intrinsics.Plo
-	-rm -f contrib/libtests/$(DEPDIR)/pngimage.Po
-	-rm -f contrib/libtests/$(DEPDIR)/pngstest.Po
-	-rm -f contrib/libtests/$(DEPDIR)/pngunknown.Po
-	-rm -f contrib/libtests/$(DEPDIR)/pngvalid.Po
-	-rm -f contrib/libtests/$(DEPDIR)/timepng.Po
-	-rm -f contrib/tools/$(DEPDIR)/png-fix-itxt.Po
-	-rm -f contrib/tools/$(DEPDIR)/pngcp.Po
-	-rm -f contrib/tools/$(DEPDIR)/pngfix.Po
-	-rm -f intel/$(DEPDIR)/filter_sse2_intrinsics.Plo
-	-rm -f intel/$(DEPDIR)/intel_init.Plo
-	-rm -f mips/$(DEPDIR)/filter_msa_intrinsics.Plo
-	-rm -f mips/$(DEPDIR)/mips_init.Plo
-	-rm -f powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo
-	-rm -f powerpc/$(DEPDIR)/powerpc_init.Plo
-	-rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-compile mostlyclean-generic \
-	mostlyclean-libtool
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
-	uninstall-libLTLIBRARIES uninstall-man \
-	uninstall-nodist_pkgincludeHEADERS uninstall-pkgconfigDATA \
-	uninstall-pkgincludeHEADERS
-	@$(NORMAL_INSTALL)
-	$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-uninstall-man: uninstall-man3 uninstall-man5
-
-.MAKE: all check check-am install install-am install-data-am \
-	install-exec-am install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \
-	check-TESTS check-am clean clean-binPROGRAMS \
-	clean-checkPROGRAMS clean-cscope clean-generic \
-	clean-libLTLIBRARIES clean-libtool cscope cscopelist-am ctags \
-	ctags-am dist dist-all dist-bzip2 dist-gzip dist-hook \
-	dist-lzip dist-shar dist-tarZ dist-xz dist-zip distcheck \
-	distclean distclean-compile distclean-generic distclean-hdr \
-	distclean-libtool distclean-tags distcleancheck distdir \
-	distuninstallcheck dvi dvi-am html html-am info info-am \
-	install install-am install-binPROGRAMS install-binSCRIPTS \
-	install-data install-data-am install-data-hook install-dvi \
-	install-dvi-am install-exec install-exec-am install-exec-hook \
-	install-html install-html-am install-info install-info-am \
-	install-libLTLIBRARIES install-man install-man3 install-man5 \
-	install-nodist_pkgincludeHEADERS install-pdf install-pdf-am \
-	install-pkgconfigDATA install-pkgincludeHEADERS install-ps \
-	install-ps-am install-strip installcheck installcheck-am \
-	installdirs maintainer-clean maintainer-clean-generic \
-	mostlyclean mostlyclean-compile mostlyclean-generic \
-	mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
-	uninstall uninstall-am uninstall-binPROGRAMS \
-	uninstall-binSCRIPTS uninstall-hook uninstall-libLTLIBRARIES \
-	uninstall-man uninstall-man3 uninstall-man5 \
-	uninstall-nodist_pkgincludeHEADERS uninstall-pkgconfigDATA \
-	uninstall-pkgincludeHEADERS
-
-.PRECIOUS: Makefile
-
-
-$(PNGLIB_BASENAME).pc: libpng.pc
-	cp libpng.pc $@
-
-$(PNGLIB_BASENAME)-config: libpng-config
-	cp libpng-config $@
-
-scripts/sym.out scripts/vers.out: png.h pngconf.h pnglibconf.h
-scripts/prefix.out: png.h pngconf.h pnglibconf.out
-scripts/symbols.out: png.h pngconf.h $(srcdir)/scripts/pnglibconf.h.prebuilt
-scripts/intprefix.out: pnglibconf.h
-
-libpng.sym: scripts/sym.out
-	rm -f $@
-	cp $? $@
-libpng.vers: scripts/vers.out
-	rm -f $@
-	cp $? $@
-
-# Rename functions in scripts/prefix.out with a PNG_PREFIX prefix.
-# Rename macros in scripts/macro.lst from PNG_PREFIXpng_ to PNG_ (the actual
-# implementation of the macro).
-#pnglibconf.h: pnglibconf.out scripts/prefix.out scripts/macro.lst
-#	rm -f $@
-#	$(AWK) 's==0 && NR>1{print prev}\
-#	   s==0{prev=$$0}\
-#	   s==1{print "#define", $$1, "" $$1}\
-#	   s==2{print "#define png_" $$1, "PNG_" $$1}\
-#	   END{print prev}' s=0 pnglibconf.out s=1 scripts/prefix.out\
-#	   s=2 ${srcdir}/scripts/macro.lst >pnglibconf.tf8
-#	mv pnglibconf.tf8 $@
-
-#pngprefix.h: scripts/intprefix.out
-#	rm -f pngprefix.tf1
-#	$(AWK) '{print "#define", $$1, "" $$1}' $? >pngprefix.tf1
-#	mv pngprefix.tf1 $@
-pnglibconf.h: pnglibconf.out
-	rm -f $@
-	cp $? $@
-
-pngprefix.h: # is empty
-	:>$@
-
-$(srcdir)/scripts/pnglibconf.h.prebuilt:
-	@echo "Attempting to build $@" >&2
-	@echo "This is a machine generated file, but if you want to make" >&2
-	@echo "a new one simply make 'scripts/pnglibconf.out', copy that" >&2
-	@echo "AND set PNG_ZLIB_VERNUM to 0 (you MUST do this)" >&2
-	@exit 1
-
-# The following is necessary to ensure that the local pnglibconf.h is used, not
-# an installed one (this can happen immediately after on a clean system if
-# 'make test' is the first thing the user does.)  Only files which include
-# one of the png source files (typically png.h or pngpriv.h) need to be listed
-# here:
-pngtest.o: pnglibconf.h
-
-contrib/libtests/makepng.o: pnglibconf.h
-contrib/libtests/pngstest.o: pnglibconf.h
-contrib/libtests/pngunknown.o: pnglibconf.h
-contrib/libtests/pngimage.o: pnglibconf.h
-contrib/libtests/pngvalid.o: pnglibconf.h
-contrib/libtests/readpng.o: pnglibconf.h
-contrib/libtests/tarith.o: pnglibconf.h
-contrib/libtests/timepng.o: pnglibconf.h
-
-contrib/tools/makesRGB.o: pnglibconf.h
-contrib/tools/pngfix.o: pnglibconf.h
-contrib/tools/pngcp.o: pnglibconf.h
-
-.c.out:
-	rm -f $@ $*.tf[12]
-	test -d scripts || mkdir scripts || test -d scripts
-	$(DFNCPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)\
-	    $(CPPFLAGS) $(SYMBOL_CFLAGS) $< > $*.tf1
-	$(AWK) -f "${srcdir}/scripts/dfn.awk" out="$*.tf2" $*.tf1 1>&2
-	rm -f $*.tf1
-	mv $*.tf2 $@
-
-# The .c file for pnglibconf.h is machine generated
-pnglibconf.c: scripts/pnglibconf.dfa scripts/options.awk pngconf.h pngusr.dfa $(DFA_XTRA)
-	rm -f $@ $*.tf[45]
-	$(AWK) -f ${srcdir}/scripts/options.awk out=$*.tf4 version=search\
-	    ${srcdir}/pngconf.h ${srcdir}/scripts/pnglibconf.dfa\
-	    ${srcdir}/pngusr.dfa $(DFA_XTRA) 1>&2
-	$(AWK) -f ${srcdir}/scripts/options.awk out=$*.tf5 $*.tf4 1>&2
-	rm $*.tf4
-	mv $*.tf5 $@
-
-# Symbol checks (.def and .out files should match)
-scripts/symbols.chk: scripts/checksym.awk scripts/symbols.def scripts/symbols.out
-
-.out.chk:
-	rm -f $@ $*.new
-	$(AWK) -f ${srcdir}/scripts/checksym.awk ${srcdir}/scripts/${*F}.def\
-	    of="$*.new" $< >&2
-	mv $*.new $@
-
-# used on demand to regenerate the standard header, CPPFLAGS should
-# be empty - no non-standard defines
-scripts/pnglibconf.c: scripts/pnglibconf.dfa scripts/options.awk pngconf.h
-	rm -f $@ pnglibconf.tf[67]
-	test -z "$(CPPFLAGS)"
-	echo "com 1.6.37 STANDARD API DEFINITION" |\
-	$(AWK) -f ${srcdir}/scripts/options.awk out=pnglibconf.tf6\
-	    logunsupported=1 version=search ${srcdir}/pngconf.h -\
-	    ${srcdir}/scripts/pnglibconf.dfa 1>&2
-	$(AWK) -f ${srcdir}/scripts/options.awk out=pnglibconf.tf7\
-	    pnglibconf.tf6 1>&2
-	rm pnglibconf.tf6
-	mv pnglibconf.tf7 $@
-
-$(libpng16_la_OBJECTS): png.h pngconf.h \
-	pnglibconf.h pngpriv.h pngdebug.h pnginfo.h pngstruct.h pngprefix.h
-
-test: check-am
-
-# Extra checks
-check: scripts/symbols.chk
-
-# Don't distribute the generated script files
-dist-hook:
-	cd '$(top_distdir)'; rm -f $(SCRIPT_CLEANFILES)
-
-# Make links between installed files with release-specific names and the generic
-# file names.  If this install rule is run the generic names will be deleted and
-# recreated - this has obvious issues for systems with multiple installations.
-
-install-header-links:
-	@set -ex; cd '$(DESTDIR)$(includedir)'; for f in $(HEADERS); do \
-	   rm -f "$$f"; $(LN_S) "$(PNGLIB_BASENAME)/$$f" "$$f"; done
-
-uninstall-header-links:
-	cd '$(DESTDIR)$(includedir)'; rm -f $(HEADERS)
-
-install-libpng-pc:
-	@set -ex; cd '$(DESTDIR)$(pkgconfigdir)'; rm -f libpng.pc; \
-	   $(LN_S) '$(PNGLIB_BASENAME).pc' libpng.pc
-
-uninstall-libpng-pc:
-	rm -f '$(DESTDIR)$(pkgconfigdir)/libpng.pc'
-
-install-library-links:
-	@set -x; cd '$(DESTDIR)$(libdir)';\
-	for ext in $(EXT_LIST); do\
-	   rm -f "libpng.$$ext";\
-           if test -f "$(PNGLIB_BASENAME).$$ext"; then\
-              $(LN_S) "$(PNGLIB_BASENAME).$$ext" "libpng.$$ext" || exit 1;\
-           fi;\
-	done
-
-uninstall-library-links:
-	@set -x; cd '$(DESTDIR)$(libdir)'; for ext in $(EXT_LIST); do\
-	   rm -f "libpng.$$ext"; done
-
-install-libpng-config:
-	@set -ex; cd '$(DESTDIR)$(bindir)'; rm -f libpng-config; \
-	   $(LN_S) '$(PNGLIB_BASENAME)-config' libpng-config
-
-uninstall-libpng-config:
-	rm -f '$(DESTDIR)$(bindir)/libpng-config'
-
-# If --enable-unversioned-links is specified the header and lib file links
-# will be automatically made on a 'make install':
-
-install-data-hook: install-header-links
-uninstall-hook: uninstall-header-links
-install-exec-hook: install-library-links
-uninstall-hook: uninstall-library-links
-
-# Likewise, --install-pc causes libpng.pc to be constructed:
-
-install-data-hook: install-libpng-pc
-uninstall-hook: uninstall-libpng-pc
-
-# And --install-config:
-
-install-exec-hook: install-libpng-config
-uninstall-hook: uninstall-libpng-config
-
-# The following addition ensures that 'make all' always builds the test programs
-# too.  It used to, but some change either in libpng or configure stopped this
-# working.
-all-am: $(check_PROGRAMS)
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/libs/libpng-src/config.h b/libs/libpng-src/config.h
deleted file mode 100644
index 54a395c261bab3da4dfb2c657616b1bca4362b53..0000000000000000000000000000000000000000
--- a/libs/libpng-src/config.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-/* #undef HAVE_DLFCN_H */
-
-/* Define to 1 if you have the `feenableexcept' function. */
-/* #undef HAVE_FEENABLEEXCEPT */
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the `m' library (-lm). */
-/* #undef HAVE_LIBM */
-
-/* Define to 1 if you have the `z' library (-lz). */
-#define HAVE_LIBZ 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the `pow' function. */
-#define HAVE_POW 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to the sub-directory where libtool stores uninstalled libraries. */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
-#define PACKAGE "libpng"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "png-mng-implement@lists.sourceforge.net"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "libpng"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "libpng 1.6.37"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "libpng"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.6.37"
-
-/* Turn on ARM Neon optimizations at run-time */
-/* #undef PNG_ARM_NEON_API_SUPPORTED */
-
-/* Check for ARM Neon support at run-time */
-/* #undef PNG_ARM_NEON_CHECK_SUPPORTED */
-
-/* Enable ARM Neon optimizations */
-/* #undef PNG_ARM_NEON_OPT */
-
-/* Enable Intel SSE optimizations */
-/* #undef PNG_INTEL_SSE_OPT */
-
-/* Turn on MIPS MSA optimizations at run-time */
-/* #undef PNG_MIPS_MSA_API_SUPPORTED */
-
-/* Check for MIPS MSA support at run-time */
-/* #undef PNG_MIPS_MSA_CHECK_SUPPORTED */
-
-/* Enable MIPS MSA optimizations */
-/* #undef PNG_MIPS_MSA_OPT */
-
-/* Turn on POWERPC VSX optimizations at run-time */
-/* #undef PNG_POWERPC_VSX_API_SUPPORTED */
-
-/* Check for POWERPC VSX support at run-time */
-/* #undef PNG_POWERPC_VSX_CHECK_SUPPORTED */
-
-/* Enable POWERPC VSX optimizations */
-/* #undef PNG_POWERPC_VSX_OPT */
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Define to 1 if your <sys/time.h> declares `struct tm'. */
-/* #undef TM_IN_SYS_TIME */
-
-/* Version number of package */
-#define VERSION "1.6.37"
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to the equivalent of the C99 'restrict' keyword, or to
-   nothing if this is not supported.  Do not define if restrict is
-   supported directly.  */
-#define restrict __restrict
-/* Work around a bug in Sun C++: it does not support _Restrict or
-   __restrict__, even though the corresponding Sun C compiler ends up with
-   "#define restrict _Restrict" or "#define restrict __restrict__" in the
-   previous line.  Perhaps some future version of Sun C++ will work with
-   restrict; if so, hopefully it defines __RESTRICT like Sun C does.  */
-#if defined __SUNPRO_CC && !defined __RESTRICT
-# define _Restrict
-# define __restrict__
-#endif
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
diff --git a/libs/libpng-src/config.status b/libs/libpng-src/config.status
deleted file mode 100644
index e9a33afa2eb696f3f2c2d9d0b068464706b5f6bf..0000000000000000000000000000000000000000
--- a/libs/libpng-src/config.status
+++ /dev/null
@@ -1,2026 +0,0 @@
-#! /bin/sh
-# Generated by configure.
-# Run this file to recreate the current configuration.
-# Compiler output produced by configure, useful for debugging
-# configure, is in config.log if it exists.
-
-debug=false
-ac_cs_recheck=false
-ac_cs_silent=false
-
-SHELL=${CONFIG_SHELL-/bin/sh}
-export SHELL
-## -------------------- ##
-## M4sh Initialization. ##
-## -------------------- ##
-
-# Be more Bourne compatible
-DUALCASE=1; export DUALCASE # for MKS sh
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
-  emulate sh
-  NULLCMD=:
-  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
-  # is contrary to our usage.  Disable this feature.
-  alias -g '${1+"$@"}'='"$@"'
-  setopt NO_GLOB_SUBST
-else
-  case `(set -o) 2>/dev/null` in #(
-  *posix*) :
-    set -o posix ;; #(
-  *) :
-     ;;
-esac
-fi
-
-
-as_nl='
-'
-export as_nl
-# Printing a long string crashes Solaris 7 /usr/bin/printf.
-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
-# Prefer a ksh shell builtin over an external printf program on Solaris,
-# but without wasting forks for bash or zsh.
-if test -z "$BASH_VERSION$ZSH_VERSION" \
-    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
-  as_echo='print -r --'
-  as_echo_n='print -rn --'
-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
-  as_echo='printf %s\n'
-  as_echo_n='printf %s'
-else
-  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
-    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
-    as_echo_n='/usr/ucb/echo -n'
-  else
-    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
-    as_echo_n_body='eval
-      arg=$1;
-      case $arg in #(
-      *"$as_nl"*)
-	expr "X$arg" : "X\\(.*\\)$as_nl";
-	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
-      esac;
-      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
-    '
-    export as_echo_n_body
-    as_echo_n='sh -c $as_echo_n_body as_echo'
-  fi
-  export as_echo_body
-  as_echo='sh -c $as_echo_body as_echo'
-fi
-
-# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
-  PATH_SEPARATOR=:
-  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
-    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
-      PATH_SEPARATOR=';'
-  }
-fi
-
-
-# IFS
-# We need space, tab and new line, in precisely that order.  Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-IFS=" ""	$as_nl"
-
-# Find who we are.  Look in the path if we contain no directory separator.
-as_myself=
-case $0 in #((
-  *[\\/]* ) as_myself=$0 ;;
-  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
-  done
-IFS=$as_save_IFS
-
-     ;;
-esac
-# We did not find ourselves, most probably we were run as `sh COMMAND'
-# in which case we are not to be found in the path.
-if test "x$as_myself" = x; then
-  as_myself=$0
-fi
-if test ! -f "$as_myself"; then
-  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
-  exit 1
-fi
-
-# Unset variables that we do not need and which cause bugs (e.g. in
-# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
-# suppresses any "Segmentation fault" message there.  '((' could
-# trigger a bug in pdksh 5.2.14.
-for as_var in BASH_ENV ENV MAIL MAILPATH
-do eval test x\${$as_var+set} = xset \
-  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
-done
-PS1='$ '
-PS2='> '
-PS4='+ '
-
-# NLS nuisances.
-LC_ALL=C
-export LC_ALL
-LANGUAGE=C
-export LANGUAGE
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-
-# as_fn_error STATUS ERROR [LINENO LOG_FD]
-# ----------------------------------------
-# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
-# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
-# script with STATUS, using 1 if that was 0.
-as_fn_error ()
-{
-  as_status=$1; test $as_status -eq 0 && as_status=1
-  if test "$4"; then
-    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
-  fi
-  $as_echo "$as_me: error: $2" >&2
-  as_fn_exit $as_status
-} # as_fn_error
-
-
-# as_fn_set_status STATUS
-# -----------------------
-# Set $? to STATUS, without forking.
-as_fn_set_status ()
-{
-  return $1
-} # as_fn_set_status
-
-# as_fn_exit STATUS
-# -----------------
-# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
-as_fn_exit ()
-{
-  set +e
-  as_fn_set_status $1
-  exit $1
-} # as_fn_exit
-
-# as_fn_unset VAR
-# ---------------
-# Portably unset VAR.
-as_fn_unset ()
-{
-  { eval $1=; unset $1;}
-}
-as_unset=as_fn_unset
-# as_fn_append VAR VALUE
-# ----------------------
-# Append the text in VALUE to the end of the definition contained in VAR. Take
-# advantage of any shell optimizations that allow amortized linear growth over
-# repeated appends, instead of the typical quadratic growth present in naive
-# implementations.
-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
-  eval 'as_fn_append ()
-  {
-    eval $1+=\$2
-  }'
-else
-  as_fn_append ()
-  {
-    eval $1=\$$1\$2
-  }
-fi # as_fn_append
-
-# as_fn_arith ARG...
-# ------------------
-# Perform arithmetic evaluation on the ARGs, and store the result in the
-# global $as_val. Take advantage of shells that can avoid forks. The arguments
-# must be portable across $(()) and expr.
-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
-  eval 'as_fn_arith ()
-  {
-    as_val=$(( $* ))
-  }'
-else
-  as_fn_arith ()
-  {
-    as_val=`expr "$@" || test $? -eq 1`
-  }
-fi # as_fn_arith
-
-
-if expr a : '\(a\)' >/dev/null 2>&1 &&
-   test "X`expr 00001 : '.*\(...\)'`" = X001; then
-  as_expr=expr
-else
-  as_expr=false
-fi
-
-if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
-  as_basename=basename
-else
-  as_basename=false
-fi
-
-if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
-  as_dirname=dirname
-else
-  as_dirname=false
-fi
-
-as_me=`$as_basename -- "$0" ||
-$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
-	 X"$0" : 'X\(//\)$' \| \
-	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X/"$0" |
-    sed '/^.*\/\([^/][^/]*\)\/*$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\/\(\/\/\)$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\/\(\/\).*/{
-	    s//\1/
-	    q
-	  }
-	  s/.*/./; q'`
-
-# Avoid depending upon Character Ranges.
-as_cr_letters='abcdefghijklmnopqrstuvwxyz'
-as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-as_cr_Letters=$as_cr_letters$as_cr_LETTERS
-as_cr_digits='0123456789'
-as_cr_alnum=$as_cr_Letters$as_cr_digits
-
-ECHO_C= ECHO_N= ECHO_T=
-case `echo -n x` in #(((((
--n*)
-  case `echo 'xy\c'` in
-  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
-  xy)  ECHO_C='\c';;
-  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
-       ECHO_T='	';;
-  esac;;
-*)
-  ECHO_N='-n';;
-esac
-
-rm -f conf$$ conf$$.exe conf$$.file
-if test -d conf$$.dir; then
-  rm -f conf$$.dir/conf$$.file
-else
-  rm -f conf$$.dir
-  mkdir conf$$.dir 2>/dev/null
-fi
-if (echo >conf$$.file) 2>/dev/null; then
-  if ln -s conf$$.file conf$$ 2>/dev/null; then
-    as_ln_s='ln -s'
-    # ... but there are two gotchas:
-    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
-    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-    # In both cases, we have to default to `cp -pR'.
-    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
-      as_ln_s='cp -pR'
-  elif ln conf$$.file conf$$ 2>/dev/null; then
-    as_ln_s=ln
-  else
-    as_ln_s='cp -pR'
-  fi
-else
-  as_ln_s='cp -pR'
-fi
-rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
-rmdir conf$$.dir 2>/dev/null
-
-
-# as_fn_mkdir_p
-# -------------
-# Create "$as_dir" as a directory, including parents if necessary.
-as_fn_mkdir_p ()
-{
-
-  case $as_dir in #(
-  -*) as_dir=./$as_dir;;
-  esac
-  test -d "$as_dir" || eval $as_mkdir_p || {
-    as_dirs=
-    while :; do
-      case $as_dir in #(
-      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
-      *) as_qdir=$as_dir;;
-      esac
-      as_dirs="'$as_qdir' $as_dirs"
-      as_dir=`$as_dirname -- "$as_dir" ||
-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-	 X"$as_dir" : 'X\(//\)[^/]' \| \
-	 X"$as_dir" : 'X\(//\)$' \| \
-	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_dir" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)[^/].*/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\).*/{
-	    s//\1/
-	    q
-	  }
-	  s/.*/./; q'`
-      test -d "$as_dir" && break
-    done
-    test -z "$as_dirs" || eval "mkdir $as_dirs"
-  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
-
-
-} # as_fn_mkdir_p
-if mkdir -p . 2>/dev/null; then
-  as_mkdir_p='mkdir -p "$as_dir"'
-else
-  test -d ./-p && rmdir ./-p
-  as_mkdir_p=false
-fi
-
-
-# as_fn_executable_p FILE
-# -----------------------
-# Test if FILE is an executable regular file.
-as_fn_executable_p ()
-{
-  test -f "$1" && test -x "$1"
-} # as_fn_executable_p
-as_test_x='test -x'
-as_executable_p=as_fn_executable_p
-
-# Sed expression to map a string onto a valid CPP name.
-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
-
-# Sed expression to map a string onto a valid variable name.
-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
-
-
-exec 6>&1
-## ----------------------------------- ##
-## Main body of $CONFIG_STATUS script. ##
-## ----------------------------------- ##
-# Save the log message, to keep $0 and so on meaningful, and to
-# report actual input values of CONFIG_FILES etc. instead of their
-# values after options handling.
-ac_log="
-This file was extended by libpng $as_me 1.6.37, which was
-generated by GNU Autoconf 2.69.  Invocation command line was
-
-  CONFIG_FILES    = $CONFIG_FILES
-  CONFIG_HEADERS  = $CONFIG_HEADERS
-  CONFIG_LINKS    = $CONFIG_LINKS
-  CONFIG_COMMANDS = $CONFIG_COMMANDS
-  $ $0 $@
-
-on `(hostname || uname -n) 2>/dev/null | sed 1q`
-"
-
-# Files that config.status was made for.
-config_files=" Makefile libpng.pc:libpng.pc.in libpng-config:libpng-config.in"
-config_headers=" config.h"
-config_commands=" depfiles libtool"
-
-ac_cs_usage="\
-\`$as_me' instantiates files and other configuration actions
-from templates according to the current configuration.  Unless the files
-and actions are specified as TAGs, all are instantiated by default.
-
-Usage: $0 [OPTION]... [TAG]...
-
-  -h, --help       print this help, then exit
-  -V, --version    print version number and configuration settings, then exit
-      --config     print configuration, then exit
-  -q, --quiet, --silent
-                   do not print progress messages
-  -d, --debug      don't remove temporary files
-      --recheck    update $as_me by reconfiguring in the same conditions
-      --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
-      --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
-
-Configuration files:
-$config_files
-
-Configuration headers:
-$config_headers
-
-Configuration commands:
-$config_commands
-
-Report bugs to <png-mng-implement@lists.sourceforge.net>."
-
-ac_cs_config=""
-ac_cs_version="\
-libpng config.status 1.6.37
-configured by ./configure, generated by GNU Autoconf 2.69,
-  with options \"$ac_cs_config\"
-
-Copyright (C) 2012 Free Software Foundation, Inc.
-This config.status script is free software; the Free Software Foundation
-gives unlimited permission to copy, distribute and modify it."
-
-ac_pwd='/f/lpng1637'
-srcdir='.'
-INSTALL='/usr/bin/install -c'
-MKDIR_P='/usr/bin/mkdir -p'
-AWK='gawk'
-test -n "$AWK" || AWK=awk
-# The default lists apply if the user does not specify any file.
-ac_need_defaults=:
-while test $# != 0
-do
-  case $1 in
-  --*=?*)
-    ac_option=`expr "X$1" : 'X\([^=]*\)='`
-    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
-    ac_shift=:
-    ;;
-  --*=)
-    ac_option=`expr "X$1" : 'X\([^=]*\)='`
-    ac_optarg=
-    ac_shift=:
-    ;;
-  *)
-    ac_option=$1
-    ac_optarg=$2
-    ac_shift=shift
-    ;;
-  esac
-
-  case $ac_option in
-  # Handling of the options.
-  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
-    ac_cs_recheck=: ;;
-  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
-    $as_echo "$ac_cs_version"; exit ;;
-  --config | --confi | --conf | --con | --co | --c )
-    $as_echo "$ac_cs_config"; exit ;;
-  --debug | --debu | --deb | --de | --d | -d )
-    debug=: ;;
-  --file | --fil | --fi | --f )
-    $ac_shift
-    case $ac_optarg in
-    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
-    '') as_fn_error $? "missing file argument" ;;
-    esac
-    as_fn_append CONFIG_FILES " '$ac_optarg'"
-    ac_need_defaults=false;;
-  --header | --heade | --head | --hea )
-    $ac_shift
-    case $ac_optarg in
-    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
-    esac
-    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
-    ac_need_defaults=false;;
-  --he | --h)
-    # Conflict between --help and --header
-    as_fn_error $? "ambiguous option: \`$1'
-Try \`$0 --help' for more information.";;
-  --help | --hel | -h )
-    $as_echo "$ac_cs_usage"; exit ;;
-  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
-  | -silent | --silent | --silen | --sile | --sil | --si | --s)
-    ac_cs_silent=: ;;
-
-  # This is an error.
-  -*) as_fn_error $? "unrecognized option: \`$1'
-Try \`$0 --help' for more information." ;;
-
-  *) as_fn_append ac_config_targets " $1"
-     ac_need_defaults=false ;;
-
-  esac
-  shift
-done
-
-ac_configure_extra_args=
-
-if $ac_cs_silent; then
-  exec 6>/dev/null
-  ac_configure_extra_args="$ac_configure_extra_args --silent"
-fi
-
-if $ac_cs_recheck; then
-  set X /bin/sh './configure'  $ac_configure_extra_args --no-create --no-recursion
-  shift
-  $as_echo "running CONFIG_SHELL=/bin/sh $*" >&6
-  CONFIG_SHELL='/bin/sh'
-  export CONFIG_SHELL
-  exec "$@"
-fi
-
-exec 5>>config.log
-{
-  echo
-  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
-## Running $as_me. ##
-_ASBOX
-  $as_echo "$ac_log"
-} >&5
-
-#
-# INIT-COMMANDS
-#
-AMDEP_TRUE="" MAKE="make"
-
-
-# The HP-UX ksh and POSIX shell print the target directory to stdout
-# if CDPATH is set.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
-double_quote_subst='s/\(["`\\]\)/\\\1/g'
-delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
-SED='/usr/bin/sed'
-Xsed='/usr/bin/sed -e 1s/^X//'
-GREP='/usr/bin/grep'
-EGREP='/usr/bin/grep -E'
-FGREP='/usr/bin/grep -F'
-SHELL='/bin/sh'
-ECHO='printf %s\n'
-LD='C:/msys64/mingw32/i686-w64-mingw32/bin/ld.exe'
-macro_version='2.4.6'
-macro_revision='2.4.6'
-AS='as'
-DLLTOOL='dlltool'
-OBJDUMP='objdump'
-enable_shared='yes'
-enable_static='yes'
-pic_mode='default'
-enable_fast_install='needless'
-shared_archive_member_spec=''
-PATH_SEPARATOR=':'
-host_alias=''
-host='i686-w64-mingw32'
-host_os='mingw32'
-build_alias='i686-w64-mingw32'
-build='i686-w64-mingw32'
-build_os='mingw32'
-NM='/mingw32/bin/nm -B'
-LN_S='cp -pR'
-max_cmd_len='8192'
-ac_objext='o'
-exeext=''
-lt_unset='unset'
-lt_SP2NL='tr \040 \012'
-lt_NL2SP='tr \015\012 \040\040'
-lt_cv_to_host_file_cmd='func_convert_file_msys_to_w32'
-lt_cv_to_tool_file_cmd='func_convert_file_msys_to_w32'
-reload_flag=' -r'
-reload_cmds='$LD$reload_flag -o $output$reload_objs'
-deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
-file_magic_cmd='func_win32_libid'
-file_magic_glob=''
-want_nocaseglob='yes'
-sharedlib_from_linklib_cmd='func_cygming_dll_for_implib'
-AR='ar'
-AR_FLAGS='cru'
-archiver_list_spec='@'
-STRIP='strip'
-RANLIB='ranlib'
-old_postinstall_cmds='chmod 644 $oldlib~$RANLIB $tool_oldlib'
-old_postuninstall_cmds=''
-old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $tool_oldlib'
-lock_old_archive_extraction='no'
-CC='gcc'
-CFLAGS='-g -O2'
-compiler='gcc'
-GCC='yes'
-lt_cv_sys_global_symbol_pipe='sed -n -e '\''s/^.*[	 ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[	 ][	 ]*_\([_A-Za-z][_A-Za-z0-9]*\)
\{0,1\}$/\1 _\2 \2/p'\'' | sed '\''/ __gnu_lto/d'\'''
-lt_cv_sys_global_symbol_to_cdecl='sed -n -e '\''s/^T .* \(.*\)$/extern int \1();/p'\'' -e '\''s/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(.*\)$/extern char \1;/p'\'''
-lt_cv_sys_global_symbol_to_import=''
-lt_cv_sys_global_symbol_to_c_name_address='sed -n -e '\''s/^: \(.*\) .*$/  {"\1", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(.*\)$/  {"\1", (void *) \&\1},/p'\'''
-lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='sed -n -e '\''s/^: \(.*\) .*$/  {"\1", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(lib.*\)$/  {"\1", (void *) \&\1},/p'\'' -e '\''s/^[ABCDGIRSTW][ABCDGIRSTW]* .* \(.*\)$/  {"lib\1", (void *) \&\1},/p'\'''
-lt_cv_nm_interface='BSD nm'
-nm_file_list_spec='@'
-lt_sysroot=''
-lt_cv_truncate_bin='/usr/bin/sed -e 4q'
-objdir='.libs'
-MAGIC_CMD='file'
-lt_prog_compiler_no_builtin_flag=' -fno-builtin'
-lt_prog_compiler_pic=' -DDLL_EXPORT -DPIC'
-lt_prog_compiler_wl='-Wl,'
-lt_prog_compiler_static='-static'
-lt_cv_prog_compiler_c_o='yes'
-need_locks='no'
-MANIFEST_TOOL=':'
-DSYMUTIL=''
-NMEDIT=''
-LIPO=''
-OTOOL=''
-OTOOL64=''
-libext='a'
-shrext_cmds='.dll'
-extract_expsyms_cmds=''
-archive_cmds_need_lc='yes'
-enable_shared_with_static_runtimes='yes'
-export_dynamic_flag_spec='$wl--export-all-symbols'
-whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
-compiler_needs_object='no'
-old_archive_from_new_cmds=''
-old_archive_from_expsyms_cmds=''
-archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
-archive_expsym_cmds='if   test DEF = "`$SED -n     -e '\''s/^[	 ]*//'\''     -e '\''/^\(;.*\)*$/d'\''     -e '\''s/^\(EXPORTS\|LIBRARY\)\([	 ].*\)*$/DEF/p'\''     -e q     $export_symbols`" ; then
-          cp $export_symbols $output_objdir/$soname.def;
-        else
-          echo EXPORTS > $output_objdir/$soname.def;
-          cat $export_symbols >> $output_objdir/$soname.def;
-        fi~
-        $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
-module_cmds=''
-module_expsym_cmds=''
-with_gnu_ld='yes'
-allow_undefined_flag='unsupported'
-no_undefined_flag=''
-hardcode_libdir_flag_spec='-L$libdir'
-hardcode_libdir_separator=''
-hardcode_direct='no'
-hardcode_direct_absolute='no'
-hardcode_minus_L='no'
-hardcode_shlibpath_var='unsupported'
-hardcode_automatic='no'
-inherit_rpath='no'
-link_all_deplibs='unknown'
-always_export_symbols='no'
-export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
-exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
-include_expsyms=''
-prelink_cmds=''
-postlink_cmds=''
-file_list_spec=''
-variables_saved_for_relink='PATH PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH'
-need_lib_prefix='no'
-need_version='no'
-version_type='windows'
-runpath_var='LD_RUN_PATH'
-shlibpath_var='PATH'
-shlibpath_overrides_runpath='yes'
-libname_spec='lib$name'
-library_names_spec='$libname.dll.a'
-soname_spec='$libname`echo $release | $SED -e s/[.]/-/g`$versuffix$shared_ext'
-install_override_mode=''
-postinstall_cmds='base_file=`basename \$file`~
-      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
-      dldir=$destdir/`dirname \$dlpath`~
-      test -d \$dldir || mkdir -p \$dldir~
-      $install_prog $dir/$dlname \$dldir/$dlname~
-      chmod a+x \$dldir/$dlname~
-      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
-        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
-      fi'
-postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
-      dlpath=$dir/\$dldll~
-       $RM \$dlpath'
-finish_cmds=''
-finish_eval=''
-hardcode_into_libs='no'
-sys_lib_search_path_spec='C:/msys64/mingw32/lib/gcc/i686-w64-mingw32/9.1.0 C:/msys64/mingw32/lib/gcc C:/msys64/mingw32/i686-w64-mingw32/lib C:/msys64/mingw32/lib '
-configure_time_dlsearch_path='/lib /usr/lib'
-configure_time_lt_sys_library_path=''
-hardcode_action='immediate'
-enable_dlopen='unknown'
-enable_dlopen_self='unknown'
-enable_dlopen_self_static='unknown'
-old_striplib='strip --strip-debug'
-striplib='strip --strip-unneeded'
-
-LTCC='gcc'
-LTCFLAGS='-g -O2'
-compiler='gcc'
-
-# A function that is used when there is no print builtin or printf.
-func_fallback_echo ()
-{
-  eval 'cat <<_LTECHO_EOF
-$1
-_LTECHO_EOF'
-}
-
-# Quote evaled strings.
-for var in SED GREP EGREP FGREP SHELL ECHO LD AS DLLTOOL OBJDUMP PATH_SEPARATOR NM LN_S lt_SP2NL lt_NL2SP reload_flag deplibs_check_method file_magic_cmd file_magic_glob want_nocaseglob sharedlib_from_linklib_cmd AR AR_FLAGS archiver_list_spec STRIP RANLIB CC CFLAGS compiler lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl lt_cv_sys_global_symbol_to_import lt_cv_sys_global_symbol_to_c_name_address lt_cv_sys_global_symbol_to_c_name_address_lib_prefix lt_cv_nm_interface nm_file_list_spec lt_cv_truncate_bin lt_prog_compiler_no_builtin_flag lt_prog_compiler_pic lt_prog_compiler_wl lt_prog_compiler_static lt_cv_prog_compiler_c_o need_locks MANIFEST_TOOL DSYMUTIL NMEDIT LIPO OTOOL OTOOL64 shrext_cmds export_dynamic_flag_spec whole_archive_flag_spec compiler_needs_object with_gnu_ld allow_undefined_flag no_undefined_flag hardcode_libdir_flag_spec hardcode_libdir_separator exclude_expsyms include_expsyms file_list_spec variables_saved_for_relink libname_spec library_names_spec soname_spec install_override_mode finish_eval old_striplib striplib; do
-    case `eval \\$ECHO \\""\\$$var"\\"` in
-    *[\\\`\"\$]*)
-      eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED \"\$sed_quote_subst\"\`\\\"" ## exclude from sc_prohibit_nested_quotes
-      ;;
-    *)
-      eval "lt_$var=\\\"\$$var\\\""
-      ;;
-    esac
-done
-
-# Double-quote double-evaled strings.
-for var in reload_cmds old_postinstall_cmds old_postuninstall_cmds old_archive_cmds extract_expsyms_cmds old_archive_from_new_cmds old_archive_from_expsyms_cmds archive_cmds archive_expsym_cmds module_cmds module_expsym_cmds export_symbols_cmds prelink_cmds postlink_cmds postinstall_cmds postuninstall_cmds finish_cmds sys_lib_search_path_spec configure_time_dlsearch_path configure_time_lt_sys_library_path; do
-    case `eval \\$ECHO \\""\\$$var"\\"` in
-    *[\\\`\"\$]*)
-      eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" ## exclude from sc_prohibit_nested_quotes
-      ;;
-    *)
-      eval "lt_$var=\\\"\$$var\\\""
-      ;;
-    esac
-done
-
-ac_aux_dir='.'
-
-# See if we are running on zsh, and set the options that allow our
-# commands through without removal of \ escapes INIT.
-if test -n "${ZSH_VERSION+set}"; then
-   setopt NO_GLOB_SUBST
-fi
-
-
-    PACKAGE='libpng'
-    VERSION='1.6.37'
-    RM='rm -f'
-    ofile='libtool'
-
-
-
-
-
-# Handling of arguments.
-for ac_config_target in $ac_config_targets
-do
-  case $ac_config_target in
-    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
-    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
-    "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
-    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
-    "libpng.pc") CONFIG_FILES="$CONFIG_FILES libpng.pc:libpng.pc.in" ;;
-    "libpng-config") CONFIG_FILES="$CONFIG_FILES libpng-config:libpng-config.in" ;;
-
-  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
-  esac
-done
-
-
-# If the user did not use the arguments to specify the items to instantiate,
-# then the envvar interface is used.  Set only those that are not.
-# We use the long form for the default assignment because of an extremely
-# bizarre bug on SunOS 4.1.3.
-if $ac_need_defaults; then
-  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
-  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
-  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
-fi
-
-# Have a temporary directory for convenience.  Make it in the build tree
-# simply because there is no reason against having it here, and in addition,
-# creating and moving files from /tmp can sometimes cause problems.
-# Hook for its removal unless debugging.
-# Note that there is a small window in which the directory will not be cleaned:
-# after its creation but before its name has been assigned to `$tmp'.
-$debug ||
-{
-  tmp= ac_tmp=
-  trap 'exit_status=$?
-  : "${ac_tmp:=$tmp}"
-  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
-' 0
-  trap 'as_fn_exit 1' 1 2 13 15
-}
-# Create a (secure) tmp directory for tmp files.
-
-{
-  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
-  test -d "$tmp"
-}  ||
-{
-  tmp=./conf$$-$RANDOM
-  (umask 077 && mkdir "$tmp")
-} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
-ac_tmp=$tmp
-
-# Set up the scripts for CONFIG_FILES section.
-# No need to generate them if there are no CONFIG_FILES.
-# This happens for instance with `./config.status config.h'.
-if test -n "$CONFIG_FILES"; then
-
-
-ac_cr=`echo X | tr X '\015'`
-# On cygwin, bash can eat \r inside `` if the user requested igncr.
-# But we know of no other shell where ac_cr would be empty at this
-# point, so we can use a bashism as a fallback.
-if test "x$ac_cr" = x; then
-  eval ac_cr=\$\'\\r\'
-fi
-ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
-if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
-  ac_cs_awk_cr='\\r'
-else
-  ac_cs_awk_cr=$ac_cr
-fi
-
-echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
-cat >>"$ac_tmp/subs1.awk" <<\_ACAWK &&
-S["am__EXEEXT_FALSE"]="#"
-S["am__EXEEXT_TRUE"]=""
-S["LTLIBOBJS"]=""
-S["PNG_POWERPC_VSX_FALSE"]="#"
-S["PNG_POWERPC_VSX_TRUE"]=""
-S["PNG_INTEL_SSE_FALSE"]="#"
-S["PNG_INTEL_SSE_TRUE"]=""
-S["PNG_MIPS_MSA_FALSE"]="#"
-S["PNG_MIPS_MSA_TRUE"]=""
-S["PNG_ARM_NEON_FALSE"]=""
-S["PNG_ARM_NEON_TRUE"]="#"
-S["DO_INSTALL_LIBPNG_CONFIG_FALSE"]="#"
-S["DO_INSTALL_LIBPNG_CONFIG_TRUE"]=""
-S["DO_INSTALL_LIBPNG_PC_FALSE"]="#"
-S["DO_INSTALL_LIBPNG_PC_TRUE"]=""
-S["DO_INSTALL_LINKS_FALSE"]="#"
-S["DO_INSTALL_LINKS_TRUE"]=""
-S["DO_PNG_PREFIX_FALSE"]=""
-S["DO_PNG_PREFIX_TRUE"]="#"
-S["PNG_PREFIX"]=""
-S["binconfigs"]="${binconfigs}"
-S["pkgconfigdir"]="${libdir}/pkgconfig"
-S["PNGLIB_RELEASE"]="37"
-S["PNGLIB_MINOR"]="6"
-S["PNGLIB_MAJOR"]="1"
-S["PNGLIB_VERSION"]="1.6.37"
-S["SYMBOL_PREFIX"]=""
-S["HAVE_LD_VERSION_SCRIPT_FALSE"]=""
-S["HAVE_LD_VERSION_SCRIPT_TRUE"]="#"
-S["HAVE_SOLARIS_LD_FALSE"]=""
-S["HAVE_SOLARIS_LD_TRUE"]="#"
-S["HAVE_CLOCK_GETTIME_FALSE"]="#"
-S["HAVE_CLOCK_GETTIME_TRUE"]=""
-S["LIBOBJS"]=""
-S["POW_LIB"]=""
-S["PNG_COPTS"]=""
-S["DFNCPP"]="gcc -E"
-S["LT_SYS_LIBRARY_PATH"]=""
-S["OTOOL64"]=""
-S["OTOOL"]=""
-S["LIPO"]=""
-S["NMEDIT"]=""
-S["DSYMUTIL"]=""
-S["MANIFEST_TOOL"]=":"
-S["RANLIB"]="ranlib"
-S["ac_ct_AR"]="ar"
-S["AR"]="ar"
-S["NM"]="/mingw32/bin/nm -B"
-S["ac_ct_DUMPBIN"]=""
-S["DUMPBIN"]=""
-S["LIBTOOL"]="$(SHELL) $(top_builddir)/libtool"
-S["OBJDUMP"]="objdump"
-S["DLLTOOL"]="dlltool"
-S["AS"]="as"
-S["LN_S"]="cp -pR"
-S["CPP"]="gcc -E"
-S["LD"]="C:/msys64/mingw32/i686-w64-mingw32/bin/ld.exe"
-S["FGREP"]="/usr/bin/grep -F"
-S["EGREP"]="/usr/bin/grep -E"
-S["GREP"]="/usr/bin/grep"
-S["SED"]="/usr/bin/sed"
-S["host_os"]="mingw32"
-S["host_vendor"]="w64"
-S["host_cpu"]="i686"
-S["host"]="i686-w64-mingw32"
-S["build_os"]="mingw32"
-S["build_vendor"]="w64"
-S["build_cpu"]="i686"
-S["build"]="i686-w64-mingw32"
-S["am__fastdepCCAS_FALSE"]="#"
-S["am__fastdepCCAS_TRUE"]=""
-S["CCASDEPMODE"]="depmode=gcc3"
-S["CCASFLAGS"]="-g -O2"
-S["CCAS"]="gcc"
-S["am__fastdepCC_FALSE"]="#"
-S["am__fastdepCC_TRUE"]=""
-S["CCDEPMODE"]="depmode=gcc3"
-S["am__nodep"]="_no"
-S["AMDEPBACKSLASH"]="\\"
-S["AMDEP_FALSE"]="#"
-S["AMDEP_TRUE"]=""
-S["am__include"]="include"
-S["DEPDIR"]=".deps"
-S["OBJEXT"]="o"
-S["EXEEXT"]=".exe"
-S["ac_ct_CC"]="gcc"
-S["CPPFLAGS"]=""
-S["LDFLAGS"]=""
-S["CFLAGS"]="-g -O2"
-S["CC"]="gcc"
-S["MAINT"]="#"
-S["MAINTAINER_MODE_FALSE"]=""
-S["MAINTAINER_MODE_TRUE"]="#"
-S["AM_BACKSLASH"]="\\"
-S["AM_DEFAULT_VERBOSITY"]="1"
-S["AM_DEFAULT_V"]="$(AM_DEFAULT_VERBOSITY)"
-S["AM_V"]="$(V)"
-S["am__untar"]="$${TAR-tar} xf -"
-S["am__tar"]="$${TAR-tar} chof - \"$$tardir\""
-S["AMTAR"]="$${TAR-tar}"
-S["am__leading_dot"]="."
-S["SET_MAKE"]=""
-S["AWK"]="gawk"
-S["mkdir_p"]="$(MKDIR_P)"
-S["MKDIR_P"]="/usr/bin/mkdir -p"
-S["INSTALL_STRIP_PROGRAM"]="$(install_sh) -c -s"
-S["STRIP"]="strip"
-S["install_sh"]="${SHELL} /f/lpng1637/install-sh"
-S["MAKEINFO"]="${SHELL} /f/lpng1637/missing makeinfo"
-S["AUTOHEADER"]="${SHELL} /f/lpng1637/missing autoheader"
-S["AUTOMAKE"]="${SHELL} /f/lpng1637/missing automake-1.16"
-S["AUTOCONF"]="${SHELL} /f/lpng1637/missing autoconf"
-S["ACLOCAL"]="${SHELL} /f/lpng1637/missing aclocal-1.16"
-S["VERSION"]="1.6.37"
-S["PACKAGE"]="libpng"
-S["CYGPATH_W"]="cygpath -w"
-S["am__isrc"]=""
-S["INSTALL_DATA"]="${INSTALL} -m 644"
-S["INSTALL_SCRIPT"]="${INSTALL}"
-S["INSTALL_PROGRAM"]="${INSTALL}"
-S["target_alias"]=""
-S["host_alias"]=""
-S["build_alias"]="i686-w64-mingw32"
-S["LIBS"]="-lz "
-S["ECHO_T"]=""
-S["ECHO_N"]="-n"
-S["ECHO_C"]=""
-S["DEFS"]="-DHAVE_CONFIG_H"
-S["mandir"]="${datarootdir}/man"
-S["localedir"]="${datarootdir}/locale"
-S["libdir"]="${exec_prefix}/lib"
-S["psdir"]="${docdir}"
-S["pdfdir"]="${docdir}"
-S["dvidir"]="${docdir}"
-S["htmldir"]="${docdir}"
-S["infodir"]="${datarootdir}/info"
-S["docdir"]="${datarootdir}/doc/${PACKAGE_TARNAME}"
-S["oldincludedir"]="/usr/include"
-S["includedir"]="${prefix}/include"
-S["localstatedir"]="${prefix}/var"
-S["sharedstatedir"]="${prefix}/com"
-S["sysconfdir"]="${prefix}/etc"
-S["datadir"]="${datarootdir}"
-S["datarootdir"]="${prefix}/share"
-S["libexecdir"]="${exec_prefix}/libexec"
-S["sbindir"]="${exec_prefix}/sbin"
-S["bindir"]="${exec_prefix}/bin"
-S["program_transform_name"]="s,x,x,"
-S["prefix"]="/mingw32"
-S["exec_prefix"]="${prefix}"
-S["PACKAGE_URL"]=""
-S["PACKAGE_BUGREPORT"]="png-mng-implement@lists.sourceforge.net"
-S["PACKAGE_STRING"]="libpng 1.6.37"
-S["PACKAGE_VERSION"]="1.6.37"
-S["PACKAGE_TARNAME"]="libpng"
-S["PACKAGE_NAME"]="libpng"
-S["PATH_SEPARATOR"]=":"
-S["SHELL"]="/bin/sh"
-S["am__quote"]=""
-_ACAWK
-cat >>"$ac_tmp/subs1.awk" <<_ACAWK &&
-  for (key in S) S_is_set[key] = 1
-  FS = ""
-
-}
-{
-  line = $ 0
-  nfields = split(line, field, "@")
-  substed = 0
-  len = length(field[1])
-  for (i = 2; i < nfields; i++) {
-    key = field[i]
-    keylen = length(key)
-    if (S_is_set[key]) {
-      value = S[key]
-      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
-      len += length(value) + length(field[++i])
-      substed = 1
-    } else
-      len += 1 + keylen
-  }
-
-  print line
-}
-
-_ACAWK
-if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
-  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
-else
-  cat
-fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
-  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
-fi # test -n "$CONFIG_FILES"
-
-# Set up the scripts for CONFIG_HEADERS section.
-# No need to generate them if there are no CONFIG_HEADERS.
-# This happens for instance with `./config.status Makefile'.
-if test -n "$CONFIG_HEADERS"; then
-cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
-BEGIN {
-D["PACKAGE_NAME"]=" \"libpng\""
-D["PACKAGE_TARNAME"]=" \"libpng\""
-D["PACKAGE_VERSION"]=" \"1.6.37\""
-D["PACKAGE_STRING"]=" \"libpng 1.6.37\""
-D["PACKAGE_BUGREPORT"]=" \"png-mng-implement@lists.sourceforge.net\""
-D["PACKAGE_URL"]=" \"\""
-D["PACKAGE"]=" \"libpng\""
-D["VERSION"]=" \"1.6.37\""
-D["STDC_HEADERS"]=" 1"
-D["HAVE_SYS_TYPES_H"]=" 1"
-D["HAVE_SYS_STAT_H"]=" 1"
-D["HAVE_STDLIB_H"]=" 1"
-D["HAVE_STRING_H"]=" 1"
-D["HAVE_MEMORY_H"]=" 1"
-D["HAVE_STRINGS_H"]=" 1"
-D["HAVE_INTTYPES_H"]=" 1"
-D["HAVE_STDINT_H"]=" 1"
-D["HAVE_UNISTD_H"]=" 1"
-D["LT_OBJDIR"]=" \".libs/\""
-D["STDC_HEADERS"]=" 1"
-D["restrict"]=" __restrict"
-D["HAVE_POW"]=" 1"
-D["HAVE_LIBZ"]=" 1"
-  for (key in D) D_is_set[key] = 1
-  FS = ""
-}
-/^[\t ]*#[\t ]*(define|undef)[\t ]+[_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ][_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]*([\t (]|$)/ {
-  line = $ 0
-  split(line, arg, " ")
-  if (arg[1] == "#") {
-    defundef = arg[2]
-    mac1 = arg[3]
-  } else {
-    defundef = substr(arg[1], 2)
-    mac1 = arg[2]
-  }
-  split(mac1, mac2, "(") #)
-  macro = mac2[1]
-  prefix = substr(line, 1, index(line, defundef) - 1)
-  if (D_is_set[macro]) {
-    # Preserve the white space surrounding the "#".
-    print prefix "define", macro P[macro] D[macro]
-    next
-  } else {
-    # Replace #undef with comments.  This is necessary, for example,
-    # in the case of _POSIX_SOURCE, which is predefined and required
-    # on some systems where configure will not decide to define it.
-    if (defundef == "undef") {
-      print "/*", prefix defundef, macro, "*/"
-      next
-    }
-  }
-}
-{ print }
-_ACAWK
-  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
-fi # test -n "$CONFIG_HEADERS"
-
-
-eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
-shift
-for ac_tag
-do
-  case $ac_tag in
-  :[FHLC]) ac_mode=$ac_tag; continue;;
-  esac
-  case $ac_mode$ac_tag in
-  :[FHL]*:*);;
-  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
-  :[FH]-) ac_tag=-:-;;
-  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
-  esac
-  ac_save_IFS=$IFS
-  IFS=:
-  set x $ac_tag
-  IFS=$ac_save_IFS
-  shift
-  ac_file=$1
-  shift
-
-  case $ac_mode in
-  :L) ac_source=$1;;
-  :[FH])
-    ac_file_inputs=
-    for ac_f
-    do
-      case $ac_f in
-      -) ac_f="$ac_tmp/stdin";;
-      *) # Look for the file first in the build tree, then in the source tree
-	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
-	 # because $ac_f cannot contain `:'.
-	 test -f "$ac_f" ||
-	   case $ac_f in
-	   [\\/$]*) false;;
-	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
-	   esac ||
-	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
-      esac
-      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
-      as_fn_append ac_file_inputs " '$ac_f'"
-    done
-
-    # Let's still pretend it is `configure' which instantiates (i.e., don't
-    # use $as_me), people would be surprised to read:
-    #    /* config.h.  Generated by config.status.  */
-    configure_input='Generated from '`
-	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
-	`' by configure.'
-    if test x"$ac_file" != x-; then
-      configure_input="$ac_file.  $configure_input"
-      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
-$as_echo "$as_me: creating $ac_file" >&6;}
-    fi
-    # Neutralize special characters interpreted by sed in replacement strings.
-    case $configure_input in #(
-    *\&* | *\|* | *\\* )
-       ac_sed_conf_input=`$as_echo "$configure_input" |
-       sed 's/[\\\\&|]/\\\\&/g'`;; #(
-    *) ac_sed_conf_input=$configure_input;;
-    esac
-
-    case $ac_tag in
-    *:-:* | *:-) cat >"$ac_tmp/stdin" \
-      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
-    esac
-    ;;
-  esac
-
-  ac_dir=`$as_dirname -- "$ac_file" ||
-$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-	 X"$ac_file" : 'X\(//\)[^/]' \| \
-	 X"$ac_file" : 'X\(//\)$' \| \
-	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$ac_file" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)[^/].*/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\).*/{
-	    s//\1/
-	    q
-	  }
-	  s/.*/./; q'`
-  as_dir="$ac_dir"; as_fn_mkdir_p
-  ac_builddir=.
-
-case "$ac_dir" in
-.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
-*)
-  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
-  # A ".." for each directory in $ac_dir_suffix.
-  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
-  case $ac_top_builddir_sub in
-  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
-  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
-  esac ;;
-esac
-ac_abs_top_builddir=$ac_pwd
-ac_abs_builddir=$ac_pwd$ac_dir_suffix
-# for backward compatibility:
-ac_top_builddir=$ac_top_build_prefix
-
-case $srcdir in
-  .)  # We are building in place.
-    ac_srcdir=.
-    ac_top_srcdir=$ac_top_builddir_sub
-    ac_abs_top_srcdir=$ac_pwd ;;
-  [\\/]* | ?:[\\/]* )  # Absolute name.
-    ac_srcdir=$srcdir$ac_dir_suffix;
-    ac_top_srcdir=$srcdir
-    ac_abs_top_srcdir=$srcdir ;;
-  *) # Relative name.
-    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
-    ac_top_srcdir=$ac_top_build_prefix$srcdir
-    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
-esac
-ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
-
-
-  case $ac_mode in
-  :F)
-  #
-  # CONFIG_FILE
-  #
-
-  case $INSTALL in
-  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
-  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
-  esac
-  ac_MKDIR_P=$MKDIR_P
-  case $MKDIR_P in
-  [\\/$]* | ?:[\\/]* ) ;;
-  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
-  esac
-# If the template does not know about datarootdir, expand it.
-# FIXME: This hack should be removed a few years after 2.60.
-ac_datarootdir_hack=; ac_datarootdir_seen=
-ac_sed_dataroot='
-/datarootdir/ {
-  p
-  q
-}
-/@datadir@/p
-/@docdir@/p
-/@infodir@/p
-/@localedir@/p
-/@mandir@/p'
-case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
-*datarootdir*) ac_datarootdir_seen=yes;;
-*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
-$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
-  ac_datarootdir_hack='
-  s&@datadir@&${datarootdir}&g
-  s&@docdir@&${datarootdir}/doc/${PACKAGE_TARNAME}&g
-  s&@infodir@&${datarootdir}/info&g
-  s&@localedir@&${datarootdir}/locale&g
-  s&@mandir@&${datarootdir}/man&g
-  s&\${datarootdir}&${prefix}/share&g' ;;
-esac
-ac_sed_extra="/^[	 ]*VPATH[	 ]*=[	 ]*/{
-h
-s///
-s/^/:/
-s/[	 ]*$/:/
-s/:\$(srcdir):/:/g
-s/:\${srcdir}:/:/g
-s/:@srcdir@:/:/g
-s/^:*//
-s/:*$//
-x
-s/\(=[	 ]*\).*/\1/
-G
-s/\n//
-s/^[^=]*=[	 ]*$//
-}
-
-:t
-/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
-s|@configure_input@|$ac_sed_conf_input|;t t
-s&@top_builddir@&$ac_top_builddir_sub&;t t
-s&@top_build_prefix@&$ac_top_build_prefix&;t t
-s&@srcdir@&$ac_srcdir&;t t
-s&@abs_srcdir@&$ac_abs_srcdir&;t t
-s&@top_srcdir@&$ac_top_srcdir&;t t
-s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
-s&@builddir@&$ac_builddir&;t t
-s&@abs_builddir@&$ac_abs_builddir&;t t
-s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
-s&@INSTALL@&$ac_INSTALL&;t t
-s&@MKDIR_P@&$ac_MKDIR_P&;t t
-$ac_datarootdir_hack
-"
-eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
-  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
-
-test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
-  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
-  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
-      "$ac_tmp/out"`; test -z "$ac_out"; } &&
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined.  Please make sure it is defined" >&5
-$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined.  Please make sure it is defined" >&2;}
-
-  rm -f "$ac_tmp/stdin"
-  case $ac_file in
-  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
-  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
-  esac \
-  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
- ;;
-  :H)
-  #
-  # CONFIG_HEADER
-  #
-  if test x"$ac_file" != x-; then
-    {
-      $as_echo "/* $configure_input  */" \
-      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
-    } >"$ac_tmp/config.h" \
-      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
-    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
-      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
-$as_echo "$as_me: $ac_file is unchanged" >&6;}
-    else
-      rm -f "$ac_file"
-      mv "$ac_tmp/config.h" "$ac_file" \
-	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
-    fi
-  else
-    $as_echo "/* $configure_input  */" \
-      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
-      || as_fn_error $? "could not create -" "$LINENO" 5
-  fi
-# Compute "$ac_file"'s index in $config_headers.
-_am_arg="$ac_file"
-_am_stamp_count=1
-for _am_header in $config_headers :; do
-  case $_am_header in
-    $_am_arg | $_am_arg:* )
-      break ;;
-    * )
-      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
-  esac
-done
-echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
-$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-	 X"$_am_arg" : 'X\(//\)[^/]' \| \
-	 X"$_am_arg" : 'X\(//\)$' \| \
-	 X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$_am_arg" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)[^/].*/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\).*/{
-	    s//\1/
-	    q
-	  }
-	  s/.*/./; q'`/stamp-h$_am_stamp_count
- ;;
-
-  :C)  { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
-$as_echo "$as_me: executing $ac_file commands" >&6;}
- ;;
-  esac
-
-
-  case $ac_file$ac_mode in
-    "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
-  # Older Autoconf quotes --file arguments for eval, but not when files
-  # are listed without --file.  Let's play safe and only enable the eval
-  # if we detect the quoting.
-  # TODO: see whether this extra hack can be removed once we start
-  # requiring Autoconf 2.70 or later.
-  case $CONFIG_FILES in #(
-  *\'*) :
-    eval set x "$CONFIG_FILES" ;; #(
-  *) :
-    set x $CONFIG_FILES ;; #(
-  *) :
-     ;;
-esac
-  shift
-  # Used to flag and report bootstrapping failures.
-  am_rc=0
-  for am_mf
-  do
-    # Strip MF so we end up with the name of the file.
-    am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
-    # Check whether this is an Automake generated Makefile which includes
-    # dependency-tracking related rules and includes.
-    # Grep'ing the whole file directly is not great: AIX grep has a line
-    # limit of 2048, but all sed's we know have understand at least 4000.
-    sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
-      || continue
-    am_dirpart=`$as_dirname -- "$am_mf" ||
-$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-	 X"$am_mf" : 'X\(//\)[^/]' \| \
-	 X"$am_mf" : 'X\(//\)$' \| \
-	 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$am_mf" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)[^/].*/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\/\)$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\(\/\).*/{
-	    s//\1/
-	    q
-	  }
-	  s/.*/./; q'`
-    am_filepart=`$as_basename -- "$am_mf" ||
-$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
-	 X"$am_mf" : 'X\(//\)$' \| \
-	 X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X/"$am_mf" |
-    sed '/^.*\/\([^/][^/]*\)\/*$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\/\(\/\/\)$/{
-	    s//\1/
-	    q
-	  }
-	  /^X\/\(\/\).*/{
-	    s//\1/
-	    q
-	  }
-	  s/.*/./; q'`
-    { echo "$as_me:$LINENO: cd "$am_dirpart" \
-      && sed -e '/# am--include-marker/d' "$am_filepart" \
-        | $MAKE -f - am--depfiles" >&5
-   (cd "$am_dirpart" \
-      && sed -e '/# am--include-marker/d' "$am_filepart" \
-        | $MAKE -f - am--depfiles) >&5 2>&5
-   ac_status=$?
-   echo "$as_me:$LINENO: \$? = $ac_status" >&5
-   (exit $ac_status); } || am_rc=$?
-  done
-  if test $am_rc -ne 0; then
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "Something went wrong bootstrapping makefile fragments
-    for automatic dependency tracking.  Try re-running configure with the
-    '--disable-dependency-tracking' option to at least be able to build
-    the package (albeit without support for automatic dependency tracking).
-See \`config.log' for more details" "$LINENO" 5; }
-  fi
-  { am_dirpart=; unset am_dirpart;}
-  { am_filepart=; unset am_filepart;}
-  { am_mf=; unset am_mf;}
-  { am_rc=; unset am_rc;}
-  rm -f conftest-deps.mk
-}
- ;;
-    "libtool":C)
-
-    # See if we are running on zsh, and set the options that allow our
-    # commands through without removal of \ escapes.
-    if test -n "${ZSH_VERSION+set}"; then
-      setopt NO_GLOB_SUBST
-    fi
-
-    cfgfile=${ofile}T
-    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
-    $RM "$cfgfile"
-
-    cat <<_LT_EOF >> "$cfgfile"
-#! $SHELL
-# Generated automatically by $as_me ($PACKAGE) $VERSION
-# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
-# NOTE: Changes made to this file will be lost: look at ltmain.sh.
-
-# Provide generalized library-building support services.
-# Written by Gordon Matzigkeit, 1996
-
-# Copyright (C) 2014 Free Software Foundation, Inc.
-# This is free software; see the source for copying conditions.  There is NO
-# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-# GNU Libtool is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of of the License, or
-# (at your option) any later version.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program or library that is built
-# using GNU Libtool, you may include this file under the  same
-# distribution terms that you use for the rest of that program.
-#
-# GNU Libtool is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-
-# The names of the tagged configurations supported by this script.
-available_tags=''
-
-# Configured defaults for sys_lib_dlsearch_path munging.
-: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
-
-# ### BEGIN LIBTOOL CONFIG
-
-# A sed program that does not truncate output.
-SED=$lt_SED
-
-# Sed that helps us avoid accidentally triggering echo(1) options like -n.
-Xsed="\$SED -e 1s/^X//"
-
-# A grep program that handles long lines.
-GREP=$lt_GREP
-
-# An ERE matcher.
-EGREP=$lt_EGREP
-
-# A literal string matcher.
-FGREP=$lt_FGREP
-
-# Shell to use when invoking shell scripts.
-SHELL=$lt_SHELL
-
-# An echo program that protects backslashes.
-ECHO=$lt_ECHO
-
-# Which release of libtool.m4 was used?
-macro_version=$macro_version
-macro_revision=$macro_revision
-
-# Assembler program.
-AS=$lt_AS
-
-# DLL creation program.
-DLLTOOL=$lt_DLLTOOL
-
-# Object dumper program.
-OBJDUMP=$lt_OBJDUMP
-
-# Whether or not to build shared libraries.
-build_libtool_libs=$enable_shared
-
-# Whether or not to build static libraries.
-build_old_libs=$enable_static
-
-# What type of objects to build.
-pic_mode=$pic_mode
-
-# Whether or not to optimize for fast installation.
-fast_install=$enable_fast_install
-
-# Shared archive member basename,for filename based shared library versioning on AIX.
-shared_archive_member_spec=$shared_archive_member_spec
-
-# The PATH separator for the build system.
-PATH_SEPARATOR=$lt_PATH_SEPARATOR
-
-# The host system.
-host_alias=$host_alias
-host=$host
-host_os=$host_os
-
-# The build system.
-build_alias=$build_alias
-build=$build
-build_os=$build_os
-
-# A BSD- or MS-compatible name lister.
-NM=$lt_NM
-
-# Whether we need soft or hard links.
-LN_S=$lt_LN_S
-
-# What is the maximum length of a command?
-max_cmd_len=$max_cmd_len
-
-# Object file suffix (normally "o").
-objext=$ac_objext
-
-# Executable file suffix (normally "").
-exeext=$exeext
-
-# whether the shell understands "unset".
-lt_unset=$lt_unset
-
-# turn spaces into newlines.
-SP2NL=$lt_lt_SP2NL
-
-# turn newlines into spaces.
-NL2SP=$lt_lt_NL2SP
-
-# convert \$build file names to \$host format.
-to_host_file_cmd=$lt_cv_to_host_file_cmd
-
-# convert \$build files to toolchain format.
-to_tool_file_cmd=$lt_cv_to_tool_file_cmd
-
-# Method to check whether dependent libraries are shared objects.
-deplibs_check_method=$lt_deplibs_check_method
-
-# Command to use when deplibs_check_method = "file_magic".
-file_magic_cmd=$lt_file_magic_cmd
-
-# How to find potential files when deplibs_check_method = "file_magic".
-file_magic_glob=$lt_file_magic_glob
-
-# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
-want_nocaseglob=$lt_want_nocaseglob
-
-# Command to associate shared and link libraries.
-sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
-
-# The archiver.
-AR=$lt_AR
-
-# Flags to create an archive.
-AR_FLAGS=$lt_AR_FLAGS
-
-# How to feed a file listing to the archiver.
-archiver_list_spec=$lt_archiver_list_spec
-
-# A symbol stripping program.
-STRIP=$lt_STRIP
-
-# Commands used to install an old-style archive.
-RANLIB=$lt_RANLIB
-old_postinstall_cmds=$lt_old_postinstall_cmds
-old_postuninstall_cmds=$lt_old_postuninstall_cmds
-
-# Whether to use a lock for old archive extraction.
-lock_old_archive_extraction=$lock_old_archive_extraction
-
-# A C compiler.
-LTCC=$lt_CC
-
-# LTCC compiler flags.
-LTCFLAGS=$lt_CFLAGS
-
-# Take the output of nm and produce a listing of raw symbols and C names.
-global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
-
-# Transform the output of nm in a proper C declaration.
-global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
-
-# Transform the output of nm into a list of symbols to manually relocate.
-global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
-
-# Transform the output of nm in a C name address pair.
-global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
-
-# Transform the output of nm in a C name address pair when lib prefix is needed.
-global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
-
-# The name lister interface.
-nm_interface=$lt_lt_cv_nm_interface
-
-# Specify filename containing input files for \$NM.
-nm_file_list_spec=$lt_nm_file_list_spec
-
-# The root where to search for dependent libraries,and where our libraries should be installed.
-lt_sysroot=$lt_sysroot
-
-# Command to truncate a binary pipe.
-lt_truncate_bin=$lt_lt_cv_truncate_bin
-
-# The name of the directory that contains temporary libtool files.
-objdir=$objdir
-
-# Used to examine libraries when file_magic_cmd begins with "file".
-MAGIC_CMD=$MAGIC_CMD
-
-# Must we lock files when doing compilation?
-need_locks=$lt_need_locks
-
-# Manifest tool.
-MANIFEST_TOOL=$lt_MANIFEST_TOOL
-
-# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
-DSYMUTIL=$lt_DSYMUTIL
-
-# Tool to change global to local symbols on Mac OS X.
-NMEDIT=$lt_NMEDIT
-
-# Tool to manipulate fat objects and archives on Mac OS X.
-LIPO=$lt_LIPO
-
-# ldd/readelf like tool for Mach-O binaries on Mac OS X.
-OTOOL=$lt_OTOOL
-
-# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
-OTOOL64=$lt_OTOOL64
-
-# Old archive suffix (normally "a").
-libext=$libext
-
-# Shared library suffix (normally ".so").
-shrext_cmds=$lt_shrext_cmds
-
-# The commands to extract the exported symbol list from a shared archive.
-extract_expsyms_cmds=$lt_extract_expsyms_cmds
-
-# Variables whose values should be saved in libtool wrapper scripts and
-# restored at link time.
-variables_saved_for_relink=$lt_variables_saved_for_relink
-
-# Do we need the "lib" prefix for modules?
-need_lib_prefix=$need_lib_prefix
-
-# Do we need a version for libraries?
-need_version=$need_version
-
-# Library versioning type.
-version_type=$version_type
-
-# Shared library runtime path variable.
-runpath_var=$runpath_var
-
-# Shared library path variable.
-shlibpath_var=$shlibpath_var
-
-# Is shlibpath searched before the hard-coded library search path?
-shlibpath_overrides_runpath=$shlibpath_overrides_runpath
-
-# Format of library name prefix.
-libname_spec=$lt_libname_spec
-
-# List of archive names.  First name is the real one, the rest are links.
-# The last name is the one that the linker finds with -lNAME
-library_names_spec=$lt_library_names_spec
-
-# The coded name of the library, if different from the real name.
-soname_spec=$lt_soname_spec
-
-# Permission mode override for installation of shared libraries.
-install_override_mode=$lt_install_override_mode
-
-# Command to use after installation of a shared archive.
-postinstall_cmds=$lt_postinstall_cmds
-
-# Command to use after uninstallation of a shared archive.
-postuninstall_cmds=$lt_postuninstall_cmds
-
-# Commands used to finish a libtool library installation in a directory.
-finish_cmds=$lt_finish_cmds
-
-# As "finish_cmds", except a single script fragment to be evaled but
-# not shown.
-finish_eval=$lt_finish_eval
-
-# Whether we should hardcode library paths into libraries.
-hardcode_into_libs=$hardcode_into_libs
-
-# Compile-time system search path for libraries.
-sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
-
-# Detected run-time system search path for libraries.
-sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path
-
-# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
-configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path
-
-# Whether dlopen is supported.
-dlopen_support=$enable_dlopen
-
-# Whether dlopen of programs is supported.
-dlopen_self=$enable_dlopen_self
-
-# Whether dlopen of statically linked programs is supported.
-dlopen_self_static=$enable_dlopen_self_static
-
-# Commands to strip libraries.
-old_striplib=$lt_old_striplib
-striplib=$lt_striplib
-
-
-# The linker used to build libraries.
-LD=$lt_LD
-
-# How to create reloadable object files.
-reload_flag=$lt_reload_flag
-reload_cmds=$lt_reload_cmds
-
-# Commands used to build an old-style archive.
-old_archive_cmds=$lt_old_archive_cmds
-
-# A language specific compiler.
-CC=$lt_compiler
-
-# Is the compiler the GNU compiler?
-with_gcc=$GCC
-
-# Compiler flag to turn off builtin functions.
-no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
-
-# Additional compiler flags for building library objects.
-pic_flag=$lt_lt_prog_compiler_pic
-
-# How to pass a linker flag through the compiler.
-wl=$lt_lt_prog_compiler_wl
-
-# Compiler flag to prevent dynamic linking.
-link_static_flag=$lt_lt_prog_compiler_static
-
-# Does compiler simultaneously support -c and -o options?
-compiler_c_o=$lt_lt_cv_prog_compiler_c_o
-
-# Whether or not to add -lc for building shared libraries.
-build_libtool_need_lc=$archive_cmds_need_lc
-
-# Whether or not to disallow shared libs when runtime libs are static.
-allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
-
-# Compiler flag to allow reflexive dlopens.
-export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
-
-# Compiler flag to generate shared objects directly from archives.
-whole_archive_flag_spec=$lt_whole_archive_flag_spec
-
-# Whether the compiler copes with passing no objects directly.
-compiler_needs_object=$lt_compiler_needs_object
-
-# Create an old-style archive from a shared archive.
-old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
-
-# Create a temporary old-style archive to link instead of a shared archive.
-old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
-
-# Commands used to build a shared archive.
-archive_cmds=$lt_archive_cmds
-archive_expsym_cmds=$lt_archive_expsym_cmds
-
-# Commands used to build a loadable module if different from building
-# a shared archive.
-module_cmds=$lt_module_cmds
-module_expsym_cmds=$lt_module_expsym_cmds
-
-# Whether we are building with GNU ld or not.
-with_gnu_ld=$lt_with_gnu_ld
-
-# Flag that allows shared libraries with undefined symbols to be built.
-allow_undefined_flag=$lt_allow_undefined_flag
-
-# Flag that enforces no undefined symbols.
-no_undefined_flag=$lt_no_undefined_flag
-
-# Flag to hardcode \$libdir into a binary during linking.
-# This must work even if \$libdir does not exist
-hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
-
-# Whether we need a single "-rpath" flag with a separated argument.
-hardcode_libdir_separator=$lt_hardcode_libdir_separator
-
-# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
-# DIR into the resulting binary.
-hardcode_direct=$hardcode_direct
-
-# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
-# DIR into the resulting binary and the resulting library dependency is
-# "absolute",i.e impossible to change by setting \$shlibpath_var if the
-# library is relocated.
-hardcode_direct_absolute=$hardcode_direct_absolute
-
-# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
-# into the resulting binary.
-hardcode_minus_L=$hardcode_minus_L
-
-# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
-# into the resulting binary.
-hardcode_shlibpath_var=$hardcode_shlibpath_var
-
-# Set to "yes" if building a shared library automatically hardcodes DIR
-# into the library and all subsequent libraries and executables linked
-# against it.
-hardcode_automatic=$hardcode_automatic
-
-# Set to yes if linker adds runtime paths of dependent libraries
-# to runtime path list.
-inherit_rpath=$inherit_rpath
-
-# Whether libtool must link a program against all its dependency libraries.
-link_all_deplibs=$link_all_deplibs
-
-# Set to "yes" if exported symbols are required.
-always_export_symbols=$always_export_symbols
-
-# The commands to list exported symbols.
-export_symbols_cmds=$lt_export_symbols_cmds
-
-# Symbols that should not be listed in the preloaded symbols.
-exclude_expsyms=$lt_exclude_expsyms
-
-# Symbols that must always be exported.
-include_expsyms=$lt_include_expsyms
-
-# Commands necessary for linking programs (against libraries) with templates.
-prelink_cmds=$lt_prelink_cmds
-
-# Commands necessary for finishing linking programs.
-postlink_cmds=$lt_postlink_cmds
-
-# Specify filename containing input files.
-file_list_spec=$lt_file_list_spec
-
-# How to hardcode a shared library path into an executable.
-hardcode_action=$hardcode_action
-
-# ### END LIBTOOL CONFIG
-
-_LT_EOF
-
-    cat <<'_LT_EOF' >> "$cfgfile"
-
-# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
-
-# func_munge_path_list VARIABLE PATH
-# -----------------------------------
-# VARIABLE is name of variable containing _space_ separated list of
-# directories to be munged by the contents of PATH, which is string
-# having a format:
-# "DIR[:DIR]:"
-#       string "DIR[ DIR]" will be prepended to VARIABLE
-# ":DIR[:DIR]"
-#       string "DIR[ DIR]" will be appended to VARIABLE
-# "DIRP[:DIRP]::[DIRA:]DIRA"
-#       string "DIRP[ DIRP]" will be prepended to VARIABLE and string
-#       "DIRA[ DIRA]" will be appended to VARIABLE
-# "DIR[:DIR]"
-#       VARIABLE will be replaced by "DIR[ DIR]"
-func_munge_path_list ()
-{
-    case x$2 in
-    x)
-        ;;
-    *:)
-        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
-        ;;
-    x:*)
-        eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
-        ;;
-    *::*)
-        eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
-        eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
-        ;;
-    *)
-        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
-        ;;
-    esac
-}
-
-
-# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
-func_cc_basename ()
-{
-    for cc_temp in $*""; do
-      case $cc_temp in
-        compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
-        distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
-        \-*) ;;
-        *) break;;
-      esac
-    done
-    func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
-}
-
-
-# ### END FUNCTIONS SHARED WITH CONFIGURE
-
-_LT_EOF
-
-  case $host_os in
-  aix3*)
-    cat <<\_LT_EOF >> "$cfgfile"
-# AIX sometimes has problems with the GCC collect2 program.  For some
-# reason, if we set the COLLECT_NAMES environment variable, the problems
-# vanish in a puff of smoke.
-if test set != "${COLLECT_NAMES+set}"; then
-  COLLECT_NAMES=
-  export COLLECT_NAMES
-fi
-_LT_EOF
-    ;;
-  esac
-
-
-ltmain=$ac_aux_dir/ltmain.sh
-
-
-  # We use sed instead of cat because bash on DJGPP gets confused if
-  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
-  # text mode, it properly converts lines to CR/LF.  This bash problem
-  # is reportedly fixed, but why not run on old versions too?
-  sed '$q' "$ltmain" >> "$cfgfile" \
-     || (rm -f "$cfgfile"; exit 1)
-
-   mv -f "$cfgfile" "$ofile" ||
-    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
-  chmod +x "$ofile"
-
- ;;
-    "libpng-config":F) chmod +x libpng-config ;;
-
-  esac
-done # for ac_tag
-
-
-as_fn_exit 0
diff --git a/libs/libpng-src/contrib/libtests/.deps/.dirstamp b/libs/libpng-src/contrib/libtests/.deps/.dirstamp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/libs/libpng-src/contrib/libtests/.deps/pngimage.Po b/libs/libpng-src/contrib/libtests/.deps/pngimage.Po
deleted file mode 100644
index b338ccbd7b4988311a273cddf527128b5d09df2e..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/libtests/.deps/pngimage.Po
+++ /dev/null
@@ -1,112 +0,0 @@
-contrib/libtests/pngimage.o: contrib/libtests/pngimage.c \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/assert.h config.h \
- F:/lpng1637/png.h F:/lpng1637/pnglibconf.h F:/lpng1637/pngconf.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/assert.h:
-
-config.h:
-
-F:/lpng1637/png.h:
-
-F:/lpng1637/pnglibconf.h:
-
-F:/lpng1637/pngconf.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h:
diff --git a/libs/libpng-src/contrib/libtests/.deps/pngstest.Po b/libs/libpng-src/contrib/libtests/.deps/pngstest.Po
deleted file mode 100644
index f4fb40c1b1c414caf3c016ef81302240f99f4d28..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/libtests/.deps/pngstest.Po
+++ /dev/null
@@ -1,111 +0,0 @@
-contrib/libtests/pngstest.o: contrib/libtests/pngstest.c \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/ctype.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/math.h config.h \
- F:/lpng1637/png.h F:/lpng1637/pnglibconf.h F:/lpng1637/pngconf.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h \
- contrib/libtests/../tools/sRGB.h contrib/libtests/pngstest-errors.h
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/ctype.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/math.h:
-
-config.h:
-
-F:/lpng1637/png.h:
-
-F:/lpng1637/pnglibconf.h:
-
-F:/lpng1637/pngconf.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h:
-
-contrib/libtests/../tools/sRGB.h:
-
-contrib/libtests/pngstest-errors.h:
diff --git a/libs/libpng-src/contrib/libtests/.deps/pngunknown.Po b/libs/libpng-src/contrib/libtests/.deps/pngunknown.Po
deleted file mode 100644
index 5f746e311e100c86f8e3ca73b29f1b3c99e984ef..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/libtests/.deps/pngunknown.Po
+++ /dev/null
@@ -1,95 +0,0 @@
-contrib/libtests/pngunknown.o: contrib/libtests/pngunknown.c \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h F:/lpng1637/png.h \
- F:/lpng1637/pnglibconf.h F:/lpng1637/pngconf.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h:
-
-F:/lpng1637/png.h:
-
-F:/lpng1637/pnglibconf.h:
-
-F:/lpng1637/pngconf.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h:
diff --git a/libs/libpng-src/contrib/libtests/.deps/pngvalid.Po b/libs/libpng-src/contrib/libtests/.deps/pngvalid.Po
deleted file mode 100644
index 23b00108e3774a3ebca8706ccb2e7b0fb7faa4b7..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/libtests/.deps/pngvalid.Po
+++ /dev/null
@@ -1,150 +0,0 @@
-contrib/libtests/pngvalid.o: contrib/libtests/pngvalid.c \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/signal.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_signal.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h config.h \
- F:/lpng1637/png.h F:/lpng1637/pnglibconf.h F:/lpng1637/pngconf.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h \
- C:/msys64/mingw64/include/zlib.h C:/msys64/mingw64/include/zconf.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/float.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/float.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/math.h \
- contrib/libtests/../visupng/cexcept.h
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/signal.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_signal.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-config.h:
-
-F:/lpng1637/png.h:
-
-F:/lpng1637/pnglibconf.h:
-
-F:/lpng1637/pngconf.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h:
-
-C:/msys64/mingw64/include/zlib.h:
-
-C:/msys64/mingw64/include/zconf.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/float.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/float.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/math.h:
-
-contrib/libtests/../visupng/cexcept.h:
diff --git a/libs/libpng-src/contrib/libtests/.deps/timepng.Po b/libs/libpng-src/contrib/libtests/.deps/timepng.Po
deleted file mode 100644
index 096e7382b515b989f2f45b6cadd0b5c7a31b9280..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/libtests/.deps/timepng.Po
+++ /dev/null
@@ -1,100 +0,0 @@
-contrib/libtests/timepng.o: contrib/libtests/timepng.c \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h config.h \
- F:/lpng1637/png.h F:/lpng1637/pnglibconf.h F:/lpng1637/pngconf.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h:
-
-config.h:
-
-F:/lpng1637/png.h:
-
-F:/lpng1637/pnglibconf.h:
-
-F:/lpng1637/pngconf.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h:
diff --git a/libs/libpng-src/contrib/libtests/.dirstamp b/libs/libpng-src/contrib/libtests/.dirstamp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/libs/libpng-src/contrib/tools/.deps/.dirstamp b/libs/libpng-src/contrib/tools/.deps/.dirstamp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/libs/libpng-src/contrib/tools/.deps/png-fix-itxt.Po b/libs/libpng-src/contrib/tools/.deps/png-fix-itxt.Po
deleted file mode 100644
index 4913d70efac730feb7b3f39859fb68e95ef503d5..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/tools/.deps/png-fix-itxt.Po
+++ /dev/null
@@ -1,93 +0,0 @@
-contrib/tools/png-fix-itxt.o: contrib/tools/png-fix-itxt.c \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h \
- C:/msys64/mingw64/include/zlib.h C:/msys64/mingw64/include/zconf.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-C:/msys64/mingw64/include/zlib.h:
-
-C:/msys64/mingw64/include/zconf.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h:
diff --git a/libs/libpng-src/contrib/tools/.deps/pngcp.Po b/libs/libpng-src/contrib/tools/.deps/pngcp.Po
deleted file mode 100644
index 42514750f50f5bd2f7d40110793596765466d5ed..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/tools/.deps/pngcp.Po
+++ /dev/null
@@ -1,144 +0,0 @@
-contrib/tools/pngcp.o: contrib/tools/pngcp.c pnglibconf.h config.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h \
- F:/lpng1637/png.h F:/lpng1637/pngconf.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/assert.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/stat.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stat64.h \
- C:/msys64/mingw64/include/zlib.h C:/msys64/mingw64/include/zconf.h
-
-pnglibconf.h:
-
-config.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-F:/lpng1637/png.h:
-
-F:/lpng1637/pngconf.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/assert.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/stat.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stat64.h:
-
-C:/msys64/mingw64/include/zlib.h:
-
-C:/msys64/mingw64/include/zconf.h:
diff --git a/libs/libpng-src/contrib/tools/.deps/pngfix.Po b/libs/libpng-src/contrib/tools/.deps/pngfix.Po
deleted file mode 100644
index 3b2068ff9eaac7158d570e4312acfe577da5747d..0000000000000000000000000000000000000000
--- a/libs/libpng-src/contrib/tools/.deps/pngfix.Po
+++ /dev/null
@@ -1,139 +0,0 @@
-contrib/tools/pngfix.o: contrib/tools/pngfix.c \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/ctype.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/assert.h F:/lpng1637/png.h \
- F:/lpng1637/pnglibconf.h F:/lpng1637/pngconf.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h \
- C:/msys64/mingw64/include/zlib.h C:/msys64/mingw64/include/zconf.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h \
- C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h \
- C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdlib.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_mac.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_secapi.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/vadefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_directx.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sdks/_mingw_ddk.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_wstdlib.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/limits.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include-fixed/syslimits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/limits.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/crtdefs.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdlib_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/malloc.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdio.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_off_t.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/swprintf.inl:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/stdio_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/string.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/string_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/ctype.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/errno.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/assert.h:
-
-F:/lpng1637/png.h:
-
-F:/lpng1637/pnglibconf.h:
-
-F:/lpng1637/pngconf.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stddef.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/setjmp.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/time.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_timeval.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/timeb.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sec_api/sys/timeb_s.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_time.h:
-
-C:/msys64/mingw64/include/zlib.h:
-
-C:/msys64/mingw64/include/zconf.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/sys/types.h:
-
-C:/msys64/mingw64/lib/gcc/x86_64-w64-mingw32/9.1.0/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/_mingw_stdarg.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/io.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/process.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/corecrt_startup.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/getopt.h:
-
-C:/msys64/mingw64/x86_64-w64-mingw32/include/pthread_unistd.h:
diff --git a/libs/libpng-src/contrib/tools/.dirstamp b/libs/libpng-src/contrib/tools/.dirstamp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/libs/libpng-src/intel/.dirstamp b/libs/libpng-src/intel/.dirstamp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/libs/libpng-src/libpng-config.in b/libs/libpng-src/libpng-config.in
new file mode 100644
index 0000000000000000000000000000000000000000..69bf8e33f0b16b73f331ec6cc98b40cddb58826c
--- /dev/null
+++ b/libs/libpng-src/libpng-config.in
@@ -0,0 +1,127 @@
+#! /bin/sh
+
+# libpng-config
+# provides configuration info for libpng.
+
+# Copyright (C) 2002, 2004, 2006, 2007 Glenn Randers-Pehrson
+
+# This code is released under the libpng license.
+# For conditions of distribution and use, see the disclaimer
+# and license in png.h
+
+# Modeled after libxml-config.
+
+version="@PNGLIB_VERSION@"
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+libdir="@libdir@"
+includedir="@includedir@/libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@"
+libs="-lpng@PNGLIB_MAJOR@@PNGLIB_MINOR@"
+all_libs="-lpng@PNGLIB_MAJOR@@PNGLIB_MINOR@ @LIBS@"
+I_opts="-I${includedir}"
+L_opts="-L${libdir}"
+R_opts=""
+cppflags=""
+ccopts=""
+ldopts=""
+
+usage()
+{
+    cat <<EOF
+Usage: $0 [OPTION] ...
+
+Known values for OPTION are:
+
+  --prefix        print libpng prefix
+  --libdir        print path to directory containing library
+  --libs          print library linking information
+  --ccopts        print compiler options
+  --cppflags      print pre-processor flags
+  --cflags        print preprocessor flags, I_opts, and compiler options
+  --I_opts        print "-I" include options
+  --L_opts        print linker "-L" flags for dynamic linking
+  --R_opts        print dynamic linker "-R" or "-rpath" flags
+  --ldopts        print linker options
+  --ldflags       print linker flags (ldopts, L_opts, R_opts, and libs)
+  --static        revise subsequent outputs for static linking
+  --help          print this help and exit
+  --version       print version information
+EOF
+
+    exit $1
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+while test $# -gt 0; do
+    case "$1" in
+
+    --prefix)
+        echo ${prefix}
+        ;;
+
+    --version)
+        echo ${version}
+        exit 0
+        ;;
+
+    --help)
+        usage 0
+        ;;
+
+    --ccopts)
+        echo ${ccopts}
+        ;;
+
+    --cppflags)
+        echo ${cppflags}
+        ;;
+
+    --cflags)
+        echo ${I_opts} ${cppflags} ${ccopts}
+        ;;
+
+    --libdir)
+        echo ${libdir}
+        ;;
+
+    --libs)
+        echo ${libs}
+        ;;
+
+    --I_opts)
+        echo ${I_opts}
+        ;;
+
+    --L_opts)
+        echo ${L_opts}
+        ;;
+
+    --R_opts)
+        echo ${R_opts}
+        ;;
+
+    --ldopts)
+        echo ${ldopts}
+        ;;
+
+    --ldflags)
+        echo ${ldopts} ${L_opts} ${R_opts} ${libs}
+        ;;
+
+    --static)
+        R_opts=""
+        libs=${all_libs}
+        ;;
+
+    *)
+        usage
+        exit 1
+        ;;
+    esac
+    shift
+done
+
+exit 0
diff --git a/libs/libpng-src/libpng.pc b/libs/libpng-src/libpng.pc
deleted file mode 100644
index a7176ca278d1de411e503335e3124e452e61c287..0000000000000000000000000000000000000000
--- a/libs/libpng-src/libpng.pc
+++ /dev/null
@@ -1,12 +0,0 @@
-prefix=/mingw32
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include/libpng16
-
-Name: libpng
-Description: Loads and saves PNG files
-Version: 1.6.37
-Requires: zlib
-Libs: -L${libdir} -lpng16
-Libs.private: -lz 
-Cflags: -I${includedir}
diff --git a/libs/libpng-src/libpng.pc.in b/libs/libpng-src/libpng.pc.in
new file mode 100644
index 0000000000000000000000000000000000000000..9708e9af2be1259147c6244c07345f39222cbea4
--- /dev/null
+++ b/libs/libpng-src/libpng.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@
+
+Name: libpng
+Description: Loads and saves PNG files
+Version: @PNGLIB_VERSION@
+Requires: zlib
+Libs: -L${libdir} -lpng@PNGLIB_MAJOR@@PNGLIB_MINOR@
+Libs.private: @LIBS@
+Cflags: -I${includedir}
diff --git a/libs/libpng-src/libtool b/libs/libpng-src/libtool
deleted file mode 100644
index 8c0474ada45fc4d4077b761d24e770841202c7e8..0000000000000000000000000000000000000000
--- a/libs/libpng-src/libtool
+++ /dev/null
@@ -1,11661 +0,0 @@
-#! /bin/sh
-# Generated automatically by config.status (libpng) 1.6.37
-# Libtool was configured on host SteelT-PC:
-# NOTE: Changes made to this file will be lost: look at ltmain.sh.
-
-# Provide generalized library-building support services.
-# Written by Gordon Matzigkeit, 1996
-
-# Copyright (C) 2014 Free Software Foundation, Inc.
-# This is free software; see the source for copying conditions.  There is NO
-# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-# GNU Libtool is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of of the License, or
-# (at your option) any later version.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program or library that is built
-# using GNU Libtool, you may include this file under the  same
-# distribution terms that you use for the rest of that program.
-#
-# GNU Libtool is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-
-# The names of the tagged configurations supported by this script.
-available_tags=''
-
-# Configured defaults for sys_lib_dlsearch_path munging.
-: ${LT_SYS_LIBRARY_PATH=""}
-
-# ### BEGIN LIBTOOL CONFIG
-
-# A sed program that does not truncate output.
-SED="/usr/bin/sed"
-
-# Sed that helps us avoid accidentally triggering echo(1) options like -n.
-Xsed="$SED -e 1s/^X//"
-
-# A grep program that handles long lines.
-GREP="/usr/bin/grep"
-
-# An ERE matcher.
-EGREP="/usr/bin/grep -E"
-
-# A literal string matcher.
-FGREP="/usr/bin/grep -F"
-
-# Shell to use when invoking shell scripts.
-SHELL="/bin/sh"
-
-# An echo program that protects backslashes.
-ECHO="printf %s\\n"
-
-# Which release of libtool.m4 was used?
-macro_version=2.4.6
-macro_revision=2.4.6
-
-# Assembler program.
-AS="as"
-
-# DLL creation program.
-DLLTOOL="dlltool"
-
-# Object dumper program.
-OBJDUMP="objdump"
-
-# Whether or not to build shared libraries.
-build_libtool_libs=yes
-
-# Whether or not to build static libraries.
-build_old_libs=yes
-
-# What type of objects to build.
-pic_mode=default
-
-# Whether or not to optimize for fast installation.
-fast_install=needless
-
-# Shared archive member basename,for filename based shared library versioning on AIX.
-shared_archive_member_spec=
-
-# The PATH separator for the build system.
-PATH_SEPARATOR=":"
-
-# The host system.
-host_alias=
-host=i686-w64-mingw32
-host_os=mingw32
-
-# The build system.
-build_alias=i686-w64-mingw32
-build=i686-w64-mingw32
-build_os=mingw32
-
-# A BSD- or MS-compatible name lister.
-NM="/mingw32/bin/nm -B"
-
-# Whether we need soft or hard links.
-LN_S="cp -pR"
-
-# What is the maximum length of a command?
-max_cmd_len=8192
-
-# Object file suffix (normally "o").
-objext=o
-
-# Executable file suffix (normally "").
-exeext=
-
-# whether the shell understands "unset".
-lt_unset=unset
-
-# turn spaces into newlines.
-SP2NL="tr \\040 \\012"
-
-# turn newlines into spaces.
-NL2SP="tr \\015\\012 \\040\\040"
-
-# convert $build file names to $host format.
-to_host_file_cmd=func_convert_file_msys_to_w32
-
-# convert $build files to toolchain format.
-to_tool_file_cmd=func_convert_file_msys_to_w32
-
-# Method to check whether dependent libraries are shared objects.
-deplibs_check_method="file_magic ^x86 archive import|^x86 DLL"
-
-# Command to use when deplibs_check_method = "file_magic".
-file_magic_cmd="func_win32_libid"
-
-# How to find potential files when deplibs_check_method = "file_magic".
-file_magic_glob=""
-
-# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
-want_nocaseglob="yes"
-
-# Command to associate shared and link libraries.
-sharedlib_from_linklib_cmd="func_cygming_dll_for_implib"
-
-# The archiver.
-AR="ar"
-
-# Flags to create an archive.
-AR_FLAGS="cru"
-
-# How to feed a file listing to the archiver.
-archiver_list_spec="@"
-
-# A symbol stripping program.
-STRIP="strip"
-
-# Commands used to install an old-style archive.
-RANLIB="ranlib"
-old_postinstall_cmds="chmod 644 \$oldlib~\$RANLIB \$tool_oldlib"
-old_postuninstall_cmds=""
-
-# Whether to use a lock for old archive extraction.
-lock_old_archive_extraction=no
-
-# A C compiler.
-LTCC="gcc"
-
-# LTCC compiler flags.
-LTCFLAGS="-g -O2"
-
-# Take the output of nm and produce a listing of raw symbols and C names.
-global_symbol_pipe="sed -n -e 's/^.*[	 ]\\([ABCDGIRSTW][ABCDGIRSTW]*\\)[	 ][	 ]*_\\([_A-Za-z][_A-Za-z0-9]*\\)\\{0,1\\}\$/\\1 _\\2 \\2/p' | sed '/ __gnu_lto/d'"
-
-# Transform the output of nm in a proper C declaration.
-global_symbol_to_cdecl="sed -n -e 's/^T .* \\(.*\\)\$/extern int \\1();/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(.*\\)\$/extern char \\1;/p'"
-
-# Transform the output of nm into a list of symbols to manually relocate.
-global_symbol_to_import=""
-
-# Transform the output of nm in a C name address pair.
-global_symbol_to_c_name_address="sed -n -e 's/^: \\(.*\\) .*\$/  {\"\\1\", (void *) 0},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(.*\\)\$/  {\"\\1\", (void *) \\&\\1},/p'"
-
-# Transform the output of nm in a C name address pair when lib prefix is needed.
-global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \\(.*\\) .*\$/  {\"\\1\", (void *) 0},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(lib.*\\)\$/  {\"\\1\", (void *) \\&\\1},/p' -e 's/^[ABCDGIRSTW][ABCDGIRSTW]* .* \\(.*\\)\$/  {\"lib\\1\", (void *) \\&\\1},/p'"
-
-# The name lister interface.
-nm_interface="BSD nm"
-
-# Specify filename containing input files for $NM.
-nm_file_list_spec="@"
-
-# The root where to search for dependent libraries,and where our libraries should be installed.
-lt_sysroot=
-
-# Command to truncate a binary pipe.
-lt_truncate_bin="/usr/bin/sed -e 4q"
-
-# The name of the directory that contains temporary libtool files.
-objdir=.libs
-
-# Used to examine libraries when file_magic_cmd begins with "file".
-MAGIC_CMD=file
-
-# Must we lock files when doing compilation?
-need_locks="no"
-
-# Manifest tool.
-MANIFEST_TOOL=":"
-
-# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
-DSYMUTIL=""
-
-# Tool to change global to local symbols on Mac OS X.
-NMEDIT=""
-
-# Tool to manipulate fat objects and archives on Mac OS X.
-LIPO=""
-
-# ldd/readelf like tool for Mach-O binaries on Mac OS X.
-OTOOL=""
-
-# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
-OTOOL64=""
-
-# Old archive suffix (normally "a").
-libext=a
-
-# Shared library suffix (normally ".so").
-shrext_cmds=".dll"
-
-# The commands to extract the exported symbol list from a shared archive.
-extract_expsyms_cmds=""
-
-# Variables whose values should be saved in libtool wrapper scripts and
-# restored at link time.
-variables_saved_for_relink="PATH PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
-
-# Do we need the "lib" prefix for modules?
-need_lib_prefix=no
-
-# Do we need a version for libraries?
-need_version=no
-
-# Library versioning type.
-version_type=windows
-
-# Shared library runtime path variable.
-runpath_var=LD_RUN_PATH
-
-# Shared library path variable.
-shlibpath_var=PATH
-
-# Is shlibpath searched before the hard-coded library search path?
-shlibpath_overrides_runpath=yes
-
-# Format of library name prefix.
-libname_spec="lib\$name"
-
-# List of archive names.  First name is the real one, the rest are links.
-# The last name is the one that the linker finds with -lNAME
-library_names_spec="\$libname.dll.a"
-
-# The coded name of the library, if different from the real name.
-soname_spec="\$libname\`echo \$release | \$SED -e s/[.]/-/g\`\$versuffix\$shared_ext"
-
-# Permission mode override for installation of shared libraries.
-install_override_mode=""
-
-# Command to use after installation of a shared archive.
-postinstall_cmds="base_file=\\\`basename \\\$file\\\`~
-      dlpath=\\\`\$SHELL 2>&1 -c '. \$dir/'\\\$base_file'i; echo \\\$dlname'\\\`~
-      dldir=\$destdir/\\\`dirname \\\$dlpath\\\`~
-      test -d \\\$dldir || mkdir -p \\\$dldir~
-      \$install_prog \$dir/\$dlname \\\$dldir/\$dlname~
-      chmod a+x \\\$dldir/\$dlname~
-      if test -n '\$stripme' && test -n '\$striplib'; then
-        eval '\$striplib \\\$dldir/\$dlname' || exit \\\$?;
-      fi"
-
-# Command to use after uninstallation of a shared archive.
-postuninstall_cmds="dldll=\\\`\$SHELL 2>&1 -c '. \$file; echo \\\$dlname'\\\`~
-      dlpath=\$dir/\\\$dldll~
-       \$RM \\\$dlpath"
-
-# Commands used to finish a libtool library installation in a directory.
-finish_cmds=""
-
-# As "finish_cmds", except a single script fragment to be evaled but
-# not shown.
-finish_eval=""
-
-# Whether we should hardcode library paths into libraries.
-hardcode_into_libs=no
-
-# Compile-time system search path for libraries.
-sys_lib_search_path_spec="C:/msys64/mingw32/lib/gcc/i686-w64-mingw32/9.1.0 C:/msys64/mingw32/lib/gcc C:/msys64/mingw32/i686-w64-mingw32/lib C:/msys64/mingw32/lib "
-
-# Detected run-time system search path for libraries.
-sys_lib_dlsearch_path_spec="/lib /usr/lib"
-
-# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
-configure_time_lt_sys_library_path=""
-
-# Whether dlopen is supported.
-dlopen_support=unknown
-
-# Whether dlopen of programs is supported.
-dlopen_self=unknown
-
-# Whether dlopen of statically linked programs is supported.
-dlopen_self_static=unknown
-
-# Commands to strip libraries.
-old_striplib="strip --strip-debug"
-striplib="strip --strip-unneeded"
-
-
-# The linker used to build libraries.
-LD="C:/msys64/mingw32/i686-w64-mingw32/bin/ld.exe"
-
-# How to create reloadable object files.
-reload_flag=" -r"
-reload_cmds="\$LD\$reload_flag -o \$output\$reload_objs"
-
-# Commands used to build an old-style archive.
-old_archive_cmds="\$AR \$AR_FLAGS \$oldlib\$oldobjs~\$RANLIB \$tool_oldlib"
-
-# A language specific compiler.
-CC="gcc"
-
-# Is the compiler the GNU compiler?
-with_gcc=yes
-
-# Compiler flag to turn off builtin functions.
-no_builtin_flag=" -fno-builtin"
-
-# Additional compiler flags for building library objects.
-pic_flag=" -DDLL_EXPORT -DPIC"
-
-# How to pass a linker flag through the compiler.
-wl="-Wl,"
-
-# Compiler flag to prevent dynamic linking.
-link_static_flag="-static"
-
-# Does compiler simultaneously support -c and -o options?
-compiler_c_o="yes"
-
-# Whether or not to add -lc for building shared libraries.
-build_libtool_need_lc=yes
-
-# Whether or not to disallow shared libs when runtime libs are static.
-allow_libtool_libs_with_static_runtimes=yes
-
-# Compiler flag to allow reflexive dlopens.
-export_dynamic_flag_spec="\$wl--export-all-symbols"
-
-# Compiler flag to generate shared objects directly from archives.
-whole_archive_flag_spec="\$wl--whole-archive\$convenience \$wl--no-whole-archive"
-
-# Whether the compiler copes with passing no objects directly.
-compiler_needs_object="no"
-
-# Create an old-style archive from a shared archive.
-old_archive_from_new_cmds=""
-
-# Create a temporary old-style archive to link instead of a shared archive.
-old_archive_from_expsyms_cmds=""
-
-# Commands used to build a shared archive.
-archive_cmds="\$CC -shared \$libobjs \$deplibs \$compiler_flags -o \$output_objdir/\$soname \$wl--enable-auto-image-base -Xlinker --out-implib -Xlinker \$lib"
-archive_expsym_cmds="if   test DEF = \\\"\\\`\$SED -n     -e 's/^[	 ]*//'     -e '/^\\\\(;.*\\\\)*\$/d'     -e 's/^\\\\(EXPORTS\\\\|LIBRARY\\\\)\\\\([	 ].*\\\\)*\$/DEF/p'     -e q     \$export_symbols\\\`\\\" ; then
-          cp \$export_symbols \$output_objdir/\$soname.def;
-        else
-          echo EXPORTS > \$output_objdir/\$soname.def;
-          cat \$export_symbols >> \$output_objdir/\$soname.def;
-        fi~
-        \$CC -shared \$output_objdir/\$soname.def \$libobjs \$deplibs \$compiler_flags -o \$output_objdir/\$soname \$wl--enable-auto-image-base -Xlinker --out-implib -Xlinker \$lib"
-
-# Commands used to build a loadable module if different from building
-# a shared archive.
-module_cmds=""
-module_expsym_cmds=""
-
-# Whether we are building with GNU ld or not.
-with_gnu_ld="yes"
-
-# Flag that allows shared libraries with undefined symbols to be built.
-allow_undefined_flag="unsupported"
-
-# Flag that enforces no undefined symbols.
-no_undefined_flag=""
-
-# Flag to hardcode $libdir into a binary during linking.
-# This must work even if $libdir does not exist
-hardcode_libdir_flag_spec="-L\$libdir"
-
-# Whether we need a single "-rpath" flag with a separated argument.
-hardcode_libdir_separator=""
-
-# Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
-# DIR into the resulting binary.
-hardcode_direct=no
-
-# Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
-# DIR into the resulting binary and the resulting library dependency is
-# "absolute",i.e impossible to change by setting $shlibpath_var if the
-# library is relocated.
-hardcode_direct_absolute=no
-
-# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
-# into the resulting binary.
-hardcode_minus_L=no
-
-# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
-# into the resulting binary.
-hardcode_shlibpath_var=unsupported
-
-# Set to "yes" if building a shared library automatically hardcodes DIR
-# into the library and all subsequent libraries and executables linked
-# against it.
-hardcode_automatic=no
-
-# Set to yes if linker adds runtime paths of dependent libraries
-# to runtime path list.
-inherit_rpath=no
-
-# Whether libtool must link a program against all its dependency libraries.
-link_all_deplibs=unknown
-
-# Set to "yes" if exported symbols are required.
-always_export_symbols=no
-
-# The commands to list exported symbols.
-export_symbols_cmds="\$NM \$libobjs \$convenience | \$global_symbol_pipe | \$SED -e '/^[BCDGRS][ ]/s/.*[ ]\\\\([^ ]*\\\\)/\\\\1 DATA/;s/^.*[ ]__nm__\\\\([^ ]*\\\\)[ ][^ ]*/\\\\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //' | sort | uniq > \$export_symbols"
-
-# Symbols that should not be listed in the preloaded symbols.
-exclude_expsyms="[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname"
-
-# Symbols that must always be exported.
-include_expsyms=""
-
-# Commands necessary for linking programs (against libraries) with templates.
-prelink_cmds=""
-
-# Commands necessary for finishing linking programs.
-postlink_cmds=""
-
-# Specify filename containing input files.
-file_list_spec=""
-
-# How to hardcode a shared library path into an executable.
-hardcode_action=immediate
-
-# ### END LIBTOOL CONFIG
-
-
-# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
-
-# func_munge_path_list VARIABLE PATH
-# -----------------------------------
-# VARIABLE is name of variable containing _space_ separated list of
-# directories to be munged by the contents of PATH, which is string
-# having a format:
-# "DIR[:DIR]:"
-#       string "DIR[ DIR]" will be prepended to VARIABLE
-# ":DIR[:DIR]"
-#       string "DIR[ DIR]" will be appended to VARIABLE
-# "DIRP[:DIRP]::[DIRA:]DIRA"
-#       string "DIRP[ DIRP]" will be prepended to VARIABLE and string
-#       "DIRA[ DIRA]" will be appended to VARIABLE
-# "DIR[:DIR]"
-#       VARIABLE will be replaced by "DIR[ DIR]"
-func_munge_path_list ()
-{
-    case x$2 in
-    x)
-        ;;
-    *:)
-        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
-        ;;
-    x:*)
-        eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
-        ;;
-    *::*)
-        eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
-        eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
-        ;;
-    *)
-        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
-        ;;
-    esac
-}
-
-
-# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
-func_cc_basename ()
-{
-    for cc_temp in $*""; do
-      case $cc_temp in
-        compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
-        distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
-        \-*) ;;
-        *) break;;
-      esac
-    done
-    func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
-}
-
-
-# ### END FUNCTIONS SHARED WITH CONFIGURE
-
-#! /bin/sh
-## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
-##               by inline-source v2014-01-03.01
-
-# libtool (GNU libtool) 2.4.6
-# Provide generalized library-building support services.
-# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
-
-# Copyright (C) 1996-2015 Free Software Foundation, Inc.
-# This is free software; see the source for copying conditions.  There is NO
-# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-# GNU Libtool is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# As a special exception to the GNU General Public License,
-# if you distribute this file as part of a program or library that
-# is built using GNU Libtool, you may include this file under the
-# same distribution terms that you use for the rest of that program.
-#
-# GNU Libtool is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-
-PROGRAM=libtool
-PACKAGE=libtool
-VERSION=2.4.6
-package_revision=2.4.6
-
-
-## ------ ##
-## Usage. ##
-## ------ ##
-
-# Run './libtool --help' for help with using this script from the
-# command line.
-
-
-## ------------------------------- ##
-## User overridable command paths. ##
-## ------------------------------- ##
-
-# After configure completes, it has a better idea of some of the
-# shell tools we need than the defaults used by the functions shared
-# with bootstrap, so set those here where they can still be over-
-# ridden by the user, but otherwise take precedence.
-
-: ${AUTOCONF="autoconf"}
-: ${AUTOMAKE="automake"}
-
-
-## -------------------------- ##
-## Source external libraries. ##
-## -------------------------- ##
-
-# Much of our low-level functionality needs to be sourced from external
-# libraries, which are installed to $pkgauxdir.
-
-# Set a version string for this script.
-scriptversion=2015-01-20.17; # UTC
-
-# General shell script boiler plate, and helper functions.
-# Written by Gary V. Vaughan, 2004
-
-# Copyright (C) 2004-2015 Free Software Foundation, Inc.
-# This is free software; see the source for copying conditions.  There is NO
-# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-
-# As a special exception to the GNU General Public License, if you distribute
-# this file as part of a program or library that is built using GNU Libtool,
-# you may include this file under the same distribution terms that you use
-# for the rest of that program.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-# Please report bugs or propose patches to gary@gnu.org.
-
-
-## ------ ##
-## Usage. ##
-## ------ ##
-
-# Evaluate this file near the top of your script to gain access to
-# the functions and variables defined here:
-#
-#   . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh
-#
-# If you need to override any of the default environment variable
-# settings, do that before evaluating this file.
-
-
-## -------------------- ##
-## Shell normalisation. ##
-## -------------------- ##
-
-# Some shells need a little help to be as Bourne compatible as possible.
-# Before doing anything else, make sure all that help has been provided!
-
-DUALCASE=1; export DUALCASE # for MKS sh
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
-  emulate sh
-  NULLCMD=:
-  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
-  # is contrary to our usage.  Disable this feature.
-  alias -g '${1+"$@"}'='"$@"'
-  setopt NO_GLOB_SUBST
-else
-  case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac
-fi
-
-# NLS nuisances: We save the old values in case they are required later.
-_G_user_locale=
-_G_safe_locale=
-for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
-do
-  eval "if test set = \"\${$_G_var+set}\"; then
-          save_$_G_var=\$$_G_var
-          $_G_var=C
-	  export $_G_var
-	  _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\"
-	  _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\"
-	fi"
-done
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-# Make sure IFS has a sensible default
-sp=' '
-nl='
-'
-IFS="$sp	$nl"
-
-# There are apparently some retarded systems that use ';' as a PATH separator!
-if test "${PATH_SEPARATOR+set}" != set; then
-  PATH_SEPARATOR=:
-  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
-    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
-      PATH_SEPARATOR=';'
-  }
-fi
-
-
-
-## ------------------------- ##
-## Locate command utilities. ##
-## ------------------------- ##
-
-
-# func_executable_p FILE
-# ----------------------
-# Check that FILE is an executable regular file.
-func_executable_p ()
-{
-    test -f "$1" && test -x "$1"
-}
-
-
-# func_path_progs PROGS_LIST CHECK_FUNC [PATH]
-# --------------------------------------------
-# Search for either a program that responds to --version with output
-# containing "GNU", or else returned by CHECK_FUNC otherwise, by
-# trying all the directories in PATH with each of the elements of
-# PROGS_LIST.
-#
-# CHECK_FUNC should accept the path to a candidate program, and
-# set $func_check_prog_result if it truncates its output less than
-# $_G_path_prog_max characters.
-func_path_progs ()
-{
-    _G_progs_list=$1
-    _G_check_func=$2
-    _G_PATH=${3-"$PATH"}
-
-    _G_path_prog_max=0
-    _G_path_prog_found=false
-    _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:}
-    for _G_dir in $_G_PATH; do
-      IFS=$_G_save_IFS
-      test -z "$_G_dir" && _G_dir=.
-      for _G_prog_name in $_G_progs_list; do
-        for _exeext in '' .EXE; do
-          _G_path_prog=$_G_dir/$_G_prog_name$_exeext
-          func_executable_p "$_G_path_prog" || continue
-          case `"$_G_path_prog" --version 2>&1` in
-            *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;;
-            *)     $_G_check_func $_G_path_prog
-		   func_path_progs_result=$func_check_prog_result
-		   ;;
-          esac
-          $_G_path_prog_found && break 3
-        done
-      done
-    done
-    IFS=$_G_save_IFS
-    test -z "$func_path_progs_result" && {
-      echo "no acceptable sed could be found in \$PATH" >&2
-      exit 1
-    }
-}
-
-
-# We want to be able to use the functions in this file before configure
-# has figured out where the best binaries are kept, which means we have
-# to search for them ourselves - except when the results are already set
-# where we skip the searches.
-
-# Unless the user overrides by setting SED, search the path for either GNU
-# sed, or the sed that truncates its output the least.
-test -z "$SED" && {
-  _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
-  for _G_i in 1 2 3 4 5 6 7; do
-    _G_sed_script=$_G_sed_script$nl$_G_sed_script
-  done
-  echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed
-  _G_sed_script=
-
-  func_check_prog_sed ()
-  {
-    _G_path_prog=$1
-
-    _G_count=0
-    printf 0123456789 >conftest.in
-    while :
-    do
-      cat conftest.in conftest.in >conftest.tmp
-      mv conftest.tmp conftest.in
-      cp conftest.in conftest.nl
-      echo '' >> conftest.nl
-      "$_G_path_prog" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break
-      diff conftest.out conftest.nl >/dev/null 2>&1 || break
-      _G_count=`expr $_G_count + 1`
-      if test "$_G_count" -gt "$_G_path_prog_max"; then
-        # Best one so far, save it but keep looking for a better one
-        func_check_prog_result=$_G_path_prog
-        _G_path_prog_max=$_G_count
-      fi
-      # 10*(2^10) chars as input seems more than enough
-      test 10 -lt "$_G_count" && break
-    done
-    rm -f conftest.in conftest.tmp conftest.nl conftest.out
-  }
-
-  func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin
-  rm -f conftest.sed
-  SED=$func_path_progs_result
-}
-
-
-# Unless the user overrides by setting GREP, search the path for either GNU
-# grep, or the grep that truncates its output the least.
-test -z "$GREP" && {
-  func_check_prog_grep ()
-  {
-    _G_path_prog=$1
-
-    _G_count=0
-    _G_path_prog_max=0
-    printf 0123456789 >conftest.in
-    while :
-    do
-      cat conftest.in conftest.in >conftest.tmp
-      mv conftest.tmp conftest.in
-      cp conftest.in conftest.nl
-      echo 'GREP' >> conftest.nl
-      "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break
-      diff conftest.out conftest.nl >/dev/null 2>&1 || break
-      _G_count=`expr $_G_count + 1`
-      if test "$_G_count" -gt "$_G_path_prog_max"; then
-        # Best one so far, save it but keep looking for a better one
-        func_check_prog_result=$_G_path_prog
-        _G_path_prog_max=$_G_count
-      fi
-      # 10*(2^10) chars as input seems more than enough
-      test 10 -lt "$_G_count" && break
-    done
-    rm -f conftest.in conftest.tmp conftest.nl conftest.out
-  }
-
-  func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin
-  GREP=$func_path_progs_result
-}
-
-
-## ------------------------------- ##
-## User overridable command paths. ##
-## ------------------------------- ##
-
-# All uppercase variable names are used for environment variables.  These
-# variables can be overridden by the user before calling a script that
-# uses them if a suitable command of that name is not already available
-# in the command search PATH.
-
-: ${CP="cp -f"}
-: ${ECHO="printf %s\n"}
-: ${EGREP="$GREP -E"}
-: ${FGREP="$GREP -F"}
-: ${LN_S="ln -s"}
-: ${MAKE="make"}
-: ${MKDIR="mkdir"}
-: ${MV="mv -f"}
-: ${RM="rm -f"}
-: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
-
-
-## -------------------- ##
-## Useful sed snippets. ##
-## -------------------- ##
-
-sed_dirname='s|/[^/]*$||'
-sed_basename='s|^.*/||'
-
-# Sed substitution that helps us do robust quoting.  It backslashifies
-# metacharacters that are still active within double-quoted strings.
-sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
-
-# Same as above, but do not quote variable references.
-sed_double_quote_subst='s/\(["`\\]\)/\\\1/g'
-
-# Sed substitution that turns a string into a regex matching for the
-# string literally.
-sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g'
-
-# Sed substitution that converts a w32 file name or path
-# that contains forward slashes, into one that contains
-# (escaped) backslashes.  A very naive implementation.
-sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
-
-# Re-'\' parameter expansions in output of sed_double_quote_subst that
-# were '\'-ed in input to the same.  If an odd number of '\' preceded a
-# '$' in input to sed_double_quote_subst, that '$' was protected from
-# expansion.  Since each input '\' is now two '\'s, look for any number
-# of runs of four '\'s followed by two '\'s and then a '$'.  '\' that '$'.
-_G_bs='\\'
-_G_bs2='\\\\'
-_G_bs4='\\\\\\\\'
-_G_dollar='\$'
-sed_double_backslash="\
-  s/$_G_bs4/&\\
-/g
-  s/^$_G_bs2$_G_dollar/$_G_bs&/
-  s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g
-  s/\n//g"
-
-
-## ----------------- ##
-## Global variables. ##
-## ----------------- ##
-
-# Except for the global variables explicitly listed below, the following
-# functions in the '^func_' namespace, and the '^require_' namespace
-# variables initialised in the 'Resource management' section, sourcing
-# this file will not pollute your global namespace with anything
-# else. There's no portable way to scope variables in Bourne shell
-# though, so actually running these functions will sometimes place
-# results into a variable named after the function, and often use
-# temporary variables in the '^_G_' namespace. If you are careful to
-# avoid using those namespaces casually in your sourcing script, things
-# should continue to work as you expect. And, of course, you can freely
-# overwrite any of the functions or variables defined here before
-# calling anything to customize them.
-
-EXIT_SUCCESS=0
-EXIT_FAILURE=1
-EXIT_MISMATCH=63  # $? = 63 is used to indicate version mismatch to missing.
-EXIT_SKIP=77	  # $? = 77 is used to indicate a skipped test to automake.
-
-# Allow overriding, eg assuming that you follow the convention of
-# putting '$debug_cmd' at the start of all your functions, you can get
-# bash to show function call trace with:
-#
-#    debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
-debug_cmd=${debug_cmd-":"}
-exit_cmd=:
-
-# By convention, finish your script with:
-#
-#    exit $exit_status
-#
-# so that you can set exit_status to non-zero if you want to indicate
-# something went wrong during execution without actually bailing out at
-# the point of failure.
-exit_status=$EXIT_SUCCESS
-
-# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
-# is ksh but when the shell is invoked as "sh" and the current value of
-# the _XPG environment variable is not equal to 1 (one), the special
-# positional parameter $0, within a function call, is the name of the
-# function.
-progpath=$0
-
-# The name of this program.
-progname=`$ECHO "$progpath" |$SED "$sed_basename"`
-
-# Make sure we have an absolute progpath for reexecution:
-case $progpath in
-  [\\/]*|[A-Za-z]:\\*) ;;
-  *[\\/]*)
-     progdir=`$ECHO "$progpath" |$SED "$sed_dirname"`
-     progdir=`cd "$progdir" && pwd`
-     progpath=$progdir/$progname
-     ;;
-  *)
-     _G_IFS=$IFS
-     IFS=${PATH_SEPARATOR-:}
-     for progdir in $PATH; do
-       IFS=$_G_IFS
-       test -x "$progdir/$progname" && break
-     done
-     IFS=$_G_IFS
-     test -n "$progdir" || progdir=`pwd`
-     progpath=$progdir/$progname
-     ;;
-esac
-
-
-## ----------------- ##
-## Standard options. ##
-## ----------------- ##
-
-# The following options affect the operation of the functions defined
-# below, and should be set appropriately depending on run-time para-
-# meters passed on the command line.
-
-opt_dry_run=false
-opt_quiet=false
-opt_verbose=false
-
-# Categories 'all' and 'none' are always available.  Append any others
-# you will pass as the first argument to func_warning from your own
-# code.
-warning_categories=
-
-# By default, display warnings according to 'opt_warning_types'.  Set
-# 'warning_func'  to ':' to elide all warnings, or func_fatal_error to
-# treat the next displayed warning as a fatal error.
-warning_func=func_warn_and_continue
-
-# Set to 'all' to display all warnings, 'none' to suppress all
-# warnings, or a space delimited list of some subset of
-# 'warning_categories' to display only the listed warnings.
-opt_warning_types=all
-
-
-## -------------------- ##
-## Resource management. ##
-## -------------------- ##
-
-# This section contains definitions for functions that each ensure a
-# particular resource (a file, or a non-empty configuration variable for
-# example) is available, and if appropriate to extract default values
-# from pertinent package files. Call them using their associated
-# 'require_*' variable to ensure that they are executed, at most, once.
-#
-# It's entirely deliberate that calling these functions can set
-# variables that don't obey the namespace limitations obeyed by the rest
-# of this file, in order that that they be as useful as possible to
-# callers.
-
-
-# require_term_colors
-# -------------------
-# Allow display of bold text on terminals that support it.
-require_term_colors=func_require_term_colors
-func_require_term_colors ()
-{
-    $debug_cmd
-
-    test -t 1 && {
-      # COLORTERM and USE_ANSI_COLORS environment variables take
-      # precedence, because most terminfo databases neglect to describe
-      # whether color sequences are supported.
-      test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"}
-
-      if test 1 = "$USE_ANSI_COLORS"; then
-        # Standard ANSI escape sequences
-        tc_reset=''
-        tc_bold='';   tc_standout=''
-        tc_red='';   tc_green=''
-        tc_blue='';  tc_cyan=''
-      else
-        # Otherwise trust the terminfo database after all.
-        test -n "`tput sgr0 2>/dev/null`" && {
-          tc_reset=`tput sgr0`
-          test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold`
-          tc_standout=$tc_bold
-          test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso`
-          test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1`
-          test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2`
-          test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4`
-          test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5`
-        }
-      fi
-    }
-
-    require_term_colors=:
-}
-
-
-## ----------------- ##
-## Function library. ##
-## ----------------- ##
-
-# This section contains a variety of useful functions to call in your
-# scripts. Take note of the portable wrappers for features provided by
-# some modern shells, which will fall back to slower equivalents on
-# less featureful shells.
-
-
-# func_append VAR VALUE
-# ---------------------
-# Append VALUE onto the existing contents of VAR.
-
-  # We should try to minimise forks, especially on Windows where they are
-  # unreasonably slow, so skip the feature probes when bash or zsh are
-  # being used:
-  if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then
-    : ${_G_HAVE_ARITH_OP="yes"}
-    : ${_G_HAVE_XSI_OPS="yes"}
-    # The += operator was introduced in bash 3.1
-    case $BASH_VERSION in
-      [12].* | 3.0 | 3.0*) ;;
-      *)
-        : ${_G_HAVE_PLUSEQ_OP="yes"}
-        ;;
-    esac
-  fi
-
-  # _G_HAVE_PLUSEQ_OP
-  # Can be empty, in which case the shell is probed, "yes" if += is
-  # useable or anything else if it does not work.
-  test -z "$_G_HAVE_PLUSEQ_OP" \
-    && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
-    && _G_HAVE_PLUSEQ_OP=yes
-
-if test yes = "$_G_HAVE_PLUSEQ_OP"
-then
-  # This is an XSI compatible shell, allowing a faster implementation...
-  eval 'func_append ()
-  {
-    $debug_cmd
-
-    eval "$1+=\$2"
-  }'
-else
-  # ...otherwise fall back to using expr, which is often a shell builtin.
-  func_append ()
-  {
-    $debug_cmd
-
-    eval "$1=\$$1\$2"
-  }
-fi
-
-
-# func_append_quoted VAR VALUE
-# ----------------------------
-# Quote VALUE and append to the end of shell variable VAR, separated
-# by a space.
-if test yes = "$_G_HAVE_PLUSEQ_OP"; then
-  eval 'func_append_quoted ()
-  {
-    $debug_cmd
-
-    func_quote_for_eval "$2"
-    eval "$1+=\\ \$func_quote_for_eval_result"
-  }'
-else
-  func_append_quoted ()
-  {
-    $debug_cmd
-
-    func_quote_for_eval "$2"
-    eval "$1=\$$1\\ \$func_quote_for_eval_result"
-  }
-fi
-
-
-# func_append_uniq VAR VALUE
-# --------------------------
-# Append unique VALUE onto the existing contents of VAR, assuming
-# entries are delimited by the first character of VALUE.  For example:
-#
-#   func_append_uniq options " --another-option option-argument"
-#
-# will only append to $options if " --another-option option-argument "
-# is not already present somewhere in $options already (note spaces at
-# each end implied by leading space in second argument).
-func_append_uniq ()
-{
-    $debug_cmd
-
-    eval _G_current_value='`$ECHO $'$1'`'
-    _G_delim=`expr "$2" : '\(.\)'`
-
-    case $_G_delim$_G_current_value$_G_delim in
-      *"$2$_G_delim"*) ;;
-      *) func_append "$@" ;;
-    esac
-}
-
-
-# func_arith TERM...
-# ------------------
-# Set func_arith_result to the result of evaluating TERMs.
-  test -z "$_G_HAVE_ARITH_OP" \
-    && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \
-    && _G_HAVE_ARITH_OP=yes
-
-if test yes = "$_G_HAVE_ARITH_OP"; then
-  eval 'func_arith ()
-  {
-    $debug_cmd
-
-    func_arith_result=$(( $* ))
-  }'
-else
-  func_arith ()
-  {
-    $debug_cmd
-
-    func_arith_result=`expr "$@"`
-  }
-fi
-
-
-# func_basename FILE
-# ------------------
-# Set func_basename_result to FILE with everything up to and including
-# the last / stripped.
-if test yes = "$_G_HAVE_XSI_OPS"; then
-  # If this shell supports suffix pattern removal, then use it to avoid
-  # forking. Hide the definitions single quotes in case the shell chokes
-  # on unsupported syntax...
-  _b='func_basename_result=${1##*/}'
-  _d='case $1 in
-        */*) func_dirname_result=${1%/*}$2 ;;
-        *  ) func_dirname_result=$3        ;;
-      esac'
-
-else
-  # ...otherwise fall back to using sed.
-  _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`'
-  _d='func_dirname_result=`$ECHO "$1"  |$SED "$sed_dirname"`
-      if test "X$func_dirname_result" = "X$1"; then
-        func_dirname_result=$3
-      else
-        func_append func_dirname_result "$2"
-      fi'
-fi
-
-eval 'func_basename ()
-{
-    $debug_cmd
-
-    '"$_b"'
-}'
-
-
-# func_dirname FILE APPEND NONDIR_REPLACEMENT
-# -------------------------------------------
-# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
-# otherwise set result to NONDIR_REPLACEMENT.
-eval 'func_dirname ()
-{
-    $debug_cmd
-
-    '"$_d"'
-}'
-
-
-# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT
-# --------------------------------------------------------
-# Perform func_basename and func_dirname in a single function
-# call:
-#   dirname:  Compute the dirname of FILE.  If nonempty,
-#             add APPEND to the result, otherwise set result
-#             to NONDIR_REPLACEMENT.
-#             value returned in "$func_dirname_result"
-#   basename: Compute filename of FILE.
-#             value retuned in "$func_basename_result"
-# For efficiency, we do not delegate to the functions above but instead
-# duplicate the functionality here.
-eval 'func_dirname_and_basename ()
-{
-    $debug_cmd
-
-    '"$_b"'
-    '"$_d"'
-}'
-
-
-# func_echo ARG...
-# ----------------
-# Echo program name prefixed message.
-func_echo ()
-{
-    $debug_cmd
-
-    _G_message=$*
-
-    func_echo_IFS=$IFS
-    IFS=$nl
-    for _G_line in $_G_message; do
-      IFS=$func_echo_IFS
-      $ECHO "$progname: $_G_line"
-    done
-    IFS=$func_echo_IFS
-}
-
-
-# func_echo_all ARG...
-# --------------------
-# Invoke $ECHO with all args, space-separated.
-func_echo_all ()
-{
-    $ECHO "$*"
-}
-
-
-# func_echo_infix_1 INFIX ARG...
-# ------------------------------
-# Echo program name, followed by INFIX on the first line, with any
-# additional lines not showing INFIX.
-func_echo_infix_1 ()
-{
-    $debug_cmd
-
-    $require_term_colors
-
-    _G_infix=$1; shift
-    _G_indent=$_G_infix
-    _G_prefix="$progname: $_G_infix: "
-    _G_message=$*
-
-    # Strip color escape sequences before counting printable length
-    for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan"
-    do
-      test -n "$_G_tc" && {
-        _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"`
-        _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"`
-      }
-    done
-    _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`"  " ## exclude from sc_prohibit_nested_quotes
-
-    func_echo_infix_1_IFS=$IFS
-    IFS=$nl
-    for _G_line in $_G_message; do
-      IFS=$func_echo_infix_1_IFS
-      $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2
-      _G_prefix=$_G_indent
-    done
-    IFS=$func_echo_infix_1_IFS
-}
-
-
-# func_error ARG...
-# -----------------
-# Echo program name prefixed message to standard error.
-func_error ()
-{
-    $debug_cmd
-
-    $require_term_colors
-
-    func_echo_infix_1 "  $tc_standout${tc_red}error$tc_reset" "$*" >&2
-}
-
-
-# func_fatal_error ARG...
-# -----------------------
-# Echo program name prefixed message to standard error, and exit.
-func_fatal_error ()
-{
-    $debug_cmd
-
-    func_error "$*"
-    exit $EXIT_FAILURE
-}
-
-
-# func_grep EXPRESSION FILENAME
-# -----------------------------
-# Check whether EXPRESSION matches any line of FILENAME, without output.
-func_grep ()
-{
-    $debug_cmd
-
-    $GREP "$1" "$2" >/dev/null 2>&1
-}
-
-
-# func_len STRING
-# ---------------
-# Set func_len_result to the length of STRING. STRING may not
-# start with a hyphen.
-  test -z "$_G_HAVE_XSI_OPS" \
-    && (eval 'x=a/b/c;
-      test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
-    && _G_HAVE_XSI_OPS=yes
-
-if test yes = "$_G_HAVE_XSI_OPS"; then
-  eval 'func_len ()
-  {
-    $debug_cmd
-
-    func_len_result=${#1}
-  }'
-else
-  func_len ()
-  {
-    $debug_cmd
-
-    func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
-  }
-fi
-
-
-# func_mkdir_p DIRECTORY-PATH
-# ---------------------------
-# Make sure the entire path to DIRECTORY-PATH is available.
-func_mkdir_p ()
-{
-    $debug_cmd
-
-    _G_directory_path=$1
-    _G_dir_list=
-
-    if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then
-
-      # Protect directory names starting with '-'
-      case $_G_directory_path in
-        -*) _G_directory_path=./$_G_directory_path ;;
-      esac
-
-      # While some portion of DIR does not yet exist...
-      while test ! -d "$_G_directory_path"; do
-        # ...make a list in topmost first order.  Use a colon delimited
-	# list incase some portion of path contains whitespace.
-        _G_dir_list=$_G_directory_path:$_G_dir_list
-
-        # If the last portion added has no slash in it, the list is done
-        case $_G_directory_path in */*) ;; *) break ;; esac
-
-        # ...otherwise throw away the child directory and loop
-        _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"`
-      done
-      _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'`
-
-      func_mkdir_p_IFS=$IFS; IFS=:
-      for _G_dir in $_G_dir_list; do
-	IFS=$func_mkdir_p_IFS
-        # mkdir can fail with a 'File exist' error if two processes
-        # try to create one of the directories concurrently.  Don't
-        # stop in that case!
-        $MKDIR "$_G_dir" 2>/dev/null || :
-      done
-      IFS=$func_mkdir_p_IFS
-
-      # Bail out if we (or some other process) failed to create a directory.
-      test -d "$_G_directory_path" || \
-        func_fatal_error "Failed to create '$1'"
-    fi
-}
-
-
-# func_mktempdir [BASENAME]
-# -------------------------
-# Make a temporary directory that won't clash with other running
-# libtool processes, and avoids race conditions if possible.  If
-# given, BASENAME is the basename for that directory.
-func_mktempdir ()
-{
-    $debug_cmd
-
-    _G_template=${TMPDIR-/tmp}/${1-$progname}
-
-    if test : = "$opt_dry_run"; then
-      # Return a directory name, but don't create it in dry-run mode
-      _G_tmpdir=$_G_template-$$
-    else
-
-      # If mktemp works, use that first and foremost
-      _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null`
-
-      if test ! -d "$_G_tmpdir"; then
-        # Failing that, at least try and use $RANDOM to avoid a race
-        _G_tmpdir=$_G_template-${RANDOM-0}$$
-
-        func_mktempdir_umask=`umask`
-        umask 0077
-        $MKDIR "$_G_tmpdir"
-        umask $func_mktempdir_umask
-      fi
-
-      # If we're not in dry-run mode, bomb out on failure
-      test -d "$_G_tmpdir" || \
-        func_fatal_error "cannot create temporary directory '$_G_tmpdir'"
-    fi
-
-    $ECHO "$_G_tmpdir"
-}
-
-
-# func_normal_abspath PATH
-# ------------------------
-# Remove doubled-up and trailing slashes, "." path components,
-# and cancel out any ".." path components in PATH after making
-# it an absolute path.
-func_normal_abspath ()
-{
-    $debug_cmd
-
-    # These SED scripts presuppose an absolute path with a trailing slash.
-    _G_pathcar='s|^/\([^/]*\).*$|\1|'
-    _G_pathcdr='s|^/[^/]*||'
-    _G_removedotparts=':dotsl
-		s|/\./|/|g
-		t dotsl
-		s|/\.$|/|'
-    _G_collapseslashes='s|/\{1,\}|/|g'
-    _G_finalslash='s|/*$|/|'
-
-    # Start from root dir and reassemble the path.
-    func_normal_abspath_result=
-    func_normal_abspath_tpath=$1
-    func_normal_abspath_altnamespace=
-    case $func_normal_abspath_tpath in
-      "")
-        # Empty path, that just means $cwd.
-        func_stripname '' '/' "`pwd`"
-        func_normal_abspath_result=$func_stripname_result
-        return
-        ;;
-      # The next three entries are used to spot a run of precisely
-      # two leading slashes without using negated character classes;
-      # we take advantage of case's first-match behaviour.
-      ///*)
-        # Unusual form of absolute path, do nothing.
-        ;;
-      //*)
-        # Not necessarily an ordinary path; POSIX reserves leading '//'
-        # and for example Cygwin uses it to access remote file shares
-        # over CIFS/SMB, so we conserve a leading double slash if found.
-        func_normal_abspath_altnamespace=/
-        ;;
-      /*)
-        # Absolute path, do nothing.
-        ;;
-      *)
-        # Relative path, prepend $cwd.
-        func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
-        ;;
-    esac
-
-    # Cancel out all the simple stuff to save iterations.  We also want
-    # the path to end with a slash for ease of parsing, so make sure
-    # there is one (and only one) here.
-    func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
-          -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"`
-    while :; do
-      # Processed it all yet?
-      if test / = "$func_normal_abspath_tpath"; then
-        # If we ascended to the root using ".." the result may be empty now.
-        if test -z "$func_normal_abspath_result"; then
-          func_normal_abspath_result=/
-        fi
-        break
-      fi
-      func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
-          -e "$_G_pathcar"`
-      func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
-          -e "$_G_pathcdr"`
-      # Figure out what to do with it
-      case $func_normal_abspath_tcomponent in
-        "")
-          # Trailing empty path component, ignore it.
-          ;;
-        ..)
-          # Parent dir; strip last assembled component from result.
-          func_dirname "$func_normal_abspath_result"
-          func_normal_abspath_result=$func_dirname_result
-          ;;
-        *)
-          # Actual path component, append it.
-          func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent"
-          ;;
-      esac
-    done
-    # Restore leading double-slash if one was found on entry.
-    func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
-}
-
-
-# func_notquiet ARG...
-# --------------------
-# Echo program name prefixed message only when not in quiet mode.
-func_notquiet ()
-{
-    $debug_cmd
-
-    $opt_quiet || func_echo ${1+"$@"}
-
-    # A bug in bash halts the script if the last line of a function
-    # fails when set -e is in force, so we need another command to
-    # work around that:
-    :
-}
-
-
-# func_relative_path SRCDIR DSTDIR
-# --------------------------------
-# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR.
-func_relative_path ()
-{
-    $debug_cmd
-
-    func_relative_path_result=
-    func_normal_abspath "$1"
-    func_relative_path_tlibdir=$func_normal_abspath_result
-    func_normal_abspath "$2"
-    func_relative_path_tbindir=$func_normal_abspath_result
-
-    # Ascend the tree starting from libdir
-    while :; do
-      # check if we have found a prefix of bindir
-      case $func_relative_path_tbindir in
-        $func_relative_path_tlibdir)
-          # found an exact match
-          func_relative_path_tcancelled=
-          break
-          ;;
-        $func_relative_path_tlibdir*)
-          # found a matching prefix
-          func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
-          func_relative_path_tcancelled=$func_stripname_result
-          if test -z "$func_relative_path_result"; then
-            func_relative_path_result=.
-          fi
-          break
-          ;;
-        *)
-          func_dirname $func_relative_path_tlibdir
-          func_relative_path_tlibdir=$func_dirname_result
-          if test -z "$func_relative_path_tlibdir"; then
-            # Have to descend all the way to the root!
-            func_relative_path_result=../$func_relative_path_result
-            func_relative_path_tcancelled=$func_relative_path_tbindir
-            break
-          fi
-          func_relative_path_result=../$func_relative_path_result
-          ;;
-      esac
-    done
-
-    # Now calculate path; take care to avoid doubling-up slashes.
-    func_stripname '' '/' "$func_relative_path_result"
-    func_relative_path_result=$func_stripname_result
-    func_stripname '/' '/' "$func_relative_path_tcancelled"
-    if test -n "$func_stripname_result"; then
-      func_append func_relative_path_result "/$func_stripname_result"
-    fi
-
-    # Normalisation. If bindir is libdir, return '.' else relative path.
-    if test -n "$func_relative_path_result"; then
-      func_stripname './' '' "$func_relative_path_result"
-      func_relative_path_result=$func_stripname_result
-    fi
-
-    test -n "$func_relative_path_result" || func_relative_path_result=.
-
-    :
-}
-
-
-# func_quote_for_eval ARG...
-# --------------------------
-# Aesthetically quote ARGs to be evaled later.
-# This function returns two values:
-#   i) func_quote_for_eval_result
-#      double-quoted, suitable for a subsequent eval
-#  ii) func_quote_for_eval_unquoted_result
-#      has all characters that are still active within double
-#      quotes backslashified.
-func_quote_for_eval ()
-{
-    $debug_cmd
-
-    func_quote_for_eval_unquoted_result=
-    func_quote_for_eval_result=
-    while test 0 -lt $#; do
-      case $1 in
-        *[\\\`\"\$]*)
-	  _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
-        *)
-          _G_unquoted_arg=$1 ;;
-      esac
-      if test -n "$func_quote_for_eval_unquoted_result"; then
-	func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
-      else
-        func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
-      fi
-
-      case $_G_unquoted_arg in
-        # Double-quote args containing shell metacharacters to delay
-        # word splitting, command substitution and variable expansion
-        # for a subsequent eval.
-        # Many Bourne shells cannot handle close brackets correctly
-        # in scan sets, so we specify it separately.
-        *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
-          _G_quoted_arg=\"$_G_unquoted_arg\"
-          ;;
-        *)
-          _G_quoted_arg=$_G_unquoted_arg
-	  ;;
-      esac
-
-      if test -n "$func_quote_for_eval_result"; then
-	func_append func_quote_for_eval_result " $_G_quoted_arg"
-      else
-        func_append func_quote_for_eval_result "$_G_quoted_arg"
-      fi
-      shift
-    done
-}
-
-
-# func_quote_for_expand ARG
-# -------------------------
-# Aesthetically quote ARG to be evaled later; same as above,
-# but do not quote variable references.
-func_quote_for_expand ()
-{
-    $debug_cmd
-
-    case $1 in
-      *[\\\`\"]*)
-	_G_arg=`$ECHO "$1" | $SED \
-	    -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
-      *)
-        _G_arg=$1 ;;
-    esac
-
-    case $_G_arg in
-      # Double-quote args containing shell metacharacters to delay
-      # word splitting and command substitution for a subsequent eval.
-      # Many Bourne shells cannot handle close brackets correctly
-      # in scan sets, so we specify it separately.
-      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
-        _G_arg=\"$_G_arg\"
-        ;;
-    esac
-
-    func_quote_for_expand_result=$_G_arg
-}
-
-
-# func_stripname PREFIX SUFFIX NAME
-# ---------------------------------
-# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result.
-# PREFIX and SUFFIX must not contain globbing or regex special
-# characters, hashes, percent signs, but SUFFIX may contain a leading
-# dot (in which case that matches only a dot).
-if test yes = "$_G_HAVE_XSI_OPS"; then
-  eval 'func_stripname ()
-  {
-    $debug_cmd
-
-    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
-    # positional parameters, so assign one to ordinary variable first.
-    func_stripname_result=$3
-    func_stripname_result=${func_stripname_result#"$1"}
-    func_stripname_result=${func_stripname_result%"$2"}
-  }'
-else
-  func_stripname ()
-  {
-    $debug_cmd
-
-    case $2 in
-      .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;;
-      *)  func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;;
-    esac
-  }
-fi
-
-
-# func_show_eval CMD [FAIL_EXP]
-# -----------------------------
-# Unless opt_quiet is true, then output CMD.  Then, if opt_dryrun is
-# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
-# is given, then evaluate it.
-func_show_eval ()
-{
-    $debug_cmd
-
-    _G_cmd=$1
-    _G_fail_exp=${2-':'}
-
-    func_quote_for_expand "$_G_cmd"
-    eval "func_notquiet $func_quote_for_expand_result"
-
-    $opt_dry_run || {
-      eval "$_G_cmd"
-      _G_status=$?
-      if test 0 -ne "$_G_status"; then
-	eval "(exit $_G_status); $_G_fail_exp"
-      fi
-    }
-}
-
-
-# func_show_eval_locale CMD [FAIL_EXP]
-# ------------------------------------
-# Unless opt_quiet is true, then output CMD.  Then, if opt_dryrun is
-# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
-# is given, then evaluate it.  Use the saved locale for evaluation.
-func_show_eval_locale ()
-{
-    $debug_cmd
-
-    _G_cmd=$1
-    _G_fail_exp=${2-':'}
-
-    $opt_quiet || {
-      func_quote_for_expand "$_G_cmd"
-      eval "func_echo $func_quote_for_expand_result"
-    }
-
-    $opt_dry_run || {
-      eval "$_G_user_locale
-	    $_G_cmd"
-      _G_status=$?
-      eval "$_G_safe_locale"
-      if test 0 -ne "$_G_status"; then
-	eval "(exit $_G_status); $_G_fail_exp"
-      fi
-    }
-}
-
-
-# func_tr_sh
-# ----------
-# Turn $1 into a string suitable for a shell variable name.
-# Result is stored in $func_tr_sh_result.  All characters
-# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
-# if $1 begins with a digit, a '_' is prepended as well.
-func_tr_sh ()
-{
-    $debug_cmd
-
-    case $1 in
-    [0-9]* | *[!a-zA-Z0-9_]*)
-      func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'`
-      ;;
-    * )
-      func_tr_sh_result=$1
-      ;;
-    esac
-}
-
-
-# func_verbose ARG...
-# -------------------
-# Echo program name prefixed message in verbose mode only.
-func_verbose ()
-{
-    $debug_cmd
-
-    $opt_verbose && func_echo "$*"
-
-    :
-}
-
-
-# func_warn_and_continue ARG...
-# -----------------------------
-# Echo program name prefixed warning message to standard error.
-func_warn_and_continue ()
-{
-    $debug_cmd
-
-    $require_term_colors
-
-    func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2
-}
-
-
-# func_warning CATEGORY ARG...
-# ----------------------------
-# Echo program name prefixed warning message to standard error. Warning
-# messages can be filtered according to CATEGORY, where this function
-# elides messages where CATEGORY is not listed in the global variable
-# 'opt_warning_types'.
-func_warning ()
-{
-    $debug_cmd
-
-    # CATEGORY must be in the warning_categories list!
-    case " $warning_categories " in
-      *" $1 "*) ;;
-      *) func_internal_error "invalid warning category '$1'" ;;
-    esac
-
-    _G_category=$1
-    shift
-
-    case " $opt_warning_types " in
-      *" $_G_category "*) $warning_func ${1+"$@"} ;;
-    esac
-}
-
-
-# func_sort_ver VER1 VER2
-# -----------------------
-# 'sort -V' is not generally available.
-# Note this deviates from the version comparison in automake
-# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
-# but this should suffice as we won't be specifying old
-# version formats or redundant trailing .0 in bootstrap.conf.
-# If we did want full compatibility then we should probably
-# use m4_version_compare from autoconf.
-func_sort_ver ()
-{
-    $debug_cmd
-
-    printf '%s\n%s\n' "$1" "$2" \
-      | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n
-}
-
-# func_lt_ver PREV CURR
-# ---------------------
-# Return true if PREV and CURR are in the correct order according to
-# func_sort_ver, otherwise false.  Use it like this:
-#
-#  func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..."
-func_lt_ver ()
-{
-    $debug_cmd
-
-    test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q`
-}
-
-
-# Local variables:
-# mode: shell-script
-# sh-indentation: 2
-# eval: (add-hook 'before-save-hook 'time-stamp)
-# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
-# time-stamp-time-zone: "UTC"
-# End:
-#! /bin/sh
-
-# Set a version string for this script.
-scriptversion=2014-01-07.03; # UTC
-
-# A portable, pluggable option parser for Bourne shell.
-# Written by Gary V. Vaughan, 2010
-
-# Copyright (C) 2010-2015 Free Software Foundation, Inc.
-# This is free software; see the source for copying conditions.  There is NO
-# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-# Please report bugs or propose patches to gary@gnu.org.
-
-
-## ------ ##
-## Usage. ##
-## ------ ##
-
-# This file is a library for parsing options in your shell scripts along
-# with assorted other useful supporting features that you can make use
-# of too.
-#
-# For the simplest scripts you might need only:
-#
-#   #!/bin/sh
-#   . relative/path/to/funclib.sh
-#   . relative/path/to/options-parser
-#   scriptversion=1.0
-#   func_options ${1+"$@"}
-#   eval set dummy "$func_options_result"; shift
-#   ...rest of your script...
-#
-# In order for the '--version' option to work, you will need to have a
-# suitably formatted comment like the one at the top of this file
-# starting with '# Written by ' and ending with '# warranty; '.
-#
-# For '-h' and '--help' to work, you will also need a one line
-# description of your script's purpose in a comment directly above the
-# '# Written by ' line, like the one at the top of this file.
-#
-# The default options also support '--debug', which will turn on shell
-# execution tracing (see the comment above debug_cmd below for another
-# use), and '--verbose' and the func_verbose function to allow your script
-# to display verbose messages only when your user has specified
-# '--verbose'.
-#
-# After sourcing this file, you can plug processing for additional
-# options by amending the variables from the 'Configuration' section
-# below, and following the instructions in the 'Option parsing'
-# section further down.
-
-## -------------- ##
-## Configuration. ##
-## -------------- ##
-
-# You should override these variables in your script after sourcing this
-# file so that they reflect the customisations you have added to the
-# option parser.
-
-# The usage line for option parsing errors and the start of '-h' and
-# '--help' output messages. You can embed shell variables for delayed
-# expansion at the time the message is displayed, but you will need to
-# quote other shell meta-characters carefully to prevent them being
-# expanded when the contents are evaled.
-usage='$progpath [OPTION]...'
-
-# Short help message in response to '-h' and '--help'.  Add to this or
-# override it after sourcing this library to reflect the full set of
-# options your script accepts.
-usage_message="\
-       --debug        enable verbose shell tracing
-   -W, --warnings=CATEGORY
-                      report the warnings falling in CATEGORY [all]
-   -v, --verbose      verbosely report processing
-       --version      print version information and exit
-   -h, --help         print short or long help message and exit
-"
-
-# Additional text appended to 'usage_message' in response to '--help'.
-long_help_message="
-Warning categories include:
-       'all'          show all warnings
-       'none'         turn off all the warnings
-       'error'        warnings are treated as fatal errors"
-
-# Help message printed before fatal option parsing errors.
-fatal_help="Try '\$progname --help' for more information."
-
-
-
-## ------------------------- ##
-## Hook function management. ##
-## ------------------------- ##
-
-# This section contains functions for adding, removing, and running hooks
-# to the main code.  A hook is just a named list of of function, that can
-# be run in order later on.
-
-# func_hookable FUNC_NAME
-# -----------------------
-# Declare that FUNC_NAME will run hooks added with
-# 'func_add_hook FUNC_NAME ...'.
-func_hookable ()
-{
-    $debug_cmd
-
-    func_append hookable_fns " $1"
-}
-
-
-# func_add_hook FUNC_NAME HOOK_FUNC
-# ---------------------------------
-# Request that FUNC_NAME call HOOK_FUNC before it returns.  FUNC_NAME must
-# first have been declared "hookable" by a call to 'func_hookable'.
-func_add_hook ()
-{
-    $debug_cmd
-
-    case " $hookable_fns " in
-      *" $1 "*) ;;
-      *) func_fatal_error "'$1' does not accept hook functions." ;;
-    esac
-
-    eval func_append ${1}_hooks '" $2"'
-}
-
-
-# func_remove_hook FUNC_NAME HOOK_FUNC
-# ------------------------------------
-# Remove HOOK_FUNC from the list of functions called by FUNC_NAME.
-func_remove_hook ()
-{
-    $debug_cmd
-
-    eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`'
-}
-
-
-# func_run_hooks FUNC_NAME [ARG]...
-# ---------------------------------
-# Run all hook functions registered to FUNC_NAME.
-# It is assumed that the list of hook functions contains nothing more
-# than a whitespace-delimited list of legal shell function names, and
-# no effort is wasted trying to catch shell meta-characters or preserve
-# whitespace.
-func_run_hooks ()
-{
-    $debug_cmd
-
-    case " $hookable_fns " in
-      *" $1 "*) ;;
-      *) func_fatal_error "'$1' does not support hook funcions.n" ;;
-    esac
-
-    eval _G_hook_fns=\$$1_hooks; shift
-
-    for _G_hook in $_G_hook_fns; do
-      eval $_G_hook '"$@"'
-
-      # store returned options list back into positional
-      # parameters for next 'cmd' execution.
-      eval _G_hook_result=\$${_G_hook}_result
-      eval set dummy "$_G_hook_result"; shift
-    done
-
-    func_quote_for_eval ${1+"$@"}
-    func_run_hooks_result=$func_quote_for_eval_result
-}
-
-
-
-## --------------- ##
-## Option parsing. ##
-## --------------- ##
-
-# In order to add your own option parsing hooks, you must accept the
-# full positional parameter list in your hook function, remove any
-# options that you action, and then pass back the remaining unprocessed
-# options in '<hooked_function_name>_result', escaped suitably for
-# 'eval'.  Like this:
-#
-#    my_options_prep ()
-#    {
-#        $debug_cmd
-#
-#        # Extend the existing usage message.
-#        usage_message=$usage_message'
-#      -s, --silent       don'\''t print informational messages
-#    '
-#
-#        func_quote_for_eval ${1+"$@"}
-#        my_options_prep_result=$func_quote_for_eval_result
-#    }
-#    func_add_hook func_options_prep my_options_prep
-#
-#
-#    my_silent_option ()
-#    {
-#        $debug_cmd
-#
-#        # Note that for efficiency, we parse as many options as we can
-#        # recognise in a loop before passing the remainder back to the
-#        # caller on the first unrecognised argument we encounter.
-#        while test $# -gt 0; do
-#          opt=$1; shift
-#          case $opt in
-#            --silent|-s) opt_silent=: ;;
-#            # Separate non-argument short options:
-#            -s*)         func_split_short_opt "$_G_opt"
-#                         set dummy "$func_split_short_opt_name" \
-#                             "-$func_split_short_opt_arg" ${1+"$@"}
-#                         shift
-#                         ;;
-#            *)            set dummy "$_G_opt" "$*"; shift; break ;;
-#          esac
-#        done
-#
-#        func_quote_for_eval ${1+"$@"}
-#        my_silent_option_result=$func_quote_for_eval_result
-#    }
-#    func_add_hook func_parse_options my_silent_option
-#
-#
-#    my_option_validation ()
-#    {
-#        $debug_cmd
-#
-#        $opt_silent && $opt_verbose && func_fatal_help "\
-#    '--silent' and '--verbose' options are mutually exclusive."
-#
-#        func_quote_for_eval ${1+"$@"}
-#        my_option_validation_result=$func_quote_for_eval_result
-#    }
-#    func_add_hook func_validate_options my_option_validation
-#
-# You'll alse need to manually amend $usage_message to reflect the extra
-# options you parse.  It's preferable to append if you can, so that
-# multiple option parsing hooks can be added safely.
-
-
-# func_options [ARG]...
-# ---------------------
-# All the functions called inside func_options are hookable. See the
-# individual implementations for details.
-func_hookable func_options
-func_options ()
-{
-    $debug_cmd
-
-    func_options_prep ${1+"$@"}
-    eval func_parse_options \
-        ${func_options_prep_result+"$func_options_prep_result"}
-    eval func_validate_options \
-        ${func_parse_options_result+"$func_parse_options_result"}
-
-    eval func_run_hooks func_options \
-        ${func_validate_options_result+"$func_validate_options_result"}
-
-    # save modified positional parameters for caller
-    func_options_result=$func_run_hooks_result
-}
-
-
-# func_options_prep [ARG]...
-# --------------------------
-# All initialisations required before starting the option parse loop.
-# Note that when calling hook functions, we pass through the list of
-# positional parameters.  If a hook function modifies that list, and
-# needs to propogate that back to rest of this script, then the complete
-# modified list must be put in 'func_run_hooks_result' before
-# returning.
-func_hookable func_options_prep
-func_options_prep ()
-{
-    $debug_cmd
-
-    # Option defaults:
-    opt_verbose=false
-    opt_warning_types=
-
-    func_run_hooks func_options_prep ${1+"$@"}
-
-    # save modified positional parameters for caller
-    func_options_prep_result=$func_run_hooks_result
-}
-
-
-# func_parse_options [ARG]...
-# ---------------------------
-# The main option parsing loop.
-func_hookable func_parse_options
-func_parse_options ()
-{
-    $debug_cmd
-
-    func_parse_options_result=
-
-    # this just eases exit handling
-    while test $# -gt 0; do
-      # Defer to hook functions for initial option parsing, so they
-      # get priority in the event of reusing an option name.
-      func_run_hooks func_parse_options ${1+"$@"}
-
-      # Adjust func_parse_options positional parameters to match
-      eval set dummy "$func_run_hooks_result"; shift
-
-      # Break out of the loop if we already parsed every option.
-      test $# -gt 0 || break
-
-      _G_opt=$1
-      shift
-      case $_G_opt in
-        --debug|-x)   debug_cmd='set -x'
-                      func_echo "enabling shell trace mode"
-                      $debug_cmd
-                      ;;
-
-        --no-warnings|--no-warning|--no-warn)
-                      set dummy --warnings none ${1+"$@"}
-                      shift
-		      ;;
-
-        --warnings|--warning|-W)
-                      test $# = 0 && func_missing_arg $_G_opt && break
-                      case " $warning_categories $1" in
-                        *" $1 "*)
-                          # trailing space prevents matching last $1 above
-                          func_append_uniq opt_warning_types " $1"
-                          ;;
-                        *all)
-                          opt_warning_types=$warning_categories
-                          ;;
-                        *none)
-                          opt_warning_types=none
-                          warning_func=:
-                          ;;
-                        *error)
-                          opt_warning_types=$warning_categories
-                          warning_func=func_fatal_error
-                          ;;
-                        *)
-                          func_fatal_error \
-                             "unsupported warning category: '$1'"
-                          ;;
-                      esac
-                      shift
-                      ;;
-
-        --verbose|-v) opt_verbose=: ;;
-        --version)    func_version ;;
-        -\?|-h)       func_usage ;;
-        --help)       func_help ;;
-
-	# Separate optargs to long options (plugins may need this):
-	--*=*)        func_split_equals "$_G_opt"
-	              set dummy "$func_split_equals_lhs" \
-                          "$func_split_equals_rhs" ${1+"$@"}
-                      shift
-                      ;;
-
-       # Separate optargs to short options:
-        -W*)
-                      func_split_short_opt "$_G_opt"
-                      set dummy "$func_split_short_opt_name" \
-                          "$func_split_short_opt_arg" ${1+"$@"}
-                      shift
-                      ;;
-
-        # Separate non-argument short options:
-        -\?*|-h*|-v*|-x*)
-                      func_split_short_opt "$_G_opt"
-                      set dummy "$func_split_short_opt_name" \
-                          "-$func_split_short_opt_arg" ${1+"$@"}
-                      shift
-                      ;;
-
-        --)           break ;;
-        -*)           func_fatal_help "unrecognised option: '$_G_opt'" ;;
-        *)            set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
-      esac
-    done
-
-    # save modified positional parameters for caller
-    func_quote_for_eval ${1+"$@"}
-    func_parse_options_result=$func_quote_for_eval_result
-}
-
-
-# func_validate_options [ARG]...
-# ------------------------------
-# Perform any sanity checks on option settings and/or unconsumed
-# arguments.
-func_hookable func_validate_options
-func_validate_options ()
-{
-    $debug_cmd
-
-    # Display all warnings if -W was not given.
-    test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
-
-    func_run_hooks func_validate_options ${1+"$@"}
-
-    # Bail if the options were screwed!
-    $exit_cmd $EXIT_FAILURE
-
-    # save modified positional parameters for caller
-    func_validate_options_result=$func_run_hooks_result
-}
-
-
-
-## ----------------- ##
-## Helper functions. ##
-## ----------------- ##
-
-# This section contains the helper functions used by the rest of the
-# hookable option parser framework in ascii-betical order.
-
-
-# func_fatal_help ARG...
-# ----------------------
-# Echo program name prefixed message to standard error, followed by
-# a help hint, and exit.
-func_fatal_help ()
-{
-    $debug_cmd
-
-    eval \$ECHO \""Usage: $usage"\"
-    eval \$ECHO \""$fatal_help"\"
-    func_error ${1+"$@"}
-    exit $EXIT_FAILURE
-}
-
-
-# func_help
-# ---------
-# Echo long help message to standard output and exit.
-func_help ()
-{
-    $debug_cmd
-
-    func_usage_message
-    $ECHO "$long_help_message"
-    exit 0
-}
-
-
-# func_missing_arg ARGNAME
-# ------------------------
-# Echo program name prefixed message to standard error and set global
-# exit_cmd.
-func_missing_arg ()
-{
-    $debug_cmd
-
-    func_error "Missing argument for '$1'."
-    exit_cmd=exit
-}
-
-
-# func_split_equals STRING
-# ------------------------
-# Set func_split_equals_lhs and func_split_equals_rhs shell variables after
-# splitting STRING at the '=' sign.
-test -z "$_G_HAVE_XSI_OPS" \
-    && (eval 'x=a/b/c;
-      test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
-    && _G_HAVE_XSI_OPS=yes
-
-if test yes = "$_G_HAVE_XSI_OPS"
-then
-  # This is an XSI compatible shell, allowing a faster implementation...
-  eval 'func_split_equals ()
-  {
-      $debug_cmd
-
-      func_split_equals_lhs=${1%%=*}
-      func_split_equals_rhs=${1#*=}
-      test "x$func_split_equals_lhs" = "x$1" \
-        && func_split_equals_rhs=
-  }'
-else
-  # ...otherwise fall back to using expr, which is often a shell builtin.
-  func_split_equals ()
-  {
-      $debug_cmd
-
-      func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
-      func_split_equals_rhs=
-      test "x$func_split_equals_lhs" = "x$1" \
-        || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
-  }
-fi #func_split_equals
-
-
-# func_split_short_opt SHORTOPT
-# -----------------------------
-# Set func_split_short_opt_name and func_split_short_opt_arg shell
-# variables after splitting SHORTOPT after the 2nd character.
-if test yes = "$_G_HAVE_XSI_OPS"
-then
-  # This is an XSI compatible shell, allowing a faster implementation...
-  eval 'func_split_short_opt ()
-  {
-      $debug_cmd
-
-      func_split_short_opt_arg=${1#??}
-      func_split_short_opt_name=${1%"$func_split_short_opt_arg"}
-  }'
-else
-  # ...otherwise fall back to using expr, which is often a shell builtin.
-  func_split_short_opt ()
-  {
-      $debug_cmd
-
-      func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'`
-      func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
-  }
-fi #func_split_short_opt
-
-
-# func_usage
-# ----------
-# Echo short help message to standard output and exit.
-func_usage ()
-{
-    $debug_cmd
-
-    func_usage_message
-    $ECHO "Run '$progname --help |${PAGER-more}' for full usage"
-    exit 0
-}
-
-
-# func_usage_message
-# ------------------
-# Echo short help message to standard output.
-func_usage_message ()
-{
-    $debug_cmd
-
-    eval \$ECHO \""Usage: $usage"\"
-    echo
-    $SED -n 's|^# ||
-        /^Written by/{
-          x;p;x
-        }
-	h
-	/^Written by/q' < "$progpath"
-    echo
-    eval \$ECHO \""$usage_message"\"
-}
-
-
-# func_version
-# ------------
-# Echo version message to standard output and exit.
-func_version ()
-{
-    $debug_cmd
-
-    printf '%s\n' "$progname $scriptversion"
-    $SED -n '
-        /(C)/!b go
-        :more
-        /\./!{
-          N
-          s|\n# | |
-          b more
-        }
-        :go
-        /^# Written by /,/# warranty; / {
-          s|^# ||
-          s|^# *$||
-          s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
-          p
-        }
-        /^# Written by / {
-          s|^# ||
-          p
-        }
-        /^warranty; /q' < "$progpath"
-
-    exit $?
-}
-
-
-# Local variables:
-# mode: shell-script
-# sh-indentation: 2
-# eval: (add-hook 'before-save-hook 'time-stamp)
-# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
-# time-stamp-time-zone: "UTC"
-# End:
-
-# Set a version string.
-scriptversion='(GNU libtool) 2.4.6'
-
-
-# func_echo ARG...
-# ----------------
-# Libtool also displays the current mode in messages, so override
-# funclib.sh func_echo with this custom definition.
-func_echo ()
-{
-    $debug_cmd
-
-    _G_message=$*
-
-    func_echo_IFS=$IFS
-    IFS=$nl
-    for _G_line in $_G_message; do
-      IFS=$func_echo_IFS
-      $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line"
-    done
-    IFS=$func_echo_IFS
-}
-
-
-# func_warning ARG...
-# -------------------
-# Libtool warnings are not categorized, so override funclib.sh
-# func_warning with this simpler definition.
-func_warning ()
-{
-    $debug_cmd
-
-    $warning_func ${1+"$@"}
-}
-
-
-## ---------------- ##
-## Options parsing. ##
-## ---------------- ##
-
-# Hook in the functions to make sure our own options are parsed during
-# the option parsing loop.
-
-usage='$progpath [OPTION]... [MODE-ARG]...'
-
-# Short help message in response to '-h'.
-usage_message="Options:
-       --config             show all configuration variables
-       --debug              enable verbose shell tracing
-   -n, --dry-run            display commands without modifying any files
-       --features           display basic configuration information and exit
-       --mode=MODE          use operation mode MODE
-       --no-warnings        equivalent to '-Wnone'
-       --preserve-dup-deps  don't remove duplicate dependency libraries
-       --quiet, --silent    don't print informational messages
-       --tag=TAG            use configuration variables from tag TAG
-   -v, --verbose            print more informational messages than default
-       --version            print version information
-   -W, --warnings=CATEGORY  report the warnings falling in CATEGORY [all]
-   -h, --help, --help-all   print short, long, or detailed help message
-"
-
-# Additional text appended to 'usage_message' in response to '--help'.
-func_help ()
-{
-    $debug_cmd
-
-    func_usage_message
-    $ECHO "$long_help_message
-
-MODE must be one of the following:
-
-       clean           remove files from the build directory
-       compile         compile a source file into a libtool object
-       execute         automatically set library path, then run a program
-       finish          complete the installation of libtool libraries
-       install         install libraries or executables
-       link            create a library or an executable
-       uninstall       remove libraries from an installed directory
-
-MODE-ARGS vary depending on the MODE.  When passed as first option,
-'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that.
-Try '$progname --help --mode=MODE' for a more detailed description of MODE.
-
-When reporting a bug, please describe a test case to reproduce it and
-include the following information:
-
-       host-triplet:   $host
-       shell:          $SHELL
-       compiler:       $LTCC
-       compiler flags: $LTCFLAGS
-       linker:         $LD (gnu? $with_gnu_ld)
-       version:        $progname (GNU libtool) 2.4.6
-       automake:       `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
-       autoconf:       `($AUTOCONF --version) 2>/dev/null |$SED 1q`
-
-Report bugs to <bug-libtool@gnu.org>.
-GNU libtool home page: <http://www.gnu.org/software/libtool/>.
-General help using GNU software: <http://www.gnu.org/gethelp/>."
-    exit 0
-}
-
-
-# func_lo2o OBJECT-NAME
-# ---------------------
-# Transform OBJECT-NAME from a '.lo' suffix to the platform specific
-# object suffix.
-
-lo2o=s/\\.lo\$/.$objext/
-o2lo=s/\\.$objext\$/.lo/
-
-if test yes = "$_G_HAVE_XSI_OPS"; then
-  eval 'func_lo2o ()
-  {
-    case $1 in
-      *.lo) func_lo2o_result=${1%.lo}.$objext ;;
-      *   ) func_lo2o_result=$1               ;;
-    esac
-  }'
-
-  # func_xform LIBOBJ-OR-SOURCE
-  # ---------------------------
-  # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise)
-  # suffix to a '.lo' libtool-object suffix.
-  eval 'func_xform ()
-  {
-    func_xform_result=${1%.*}.lo
-  }'
-else
-  # ...otherwise fall back to using sed.
-  func_lo2o ()
-  {
-    func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"`
-  }
-
-  func_xform ()
-  {
-    func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'`
-  }
-fi
-
-
-# func_fatal_configuration ARG...
-# -------------------------------
-# Echo program name prefixed message to standard error, followed by
-# a configuration failure hint, and exit.
-func_fatal_configuration ()
-{
-    func__fatal_error ${1+"$@"} \
-      "See the $PACKAGE documentation for more information." \
-      "Fatal configuration error."
-}
-
-
-# func_config
-# -----------
-# Display the configuration for all the tags in this script.
-func_config ()
-{
-    re_begincf='^# ### BEGIN LIBTOOL'
-    re_endcf='^# ### END LIBTOOL'
-
-    # Default configuration.
-    $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
-
-    # Now print the configurations for the tags.
-    for tagname in $taglist; do
-      $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
-    done
-
-    exit $?
-}
-
-
-# func_features
-# -------------
-# Display the features supported by this script.
-func_features ()
-{
-    echo "host: $host"
-    if test yes = "$build_libtool_libs"; then
-      echo "enable shared libraries"
-    else
-      echo "disable shared libraries"
-    fi
-    if test yes = "$build_old_libs"; then
-      echo "enable static libraries"
-    else
-      echo "disable static libraries"
-    fi
-
-    exit $?
-}
-
-
-# func_enable_tag TAGNAME
-# -----------------------
-# Verify that TAGNAME is valid, and either flag an error and exit, or
-# enable the TAGNAME tag.  We also add TAGNAME to the global $taglist
-# variable here.
-func_enable_tag ()
-{
-    # Global variable:
-    tagname=$1
-
-    re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
-    re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
-    sed_extractcf=/$re_begincf/,/$re_endcf/p
-
-    # Validate tagname.
-    case $tagname in
-      *[!-_A-Za-z0-9,/]*)
-        func_fatal_error "invalid tag name: $tagname"
-        ;;
-    esac
-
-    # Don't test for the "default" C tag, as we know it's
-    # there but not specially marked.
-    case $tagname in
-        CC) ;;
-    *)
-        if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
-	  taglist="$taglist $tagname"
-
-	  # Evaluate the configuration.  Be careful to quote the path
-	  # and the sed script, to avoid splitting on whitespace, but
-	  # also don't use non-portable quotes within backquotes within
-	  # quotes we have to do it in 2 steps:
-	  extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
-	  eval "$extractedcf"
-        else
-	  func_error "ignoring unknown tag $tagname"
-        fi
-        ;;
-    esac
-}
-
-
-# func_check_version_match
-# ------------------------
-# Ensure that we are using m4 macros, and libtool script from the same
-# release of libtool.
-func_check_version_match ()
-{
-    if test "$package_revision" != "$macro_revision"; then
-      if test "$VERSION" != "$macro_version"; then
-        if test -z "$macro_version"; then
-          cat >&2 <<_LT_EOF
-$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
-$progname: definition of this LT_INIT comes from an older release.
-$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
-$progname: and run autoconf again.
-_LT_EOF
-        else
-          cat >&2 <<_LT_EOF
-$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
-$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
-$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
-$progname: and run autoconf again.
-_LT_EOF
-        fi
-      else
-        cat >&2 <<_LT_EOF
-$progname: Version mismatch error.  This is $PACKAGE $VERSION, revision $package_revision,
-$progname: but the definition of this LT_INIT comes from revision $macro_revision.
-$progname: You should recreate aclocal.m4 with macros from revision $package_revision
-$progname: of $PACKAGE $VERSION and run autoconf again.
-_LT_EOF
-      fi
-
-      exit $EXIT_MISMATCH
-    fi
-}
-
-
-# libtool_options_prep [ARG]...
-# -----------------------------
-# Preparation for options parsed by libtool.
-libtool_options_prep ()
-{
-    $debug_mode
-
-    # Option defaults:
-    opt_config=false
-    opt_dlopen=
-    opt_dry_run=false
-    opt_help=false
-    opt_mode=
-    opt_preserve_dup_deps=false
-    opt_quiet=false
-
-    nonopt=
-    preserve_args=
-
-    # Shorthand for --mode=foo, only valid as the first argument
-    case $1 in
-    clean|clea|cle|cl)
-      shift; set dummy --mode clean ${1+"$@"}; shift
-      ;;
-    compile|compil|compi|comp|com|co|c)
-      shift; set dummy --mode compile ${1+"$@"}; shift
-      ;;
-    execute|execut|execu|exec|exe|ex|e)
-      shift; set dummy --mode execute ${1+"$@"}; shift
-      ;;
-    finish|finis|fini|fin|fi|f)
-      shift; set dummy --mode finish ${1+"$@"}; shift
-      ;;
-    install|instal|insta|inst|ins|in|i)
-      shift; set dummy --mode install ${1+"$@"}; shift
-      ;;
-    link|lin|li|l)
-      shift; set dummy --mode link ${1+"$@"}; shift
-      ;;
-    uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
-      shift; set dummy --mode uninstall ${1+"$@"}; shift
-      ;;
-    esac
-
-    # Pass back the list of options.
-    func_quote_for_eval ${1+"$@"}
-    libtool_options_prep_result=$func_quote_for_eval_result
-}
-func_add_hook func_options_prep libtool_options_prep
-
-
-# libtool_parse_options [ARG]...
-# ---------------------------------
-# Provide handling for libtool specific options.
-libtool_parse_options ()
-{
-    $debug_cmd
-
-    # Perform our own loop to consume as many options as possible in
-    # each iteration.
-    while test $# -gt 0; do
-      _G_opt=$1
-      shift
-      case $_G_opt in
-        --dry-run|--dryrun|-n)
-                        opt_dry_run=:
-                        ;;
-
-        --config)       func_config ;;
-
-        --dlopen|-dlopen)
-                        opt_dlopen="${opt_dlopen+$opt_dlopen
-}$1"
-                        shift
-                        ;;
-
-        --preserve-dup-deps)
-                        opt_preserve_dup_deps=: ;;
-
-        --features)     func_features ;;
-
-        --finish)       set dummy --mode finish ${1+"$@"}; shift ;;
-
-        --help)         opt_help=: ;;
-
-        --help-all)     opt_help=': help-all' ;;
-
-        --mode)         test $# = 0 && func_missing_arg $_G_opt && break
-                        opt_mode=$1
-                        case $1 in
-                          # Valid mode arguments:
-                          clean|compile|execute|finish|install|link|relink|uninstall) ;;
-
-                          # Catch anything else as an error
-                          *) func_error "invalid argument for $_G_opt"
-                             exit_cmd=exit
-                             break
-                             ;;
-                        esac
-                        shift
-                        ;;
-
-        --no-silent|--no-quiet)
-                        opt_quiet=false
-                        func_append preserve_args " $_G_opt"
-                        ;;
-
-        --no-warnings|--no-warning|--no-warn)
-                        opt_warning=false
-                        func_append preserve_args " $_G_opt"
-                        ;;
-
-        --no-verbose)
-                        opt_verbose=false
-                        func_append preserve_args " $_G_opt"
-                        ;;
-
-        --silent|--quiet)
-                        opt_quiet=:
-                        opt_verbose=false
-                        func_append preserve_args " $_G_opt"
-                        ;;
-
-        --tag)          test $# = 0 && func_missing_arg $_G_opt && break
-                        opt_tag=$1
-                        func_append preserve_args " $_G_opt $1"
-                        func_enable_tag "$1"
-                        shift
-                        ;;
-
-        --verbose|-v)   opt_quiet=false
-                        opt_verbose=:
-                        func_append preserve_args " $_G_opt"
-                        ;;
-
-	# An option not handled by this hook function:
-        *)		set dummy "$_G_opt" ${1+"$@"};	shift; break  ;;
-      esac
-    done
-
-
-    # save modified positional parameters for caller
-    func_quote_for_eval ${1+"$@"}
-    libtool_parse_options_result=$func_quote_for_eval_result
-}
-func_add_hook func_parse_options libtool_parse_options
-
-
-
-# libtool_validate_options [ARG]...
-# ---------------------------------
-# Perform any sanity checks on option settings and/or unconsumed
-# arguments.
-libtool_validate_options ()
-{
-    # save first non-option argument
-    if test 0 -lt $#; then
-      nonopt=$1
-      shift
-    fi
-
-    # preserve --debug
-    test : = "$debug_cmd" || func_append preserve_args " --debug"
-
-    case $host in
-      # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
-      # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
-      *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
-        # don't eliminate duplications in $postdeps and $predeps
-        opt_duplicate_compiler_generated_deps=:
-        ;;
-      *)
-        opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
-        ;;
-    esac
-
-    $opt_help || {
-      # Sanity checks first:
-      func_check_version_match
-
-      test yes != "$build_libtool_libs" \
-        && test yes != "$build_old_libs" \
-        && func_fatal_configuration "not configured to build any kind of library"
-
-      # Darwin sucks
-      eval std_shrext=\"$shrext_cmds\"
-
-      # Only execute mode is allowed to have -dlopen flags.
-      if test -n "$opt_dlopen" && test execute != "$opt_mode"; then
-        func_error "unrecognized option '-dlopen'"
-        $ECHO "$help" 1>&2
-        exit $EXIT_FAILURE
-      fi
-
-      # Change the help message to a mode-specific one.
-      generic_help=$help
-      help="Try '$progname --help --mode=$opt_mode' for more information."
-    }
-
-    # Pass back the unparsed argument list
-    func_quote_for_eval ${1+"$@"}
-    libtool_validate_options_result=$func_quote_for_eval_result
-}
-func_add_hook func_validate_options libtool_validate_options
-
-
-# Process options as early as possible so that --help and --version
-# can return quickly.
-func_options ${1+"$@"}
-eval set dummy "$func_options_result"; shift
-
-
-
-## ----------- ##
-##    Main.    ##
-## ----------- ##
-
-magic='%%%MAGIC variable%%%'
-magic_exe='%%%MAGIC EXE variable%%%'
-
-# Global variables.
-extracted_archives=
-extracted_serial=0
-
-# If this variable is set in any of the actions, the command in it
-# will be execed at the end.  This prevents here-documents from being
-# left over by shells.
-exec_cmd=
-
-
-# A function that is used when there is no print builtin or printf.
-func_fallback_echo ()
-{
-  eval 'cat <<_LTECHO_EOF
-$1
-_LTECHO_EOF'
-}
-
-# func_generated_by_libtool
-# True iff stdin has been generated by Libtool. This function is only
-# a basic sanity check; it will hardly flush out determined imposters.
-func_generated_by_libtool_p ()
-{
-  $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
-}
-
-# func_lalib_p file
-# True iff FILE is a libtool '.la' library or '.lo' object file.
-# This function is only a basic sanity check; it will hardly flush out
-# determined imposters.
-func_lalib_p ()
-{
-    test -f "$1" &&
-      $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p
-}
-
-# func_lalib_unsafe_p file
-# True iff FILE is a libtool '.la' library or '.lo' object file.
-# This function implements the same check as func_lalib_p without
-# resorting to external programs.  To this end, it redirects stdin and
-# closes it afterwards, without saving the original file descriptor.
-# As a safety measure, use it only where a negative result would be
-# fatal anyway.  Works if 'file' does not exist.
-func_lalib_unsafe_p ()
-{
-    lalib_p=no
-    if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
-	for lalib_p_l in 1 2 3 4
-	do
-	    read lalib_p_line
-	    case $lalib_p_line in
-		\#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
-	    esac
-	done
-	exec 0<&5 5<&-
-    fi
-    test yes = "$lalib_p"
-}
-
-# func_ltwrapper_script_p file
-# True iff FILE is a libtool wrapper script
-# This function is only a basic sanity check; it will hardly flush out
-# determined imposters.
-func_ltwrapper_script_p ()
-{
-    test -f "$1" &&
-      $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p
-}
-
-# func_ltwrapper_executable_p file
-# True iff FILE is a libtool wrapper executable
-# This function is only a basic sanity check; it will hardly flush out
-# determined imposters.
-func_ltwrapper_executable_p ()
-{
-    func_ltwrapper_exec_suffix=
-    case $1 in
-    *.exe) ;;
-    *) func_ltwrapper_exec_suffix=.exe ;;
-    esac
-    $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
-}
-
-# func_ltwrapper_scriptname file
-# Assumes file is an ltwrapper_executable
-# uses $file to determine the appropriate filename for a
-# temporary ltwrapper_script.
-func_ltwrapper_scriptname ()
-{
-    func_dirname_and_basename "$1" "" "."
-    func_stripname '' '.exe' "$func_basename_result"
-    func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper
-}
-
-# func_ltwrapper_p file
-# True iff FILE is a libtool wrapper script or wrapper executable
-# This function is only a basic sanity check; it will hardly flush out
-# determined imposters.
-func_ltwrapper_p ()
-{
-    func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
-}
-
-
-# func_execute_cmds commands fail_cmd
-# Execute tilde-delimited COMMANDS.
-# If FAIL_CMD is given, eval that upon failure.
-# FAIL_CMD may read-access the current command in variable CMD!
-func_execute_cmds ()
-{
-    $debug_cmd
-
-    save_ifs=$IFS; IFS='~'
-    for cmd in $1; do
-      IFS=$sp$nl
-      eval cmd=\"$cmd\"
-      IFS=$save_ifs
-      func_show_eval "$cmd" "${2-:}"
-    done
-    IFS=$save_ifs
-}
-
-
-# func_source file
-# Source FILE, adding directory component if necessary.
-# Note that it is not necessary on cygwin/mingw to append a dot to
-# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
-# behavior happens only for exec(3), not for open(2)!  Also, sourcing
-# 'FILE.' does not work on cygwin managed mounts.
-func_source ()
-{
-    $debug_cmd
-
-    case $1 in
-    */* | *\\*)	. "$1" ;;
-    *)		. "./$1" ;;
-    esac
-}
-
-
-# func_resolve_sysroot PATH
-# Replace a leading = in PATH with a sysroot.  Store the result into
-# func_resolve_sysroot_result
-func_resolve_sysroot ()
-{
-  func_resolve_sysroot_result=$1
-  case $func_resolve_sysroot_result in
-  =*)
-    func_stripname '=' '' "$func_resolve_sysroot_result"
-    func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
-    ;;
-  esac
-}
-
-# func_replace_sysroot PATH
-# If PATH begins with the sysroot, replace it with = and
-# store the result into func_replace_sysroot_result.
-func_replace_sysroot ()
-{
-  case $lt_sysroot:$1 in
-  ?*:"$lt_sysroot"*)
-    func_stripname "$lt_sysroot" '' "$1"
-    func_replace_sysroot_result='='$func_stripname_result
-    ;;
-  *)
-    # Including no sysroot.
-    func_replace_sysroot_result=$1
-    ;;
-  esac
-}
-
-# func_infer_tag arg
-# Infer tagged configuration to use if any are available and
-# if one wasn't chosen via the "--tag" command line option.
-# Only attempt this if the compiler in the base compile
-# command doesn't match the default compiler.
-# arg is usually of the form 'gcc ...'
-func_infer_tag ()
-{
-    $debug_cmd
-
-    if test -n "$available_tags" && test -z "$tagname"; then
-      CC_quoted=
-      for arg in $CC; do
-	func_append_quoted CC_quoted "$arg"
-      done
-      CC_expanded=`func_echo_all $CC`
-      CC_quoted_expanded=`func_echo_all $CC_quoted`
-      case $@ in
-      # Blanks in the command may have been stripped by the calling shell,
-      # but not from the CC environment variable when configure was run.
-      " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
-      " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
-      # Blanks at the start of $base_compile will cause this to fail
-      # if we don't check for them as well.
-      *)
-	for z in $available_tags; do
-	  if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
-	    # Evaluate the configuration.
-	    eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
-	    CC_quoted=
-	    for arg in $CC; do
-	      # Double-quote args containing other shell metacharacters.
-	      func_append_quoted CC_quoted "$arg"
-	    done
-	    CC_expanded=`func_echo_all $CC`
-	    CC_quoted_expanded=`func_echo_all $CC_quoted`
-	    case "$@ " in
-	    " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
-	    " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
-	      # The compiler in the base compile command matches
-	      # the one in the tagged configuration.
-	      # Assume this is the tagged configuration we want.
-	      tagname=$z
-	      break
-	      ;;
-	    esac
-	  fi
-	done
-	# If $tagname still isn't set, then no tagged configuration
-	# was found and let the user know that the "--tag" command
-	# line option must be used.
-	if test -z "$tagname"; then
-	  func_echo "unable to infer tagged configuration"
-	  func_fatal_error "specify a tag with '--tag'"
-#	else
-#	  func_verbose "using $tagname tagged configuration"
-	fi
-	;;
-      esac
-    fi
-}
-
-
-
-# func_write_libtool_object output_name pic_name nonpic_name
-# Create a libtool object file (analogous to a ".la" file),
-# but don't create it if we're doing a dry run.
-func_write_libtool_object ()
-{
-    write_libobj=$1
-    if test yes = "$build_libtool_libs"; then
-      write_lobj=\'$2\'
-    else
-      write_lobj=none
-    fi
-
-    if test yes = "$build_old_libs"; then
-      write_oldobj=\'$3\'
-    else
-      write_oldobj=none
-    fi
-
-    $opt_dry_run || {
-      cat >${write_libobj}T <<EOF
-# $write_libobj - a libtool object file
-# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
-#
-# Please DO NOT delete this file!
-# It is necessary for linking the library.
-
-# Name of the PIC object.
-pic_object=$write_lobj
-
-# Name of the non-PIC object
-non_pic_object=$write_oldobj
-
-EOF
-      $MV "${write_libobj}T" "$write_libobj"
-    }
-}
-
-
-##################################################
-# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
-##################################################
-
-# func_convert_core_file_wine_to_w32 ARG
-# Helper function used by file name conversion functions when $build is *nix,
-# and $host is mingw, cygwin, or some other w32 environment. Relies on a
-# correctly configured wine environment available, with the winepath program
-# in $build's $PATH.
-#
-# ARG is the $build file name to be converted to w32 format.
-# Result is available in $func_convert_core_file_wine_to_w32_result, and will
-# be empty on error (or when ARG is empty)
-func_convert_core_file_wine_to_w32 ()
-{
-  $debug_cmd
-
-  func_convert_core_file_wine_to_w32_result=$1
-  if test -n "$1"; then
-    # Unfortunately, winepath does not exit with a non-zero error code, so we
-    # are forced to check the contents of stdout. On the other hand, if the
-    # command is not found, the shell will set an exit code of 127 and print
-    # *an error message* to stdout. So we must check for both error code of
-    # zero AND non-empty stdout, which explains the odd construction:
-    func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
-    if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then
-      func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
-        $SED -e "$sed_naive_backslashify"`
-    else
-      func_convert_core_file_wine_to_w32_result=
-    fi
-  fi
-}
-# end: func_convert_core_file_wine_to_w32
-
-
-# func_convert_core_path_wine_to_w32 ARG
-# Helper function used by path conversion functions when $build is *nix, and
-# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
-# configured wine environment available, with the winepath program in $build's
-# $PATH. Assumes ARG has no leading or trailing path separator characters.
-#
-# ARG is path to be converted from $build format to win32.
-# Result is available in $func_convert_core_path_wine_to_w32_result.
-# Unconvertible file (directory) names in ARG are skipped; if no directory names
-# are convertible, then the result may be empty.
-func_convert_core_path_wine_to_w32 ()
-{
-  $debug_cmd
-
-  # unfortunately, winepath doesn't convert paths, only file names
-  func_convert_core_path_wine_to_w32_result=
-  if test -n "$1"; then
-    oldIFS=$IFS
-    IFS=:
-    for func_convert_core_path_wine_to_w32_f in $1; do
-      IFS=$oldIFS
-      func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
-      if test -n "$func_convert_core_file_wine_to_w32_result"; then
-        if test -z "$func_convert_core_path_wine_to_w32_result"; then
-          func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result
-        else
-          func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
-        fi
-      fi
-    done
-    IFS=$oldIFS
-  fi
-}
-# end: func_convert_core_path_wine_to_w32
-
-
-# func_cygpath ARGS...
-# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
-# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
-# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
-# (2), returns the Cygwin file name or path in func_cygpath_result (input
-# file name or path is assumed to be in w32 format, as previously converted
-# from $build's *nix or MSYS format). In case (3), returns the w32 file name
-# or path in func_cygpath_result (input file name or path is assumed to be in
-# Cygwin format). Returns an empty string on error.
-#
-# ARGS are passed to cygpath, with the last one being the file name or path to
-# be converted.
-#
-# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
-# environment variable; do not put it in $PATH.
-func_cygpath ()
-{
-  $debug_cmd
-
-  if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
-    func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
-    if test "$?" -ne 0; then
-      # on failure, ensure result is empty
-      func_cygpath_result=
-    fi
-  else
-    func_cygpath_result=
-    func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'"
-  fi
-}
-#end: func_cygpath
-
-
-# func_convert_core_msys_to_w32 ARG
-# Convert file name or path ARG from MSYS format to w32 format.  Return
-# result in func_convert_core_msys_to_w32_result.
-func_convert_core_msys_to_w32 ()
-{
-  $debug_cmd
-
-  # awkward: cmd appends spaces to result
-  func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
-    $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"`
-}
-#end: func_convert_core_msys_to_w32
-
-
-# func_convert_file_check ARG1 ARG2
-# Verify that ARG1 (a file name in $build format) was converted to $host
-# format in ARG2. Otherwise, emit an error message, but continue (resetting
-# func_to_host_file_result to ARG1).
-func_convert_file_check ()
-{
-  $debug_cmd
-
-  if test -z "$2" && test -n "$1"; then
-    func_error "Could not determine host file name corresponding to"
-    func_error "  '$1'"
-    func_error "Continuing, but uninstalled executables may not work."
-    # Fallback:
-    func_to_host_file_result=$1
-  fi
-}
-# end func_convert_file_check
-
-
-# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
-# Verify that FROM_PATH (a path in $build format) was converted to $host
-# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
-# func_to_host_file_result to a simplistic fallback value (see below).
-func_convert_path_check ()
-{
-  $debug_cmd
-
-  if test -z "$4" && test -n "$3"; then
-    func_error "Could not determine the host path corresponding to"
-    func_error "  '$3'"
-    func_error "Continuing, but uninstalled executables may not work."
-    # Fallback.  This is a deliberately simplistic "conversion" and
-    # should not be "improved".  See libtool.info.
-    if test "x$1" != "x$2"; then
-      lt_replace_pathsep_chars="s|$1|$2|g"
-      func_to_host_path_result=`echo "$3" |
-        $SED -e "$lt_replace_pathsep_chars"`
-    else
-      func_to_host_path_result=$3
-    fi
-  fi
-}
-# end func_convert_path_check
-
-
-# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
-# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
-# and appending REPL if ORIG matches BACKPAT.
-func_convert_path_front_back_pathsep ()
-{
-  $debug_cmd
-
-  case $4 in
-  $1 ) func_to_host_path_result=$3$func_to_host_path_result
-    ;;
-  esac
-  case $4 in
-  $2 ) func_append func_to_host_path_result "$3"
-    ;;
-  esac
-}
-# end func_convert_path_front_back_pathsep
-
-
-##################################################
-# $build to $host FILE NAME CONVERSION FUNCTIONS #
-##################################################
-# invoked via '$to_host_file_cmd ARG'
-#
-# In each case, ARG is the path to be converted from $build to $host format.
-# Result will be available in $func_to_host_file_result.
-
-
-# func_to_host_file ARG
-# Converts the file name ARG from $build format to $host format. Return result
-# in func_to_host_file_result.
-func_to_host_file ()
-{
-  $debug_cmd
-
-  $to_host_file_cmd "$1"
-}
-# end func_to_host_file
-
-
-# func_to_tool_file ARG LAZY
-# converts the file name ARG from $build format to toolchain format. Return
-# result in func_to_tool_file_result.  If the conversion in use is listed
-# in (the comma separated) LAZY, no conversion takes place.
-func_to_tool_file ()
-{
-  $debug_cmd
-
-  case ,$2, in
-    *,"$to_tool_file_cmd",*)
-      func_to_tool_file_result=$1
-      ;;
-    *)
-      $to_tool_file_cmd "$1"
-      func_to_tool_file_result=$func_to_host_file_result
-      ;;
-  esac
-}
-# end func_to_tool_file
-
-
-# func_convert_file_noop ARG
-# Copy ARG to func_to_host_file_result.
-func_convert_file_noop ()
-{
-  func_to_host_file_result=$1
-}
-# end func_convert_file_noop
-
-
-# func_convert_file_msys_to_w32 ARG
-# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
-# conversion to w32 is not available inside the cwrapper.  Returns result in
-# func_to_host_file_result.
-func_convert_file_msys_to_w32 ()
-{
-  $debug_cmd
-
-  func_to_host_file_result=$1
-  if test -n "$1"; then
-    func_convert_core_msys_to_w32 "$1"
-    func_to_host_file_result=$func_convert_core_msys_to_w32_result
-  fi
-  func_convert_file_check "$1" "$func_to_host_file_result"
-}
-# end func_convert_file_msys_to_w32
-
-
-# func_convert_file_cygwin_to_w32 ARG
-# Convert file name ARG from Cygwin to w32 format.  Returns result in
-# func_to_host_file_result.
-func_convert_file_cygwin_to_w32 ()
-{
-  $debug_cmd
-
-  func_to_host_file_result=$1
-  if test -n "$1"; then
-    # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
-    # LT_CYGPATH in this case.
-    func_to_host_file_result=`cygpath -m "$1"`
-  fi
-  func_convert_file_check "$1" "$func_to_host_file_result"
-}
-# end func_convert_file_cygwin_to_w32
-
-
-# func_convert_file_nix_to_w32 ARG
-# Convert file name ARG from *nix to w32 format.  Requires a wine environment
-# and a working winepath. Returns result in func_to_host_file_result.
-func_convert_file_nix_to_w32 ()
-{
-  $debug_cmd
-
-  func_to_host_file_result=$1
-  if test -n "$1"; then
-    func_convert_core_file_wine_to_w32 "$1"
-    func_to_host_file_result=$func_convert_core_file_wine_to_w32_result
-  fi
-  func_convert_file_check "$1" "$func_to_host_file_result"
-}
-# end func_convert_file_nix_to_w32
-
-
-# func_convert_file_msys_to_cygwin ARG
-# Convert file name ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
-# Returns result in func_to_host_file_result.
-func_convert_file_msys_to_cygwin ()
-{
-  $debug_cmd
-
-  func_to_host_file_result=$1
-  if test -n "$1"; then
-    func_convert_core_msys_to_w32 "$1"
-    func_cygpath -u "$func_convert_core_msys_to_w32_result"
-    func_to_host_file_result=$func_cygpath_result
-  fi
-  func_convert_file_check "$1" "$func_to_host_file_result"
-}
-# end func_convert_file_msys_to_cygwin
-
-
-# func_convert_file_nix_to_cygwin ARG
-# Convert file name ARG from *nix to Cygwin format.  Requires Cygwin installed
-# in a wine environment, working winepath, and LT_CYGPATH set.  Returns result
-# in func_to_host_file_result.
-func_convert_file_nix_to_cygwin ()
-{
-  $debug_cmd
-
-  func_to_host_file_result=$1
-  if test -n "$1"; then
-    # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
-    func_convert_core_file_wine_to_w32 "$1"
-    func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
-    func_to_host_file_result=$func_cygpath_result
-  fi
-  func_convert_file_check "$1" "$func_to_host_file_result"
-}
-# end func_convert_file_nix_to_cygwin
-
-
-#############################################
-# $build to $host PATH CONVERSION FUNCTIONS #
-#############################################
-# invoked via '$to_host_path_cmd ARG'
-#
-# In each case, ARG is the path to be converted from $build to $host format.
-# The result will be available in $func_to_host_path_result.
-#
-# Path separators are also converted from $build format to $host format.  If
-# ARG begins or ends with a path separator character, it is preserved (but
-# converted to $host format) on output.
-#
-# All path conversion functions are named using the following convention:
-#   file name conversion function    : func_convert_file_X_to_Y ()
-#   path conversion function         : func_convert_path_X_to_Y ()
-# where, for any given $build/$host combination the 'X_to_Y' value is the
-# same.  If conversion functions are added for new $build/$host combinations,
-# the two new functions must follow this pattern, or func_init_to_host_path_cmd
-# will break.
-
-
-# func_init_to_host_path_cmd
-# Ensures that function "pointer" variable $to_host_path_cmd is set to the
-# appropriate value, based on the value of $to_host_file_cmd.
-to_host_path_cmd=
-func_init_to_host_path_cmd ()
-{
-  $debug_cmd
-
-  if test -z "$to_host_path_cmd"; then
-    func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
-    to_host_path_cmd=func_convert_path_$func_stripname_result
-  fi
-}
-
-
-# func_to_host_path ARG
-# Converts the path ARG from $build format to $host format. Return result
-# in func_to_host_path_result.
-func_to_host_path ()
-{
-  $debug_cmd
-
-  func_init_to_host_path_cmd
-  $to_host_path_cmd "$1"
-}
-# end func_to_host_path
-
-
-# func_convert_path_noop ARG
-# Copy ARG to func_to_host_path_result.
-func_convert_path_noop ()
-{
-  func_to_host_path_result=$1
-}
-# end func_convert_path_noop
-
-
-# func_convert_path_msys_to_w32 ARG
-# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
-# conversion to w32 is not available inside the cwrapper.  Returns result in
-# func_to_host_path_result.
-func_convert_path_msys_to_w32 ()
-{
-  $debug_cmd
-
-  func_to_host_path_result=$1
-  if test -n "$1"; then
-    # Remove leading and trailing path separator characters from ARG.  MSYS
-    # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
-    # and winepath ignores them completely.
-    func_stripname : : "$1"
-    func_to_host_path_tmp1=$func_stripname_result
-    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
-    func_to_host_path_result=$func_convert_core_msys_to_w32_result
-    func_convert_path_check : ";" \
-      "$func_to_host_path_tmp1" "$func_to_host_path_result"
-    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
-  fi
-}
-# end func_convert_path_msys_to_w32
-
-
-# func_convert_path_cygwin_to_w32 ARG
-# Convert path ARG from Cygwin to w32 format.  Returns result in
-# func_to_host_file_result.
-func_convert_path_cygwin_to_w32 ()
-{
-  $debug_cmd
-
-  func_to_host_path_result=$1
-  if test -n "$1"; then
-    # See func_convert_path_msys_to_w32:
-    func_stripname : : "$1"
-    func_to_host_path_tmp1=$func_stripname_result
-    func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
-    func_convert_path_check : ";" \
-      "$func_to_host_path_tmp1" "$func_to_host_path_result"
-    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
-  fi
-}
-# end func_convert_path_cygwin_to_w32
-
-
-# func_convert_path_nix_to_w32 ARG
-# Convert path ARG from *nix to w32 format.  Requires a wine environment and
-# a working winepath.  Returns result in func_to_host_file_result.
-func_convert_path_nix_to_w32 ()
-{
-  $debug_cmd
-
-  func_to_host_path_result=$1
-  if test -n "$1"; then
-    # See func_convert_path_msys_to_w32:
-    func_stripname : : "$1"
-    func_to_host_path_tmp1=$func_stripname_result
-    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
-    func_to_host_path_result=$func_convert_core_path_wine_to_w32_result
-    func_convert_path_check : ";" \
-      "$func_to_host_path_tmp1" "$func_to_host_path_result"
-    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
-  fi
-}
-# end func_convert_path_nix_to_w32
-
-
-# func_convert_path_msys_to_cygwin ARG
-# Convert path ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
-# Returns result in func_to_host_file_result.
-func_convert_path_msys_to_cygwin ()
-{
-  $debug_cmd
-
-  func_to_host_path_result=$1
-  if test -n "$1"; then
-    # See func_convert_path_msys_to_w32:
-    func_stripname : : "$1"
-    func_to_host_path_tmp1=$func_stripname_result
-    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
-    func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
-    func_to_host_path_result=$func_cygpath_result
-    func_convert_path_check : : \
-      "$func_to_host_path_tmp1" "$func_to_host_path_result"
-    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
-  fi
-}
-# end func_convert_path_msys_to_cygwin
-
-
-# func_convert_path_nix_to_cygwin ARG
-# Convert path ARG from *nix to Cygwin format.  Requires Cygwin installed in a
-# a wine environment, working winepath, and LT_CYGPATH set.  Returns result in
-# func_to_host_file_result.
-func_convert_path_nix_to_cygwin ()
-{
-  $debug_cmd
-
-  func_to_host_path_result=$1
-  if test -n "$1"; then
-    # Remove leading and trailing path separator characters from
-    # ARG. msys behavior is inconsistent here, cygpath turns them
-    # into '.;' and ';.', and winepath ignores them completely.
-    func_stripname : : "$1"
-    func_to_host_path_tmp1=$func_stripname_result
-    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
-    func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
-    func_to_host_path_result=$func_cygpath_result
-    func_convert_path_check : : \
-      "$func_to_host_path_tmp1" "$func_to_host_path_result"
-    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
-  fi
-}
-# end func_convert_path_nix_to_cygwin
-
-
-# func_dll_def_p FILE
-# True iff FILE is a Windows DLL '.def' file.
-# Keep in sync with _LT_DLL_DEF_P in libtool.m4
-func_dll_def_p ()
-{
-  $debug_cmd
-
-  func_dll_def_p_tmp=`$SED -n \
-    -e 's/^[	 ]*//' \
-    -e '/^\(;.*\)*$/d' \
-    -e 's/^\(EXPORTS\|LIBRARY\)\([	 ].*\)*$/DEF/p' \
-    -e q \
-    "$1"`
-  test DEF = "$func_dll_def_p_tmp"
-}
-
-
-# func_mode_compile arg...
-func_mode_compile ()
-{
-    $debug_cmd
-
-    # Get the compilation command and the source file.
-    base_compile=
-    srcfile=$nonopt  #  always keep a non-empty value in "srcfile"
-    suppress_opt=yes
-    suppress_output=
-    arg_mode=normal
-    libobj=
-    later=
-    pie_flag=
-
-    for arg
-    do
-      case $arg_mode in
-      arg  )
-	# do not "continue".  Instead, add this to base_compile
-	lastarg=$arg
-	arg_mode=normal
-	;;
-
-      target )
-	libobj=$arg
-	arg_mode=normal
-	continue
-	;;
-
-      normal )
-	# Accept any command-line options.
-	case $arg in
-	-o)
-	  test -n "$libobj" && \
-	    func_fatal_error "you cannot specify '-o' more than once"
-	  arg_mode=target
-	  continue
-	  ;;
-
-	-pie | -fpie | -fPIE)
-          func_append pie_flag " $arg"
-	  continue
-	  ;;
-
-	-shared | -static | -prefer-pic | -prefer-non-pic)
-	  func_append later " $arg"
-	  continue
-	  ;;
-
-	-no-suppress)
-	  suppress_opt=no
-	  continue
-	  ;;
-
-	-Xcompiler)
-	  arg_mode=arg  #  the next one goes into the "base_compile" arg list
-	  continue      #  The current "srcfile" will either be retained or
-	  ;;            #  replaced later.  I would guess that would be a bug.
-
-	-Wc,*)
-	  func_stripname '-Wc,' '' "$arg"
-	  args=$func_stripname_result
-	  lastarg=
-	  save_ifs=$IFS; IFS=,
-	  for arg in $args; do
-	    IFS=$save_ifs
-	    func_append_quoted lastarg "$arg"
-	  done
-	  IFS=$save_ifs
-	  func_stripname ' ' '' "$lastarg"
-	  lastarg=$func_stripname_result
-
-	  # Add the arguments to base_compile.
-	  func_append base_compile " $lastarg"
-	  continue
-	  ;;
-
-	*)
-	  # Accept the current argument as the source file.
-	  # The previous "srcfile" becomes the current argument.
-	  #
-	  lastarg=$srcfile
-	  srcfile=$arg
-	  ;;
-	esac  #  case $arg
-	;;
-      esac    #  case $arg_mode
-
-      # Aesthetically quote the previous argument.
-      func_append_quoted base_compile "$lastarg"
-    done # for arg
-
-    case $arg_mode in
-    arg)
-      func_fatal_error "you must specify an argument for -Xcompile"
-      ;;
-    target)
-      func_fatal_error "you must specify a target with '-o'"
-      ;;
-    *)
-      # Get the name of the library object.
-      test -z "$libobj" && {
-	func_basename "$srcfile"
-	libobj=$func_basename_result
-      }
-      ;;
-    esac
-
-    # Recognize several different file suffixes.
-    # If the user specifies -o file.o, it is replaced with file.lo
-    case $libobj in
-    *.[cCFSifmso] | \
-    *.ada | *.adb | *.ads | *.asm | \
-    *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
-    *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
-      func_xform "$libobj"
-      libobj=$func_xform_result
-      ;;
-    esac
-
-    case $libobj in
-    *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
-    *)
-      func_fatal_error "cannot determine name of library object from '$libobj'"
-      ;;
-    esac
-
-    func_infer_tag $base_compile
-
-    for arg in $later; do
-      case $arg in
-      -shared)
-	test yes = "$build_libtool_libs" \
-	  || func_fatal_configuration "cannot build a shared library"
-	build_old_libs=no
-	continue
-	;;
-
-      -static)
-	build_libtool_libs=no
-	build_old_libs=yes
-	continue
-	;;
-
-      -prefer-pic)
-	pic_mode=yes
-	continue
-	;;
-
-      -prefer-non-pic)
-	pic_mode=no
-	continue
-	;;
-      esac
-    done
-
-    func_quote_for_eval "$libobj"
-    test "X$libobj" != "X$func_quote_for_eval_result" \
-      && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'	 &()|`$[]' \
-      && func_warning "libobj name '$libobj' may not contain shell special characters."
-    func_dirname_and_basename "$obj" "/" ""
-    objname=$func_basename_result
-    xdir=$func_dirname_result
-    lobj=$xdir$objdir/$objname
-
-    test -z "$base_compile" && \
-      func_fatal_help "you must specify a compilation command"
-
-    # Delete any leftover library objects.
-    if test yes = "$build_old_libs"; then
-      removelist="$obj $lobj $libobj ${libobj}T"
-    else
-      removelist="$lobj $libobj ${libobj}T"
-    fi
-
-    # On Cygwin there's no "real" PIC flag so we must build both object types
-    case $host_os in
-    cygwin* | mingw* | pw32* | os2* | cegcc*)
-      pic_mode=default
-      ;;
-    esac
-    if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then
-      # non-PIC code in shared libraries is not supported
-      pic_mode=default
-    fi
-
-    # Calculate the filename of the output object if compiler does
-    # not support -o with -c
-    if test no = "$compiler_c_o"; then
-      output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext
-      lockfile=$output_obj.lock
-    else
-      output_obj=
-      need_locks=no
-      lockfile=
-    fi
-
-    # Lock this critical section if it is needed
-    # We use this script file to make the link, it avoids creating a new file
-    if test yes = "$need_locks"; then
-      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
-	func_echo "Waiting for $lockfile to be removed"
-	sleep 2
-      done
-    elif test warn = "$need_locks"; then
-      if test -f "$lockfile"; then
-	$ECHO "\
-*** ERROR, $lockfile exists and contains:
-`cat $lockfile 2>/dev/null`
-
-This indicates that another process is trying to use the same
-temporary object file, and libtool could not work around it because
-your compiler does not support '-c' and '-o' together.  If you
-repeat this compilation, it may succeed, by chance, but you had better
-avoid parallel builds (make -j) in this platform, or get a better
-compiler."
-
-	$opt_dry_run || $RM $removelist
-	exit $EXIT_FAILURE
-      fi
-      func_append removelist " $output_obj"
-      $ECHO "$srcfile" > "$lockfile"
-    fi
-
-    $opt_dry_run || $RM $removelist
-    func_append removelist " $lockfile"
-    trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
-
-    func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
-    srcfile=$func_to_tool_file_result
-    func_quote_for_eval "$srcfile"
-    qsrcfile=$func_quote_for_eval_result
-
-    # Only build a PIC object if we are building libtool libraries.
-    if test yes = "$build_libtool_libs"; then
-      # Without this assignment, base_compile gets emptied.
-      fbsd_hideous_sh_bug=$base_compile
-
-      if test no != "$pic_mode"; then
-	command="$base_compile $qsrcfile $pic_flag"
-      else
-	# Don't build PIC code
-	command="$base_compile $qsrcfile"
-      fi
-
-      func_mkdir_p "$xdir$objdir"
-
-      if test -z "$output_obj"; then
-	# Place PIC objects in $objdir
-	func_append command " -o $lobj"
-      fi
-
-      func_show_eval_locale "$command"	\
-          'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
-
-      if test warn = "$need_locks" &&
-	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
-	$ECHO "\
-*** ERROR, $lockfile contains:
-`cat $lockfile 2>/dev/null`
-
-but it should contain:
-$srcfile
-
-This indicates that another process is trying to use the same
-temporary object file, and libtool could not work around it because
-your compiler does not support '-c' and '-o' together.  If you
-repeat this compilation, it may succeed, by chance, but you had better
-avoid parallel builds (make -j) in this platform, or get a better
-compiler."
-
-	$opt_dry_run || $RM $removelist
-	exit $EXIT_FAILURE
-      fi
-
-      # Just move the object if needed, then go on to compile the next one
-      if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
-	func_show_eval '$MV "$output_obj" "$lobj"' \
-	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
-      fi
-
-      # Allow error messages only from the first compilation.
-      if test yes = "$suppress_opt"; then
-	suppress_output=' >/dev/null 2>&1'
-      fi
-    fi
-
-    # Only build a position-dependent object if we build old libraries.
-    if test yes = "$build_old_libs"; then
-      if test yes != "$pic_mode"; then
-	# Don't build PIC code
-	command="$base_compile $qsrcfile$pie_flag"
-      else
-	command="$base_compile $qsrcfile $pic_flag"
-      fi
-      if test yes = "$compiler_c_o"; then
-	func_append command " -o $obj"
-      fi
-
-      # Suppress compiler output if we already did a PIC compilation.
-      func_append command "$suppress_output"
-      func_show_eval_locale "$command" \
-        '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
-
-      if test warn = "$need_locks" &&
-	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
-	$ECHO "\
-*** ERROR, $lockfile contains:
-`cat $lockfile 2>/dev/null`
-
-but it should contain:
-$srcfile
-
-This indicates that another process is trying to use the same
-temporary object file, and libtool could not work around it because
-your compiler does not support '-c' and '-o' together.  If you
-repeat this compilation, it may succeed, by chance, but you had better
-avoid parallel builds (make -j) in this platform, or get a better
-compiler."
-
-	$opt_dry_run || $RM $removelist
-	exit $EXIT_FAILURE
-      fi
-
-      # Just move the object if needed
-      if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
-	func_show_eval '$MV "$output_obj" "$obj"' \
-	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
-      fi
-    fi
-
-    $opt_dry_run || {
-      func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
-
-      # Unlock the critical section if it was locked
-      if test no != "$need_locks"; then
-	removelist=$lockfile
-        $RM "$lockfile"
-      fi
-    }
-
-    exit $EXIT_SUCCESS
-}
-
-$opt_help || {
-  test compile = "$opt_mode" && func_mode_compile ${1+"$@"}
-}
-
-func_mode_help ()
-{
-    # We need to display help for each of the modes.
-    case $opt_mode in
-      "")
-        # Generic help is extracted from the usage comments
-        # at the start of this file.
-        func_help
-        ;;
-
-      clean)
-        $ECHO \
-"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
-
-Remove files from the build directory.
-
-RM is the name of the program to use to delete files associated with each FILE
-(typically '/bin/rm').  RM-OPTIONS are options (such as '-f') to be passed
-to RM.
-
-If FILE is a libtool library, object or program, all the files associated
-with it are deleted. Otherwise, only FILE itself is deleted using RM."
-        ;;
-
-      compile)
-      $ECHO \
-"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
-
-Compile a source file into a libtool library object.
-
-This mode accepts the following additional options:
-
-  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
-  -no-suppress      do not suppress compiler output for multiple passes
-  -prefer-pic       try to build PIC objects only
-  -prefer-non-pic   try to build non-PIC objects only
-  -shared           do not build a '.o' file suitable for static linking
-  -static           only build a '.o' file suitable for static linking
-  -Wc,FLAG          pass FLAG directly to the compiler
-
-COMPILE-COMMAND is a command to be used in creating a 'standard' object file
-from the given SOURCEFILE.
-
-The output file name is determined by removing the directory component from
-SOURCEFILE, then substituting the C source code suffix '.c' with the
-library object suffix, '.lo'."
-        ;;
-
-      execute)
-        $ECHO \
-"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
-
-Automatically set library path, then run a program.
-
-This mode accepts the following additional options:
-
-  -dlopen FILE      add the directory containing FILE to the library path
-
-This mode sets the library path environment variable according to '-dlopen'
-flags.
-
-If any of the ARGS are libtool executable wrappers, then they are translated
-into their corresponding uninstalled binary, and any of their required library
-directories are added to the library path.
-
-Then, COMMAND is executed, with ARGS as arguments."
-        ;;
-
-      finish)
-        $ECHO \
-"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
-
-Complete the installation of libtool libraries.
-
-Each LIBDIR is a directory that contains libtool libraries.
-
-The commands that this mode executes may require superuser privileges.  Use
-the '--dry-run' option if you just want to see what would be executed."
-        ;;
-
-      install)
-        $ECHO \
-"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
-
-Install executables or libraries.
-
-INSTALL-COMMAND is the installation command.  The first component should be
-either the 'install' or 'cp' program.
-
-The following components of INSTALL-COMMAND are treated specially:
-
-  -inst-prefix-dir PREFIX-DIR  Use PREFIX-DIR as a staging area for installation
-
-The rest of the components are interpreted as arguments to that command (only
-BSD-compatible install options are recognized)."
-        ;;
-
-      link)
-        $ECHO \
-"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
-
-Link object files or libraries together to form another library, or to
-create an executable program.
-
-LINK-COMMAND is a command using the C compiler that you would use to create
-a program from several object files.
-
-The following components of LINK-COMMAND are treated specially:
-
-  -all-static       do not do any dynamic linking at all
-  -avoid-version    do not add a version suffix if possible
-  -bindir BINDIR    specify path to binaries directory (for systems where
-                    libraries must be found in the PATH setting at runtime)
-  -dlopen FILE      '-dlpreopen' FILE if it cannot be dlopened at runtime
-  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
-  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
-  -export-symbols SYMFILE
-                    try to export only the symbols listed in SYMFILE
-  -export-symbols-regex REGEX
-                    try to export only the symbols matching REGEX
-  -LLIBDIR          search LIBDIR for required installed libraries
-  -lNAME            OUTPUT-FILE requires the installed library libNAME
-  -module           build a library that can dlopened
-  -no-fast-install  disable the fast-install mode
-  -no-install       link a not-installable executable
-  -no-undefined     declare that a library does not refer to external symbols
-  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
-  -objectlist FILE  use a list of object files found in FILE to specify objects
-  -os2dllname NAME  force a short DLL name on OS/2 (no effect on other OSes)
-  -precious-files-regex REGEX
-                    don't remove output files matching REGEX
-  -release RELEASE  specify package release information
-  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
-  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
-  -shared           only do dynamic linking of libtool libraries
-  -shrext SUFFIX    override the standard shared library file extension
-  -static           do not do any dynamic linking of uninstalled libtool libraries
-  -static-libtool-libs
-                    do not do any dynamic linking of libtool libraries
-  -version-info CURRENT[:REVISION[:AGE]]
-                    specify library version info [each variable defaults to 0]
-  -weak LIBNAME     declare that the target provides the LIBNAME interface
-  -Wc,FLAG
-  -Xcompiler FLAG   pass linker-specific FLAG directly to the compiler
-  -Wl,FLAG
-  -Xlinker FLAG     pass linker-specific FLAG directly to the linker
-  -XCClinker FLAG   pass link-specific FLAG to the compiler driver (CC)
-
-All other options (arguments beginning with '-') are ignored.
-
-Every other argument is treated as a filename.  Files ending in '.la' are
-treated as uninstalled libtool libraries, other files are standard or library
-object files.
-
-If the OUTPUT-FILE ends in '.la', then a libtool library is created,
-only library objects ('.lo' files) may be specified, and '-rpath' is
-required, except when creating a convenience library.
-
-If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created
-using 'ar' and 'ranlib', or on Windows using 'lib'.
-
-If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file
-is created, otherwise an executable program is created."
-        ;;
-
-      uninstall)
-        $ECHO \
-"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
-
-Remove libraries from an installation directory.
-
-RM is the name of the program to use to delete files associated with each FILE
-(typically '/bin/rm').  RM-OPTIONS are options (such as '-f') to be passed
-to RM.
-
-If FILE is a libtool library, all the files associated with it are deleted.
-Otherwise, only FILE itself is deleted using RM."
-        ;;
-
-      *)
-        func_fatal_help "invalid operation mode '$opt_mode'"
-        ;;
-    esac
-
-    echo
-    $ECHO "Try '$progname --help' for more information about other modes."
-}
-
-# Now that we've collected a possible --mode arg, show help if necessary
-if $opt_help; then
-  if test : = "$opt_help"; then
-    func_mode_help
-  else
-    {
-      func_help noexit
-      for opt_mode in compile link execute install finish uninstall clean; do
-	func_mode_help
-      done
-    } | $SED -n '1p; 2,$s/^Usage:/  or: /p'
-    {
-      func_help noexit
-      for opt_mode in compile link execute install finish uninstall clean; do
-	echo
-	func_mode_help
-      done
-    } |
-    $SED '1d
-      /^When reporting/,/^Report/{
-	H
-	d
-      }
-      $x
-      /information about other modes/d
-      /more detailed .*MODE/d
-      s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
-  fi
-  exit $?
-fi
-
-
-# func_mode_execute arg...
-func_mode_execute ()
-{
-    $debug_cmd
-
-    # The first argument is the command name.
-    cmd=$nonopt
-    test -z "$cmd" && \
-      func_fatal_help "you must specify a COMMAND"
-
-    # Handle -dlopen flags immediately.
-    for file in $opt_dlopen; do
-      test -f "$file" \
-	|| func_fatal_help "'$file' is not a file"
-
-      dir=
-      case $file in
-      *.la)
-	func_resolve_sysroot "$file"
-	file=$func_resolve_sysroot_result
-
-	# Check to see that this really is a libtool archive.
-	func_lalib_unsafe_p "$file" \
-	  || func_fatal_help "'$lib' is not a valid libtool archive"
-
-	# Read the libtool library.
-	dlname=
-	library_names=
-	func_source "$file"
-
-	# Skip this library if it cannot be dlopened.
-	if test -z "$dlname"; then
-	  # Warn if it was a shared library.
-	  test -n "$library_names" && \
-	    func_warning "'$file' was not linked with '-export-dynamic'"
-	  continue
-	fi
-
-	func_dirname "$file" "" "."
-	dir=$func_dirname_result
-
-	if test -f "$dir/$objdir/$dlname"; then
-	  func_append dir "/$objdir"
-	else
-	  if test ! -f "$dir/$dlname"; then
-	    func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'"
-	  fi
-	fi
-	;;
-
-      *.lo)
-	# Just add the directory containing the .lo file.
-	func_dirname "$file" "" "."
-	dir=$func_dirname_result
-	;;
-
-      *)
-	func_warning "'-dlopen' is ignored for non-libtool libraries and objects"
-	continue
-	;;
-      esac
-
-      # Get the absolute pathname.
-      absdir=`cd "$dir" && pwd`
-      test -n "$absdir" && dir=$absdir
-
-      # Now add the directory to shlibpath_var.
-      if eval "test -z \"\$$shlibpath_var\""; then
-	eval "$shlibpath_var=\"\$dir\""
-      else
-	eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
-      fi
-    done
-
-    # This variable tells wrapper scripts just to set shlibpath_var
-    # rather than running their programs.
-    libtool_execute_magic=$magic
-
-    # Check if any of the arguments is a wrapper script.
-    args=
-    for file
-    do
-      case $file in
-      -* | *.la | *.lo ) ;;
-      *)
-	# Do a test to see if this is really a libtool program.
-	if func_ltwrapper_script_p "$file"; then
-	  func_source "$file"
-	  # Transform arg to wrapped name.
-	  file=$progdir/$program
-	elif func_ltwrapper_executable_p "$file"; then
-	  func_ltwrapper_scriptname "$file"
-	  func_source "$func_ltwrapper_scriptname_result"
-	  # Transform arg to wrapped name.
-	  file=$progdir/$program
-	fi
-	;;
-      esac
-      # Quote arguments (to preserve shell metacharacters).
-      func_append_quoted args "$file"
-    done
-
-    if $opt_dry_run; then
-      # Display what would be done.
-      if test -n "$shlibpath_var"; then
-	eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
-	echo "export $shlibpath_var"
-      fi
-      $ECHO "$cmd$args"
-      exit $EXIT_SUCCESS
-    else
-      if test -n "$shlibpath_var"; then
-	# Export the shlibpath_var.
-	eval "export $shlibpath_var"
-      fi
-
-      # Restore saved environment variables
-      for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
-      do
-	eval "if test \"\${save_$lt_var+set}\" = set; then
-                $lt_var=\$save_$lt_var; export $lt_var
-	      else
-		$lt_unset $lt_var
-	      fi"
-      done
-
-      # Now prepare to actually exec the command.
-      exec_cmd=\$cmd$args
-    fi
-}
-
-test execute = "$opt_mode" && func_mode_execute ${1+"$@"}
-
-
-# func_mode_finish arg...
-func_mode_finish ()
-{
-    $debug_cmd
-
-    libs=
-    libdirs=
-    admincmds=
-
-    for opt in "$nonopt" ${1+"$@"}
-    do
-      if test -d "$opt"; then
-	func_append libdirs " $opt"
-
-      elif test -f "$opt"; then
-	if func_lalib_unsafe_p "$opt"; then
-	  func_append libs " $opt"
-	else
-	  func_warning "'$opt' is not a valid libtool archive"
-	fi
-
-      else
-	func_fatal_error "invalid argument '$opt'"
-      fi
-    done
-
-    if test -n "$libs"; then
-      if test -n "$lt_sysroot"; then
-        sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
-        sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
-      else
-        sysroot_cmd=
-      fi
-
-      # Remove sysroot references
-      if $opt_dry_run; then
-        for lib in $libs; do
-          echo "removing references to $lt_sysroot and '=' prefixes from $lib"
-        done
-      else
-        tmpdir=`func_mktempdir`
-        for lib in $libs; do
-	  $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
-	    > $tmpdir/tmp-la
-	  mv -f $tmpdir/tmp-la $lib
-	done
-        ${RM}r "$tmpdir"
-      fi
-    fi
-
-    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
-      for libdir in $libdirs; do
-	if test -n "$finish_cmds"; then
-	  # Do each command in the finish commands.
-	  func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
-'"$cmd"'"'
-	fi
-	if test -n "$finish_eval"; then
-	  # Do the single finish_eval.
-	  eval cmds=\"$finish_eval\"
-	  $opt_dry_run || eval "$cmds" || func_append admincmds "
-       $cmds"
-	fi
-      done
-    fi
-
-    # Exit here if they wanted silent mode.
-    $opt_quiet && exit $EXIT_SUCCESS
-
-    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
-      echo "----------------------------------------------------------------------"
-      echo "Libraries have been installed in:"
-      for libdir in $libdirs; do
-	$ECHO "   $libdir"
-      done
-      echo
-      echo "If you ever happen to want to link against installed libraries"
-      echo "in a given directory, LIBDIR, you must either use libtool, and"
-      echo "specify the full pathname of the library, or use the '-LLIBDIR'"
-      echo "flag during linking and do at least one of the following:"
-      if test -n "$shlibpath_var"; then
-	echo "   - add LIBDIR to the '$shlibpath_var' environment variable"
-	echo "     during execution"
-      fi
-      if test -n "$runpath_var"; then
-	echo "   - add LIBDIR to the '$runpath_var' environment variable"
-	echo "     during linking"
-      fi
-      if test -n "$hardcode_libdir_flag_spec"; then
-	libdir=LIBDIR
-	eval flag=\"$hardcode_libdir_flag_spec\"
-
-	$ECHO "   - use the '$flag' linker flag"
-      fi
-      if test -n "$admincmds"; then
-	$ECHO "   - have your system administrator run these commands:$admincmds"
-      fi
-      if test -f /etc/ld.so.conf; then
-	echo "   - have your system administrator add LIBDIR to '/etc/ld.so.conf'"
-      fi
-      echo
-
-      echo "See any operating system documentation about shared libraries for"
-      case $host in
-	solaris2.[6789]|solaris2.1[0-9])
-	  echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
-	  echo "pages."
-	  ;;
-	*)
-	  echo "more information, such as the ld(1) and ld.so(8) manual pages."
-	  ;;
-      esac
-      echo "----------------------------------------------------------------------"
-    fi
-    exit $EXIT_SUCCESS
-}
-
-test finish = "$opt_mode" && func_mode_finish ${1+"$@"}
-
-
-# func_mode_install arg...
-func_mode_install ()
-{
-    $debug_cmd
-
-    # There may be an optional sh(1) argument at the beginning of
-    # install_prog (especially on Windows NT).
-    if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" ||
-       # Allow the use of GNU shtool's install command.
-       case $nonopt in *shtool*) :;; *) false;; esac
-    then
-      # Aesthetically quote it.
-      func_quote_for_eval "$nonopt"
-      install_prog="$func_quote_for_eval_result "
-      arg=$1
-      shift
-    else
-      install_prog=
-      arg=$nonopt
-    fi
-
-    # The real first argument should be the name of the installation program.
-    # Aesthetically quote it.
-    func_quote_for_eval "$arg"
-    func_append install_prog "$func_quote_for_eval_result"
-    install_shared_prog=$install_prog
-    case " $install_prog " in
-      *[\\\ /]cp\ *) install_cp=: ;;
-      *) install_cp=false ;;
-    esac
-
-    # We need to accept at least all the BSD install flags.
-    dest=
-    files=
-    opts=
-    prev=
-    install_type=
-    isdir=false
-    stripme=
-    no_mode=:
-    for arg
-    do
-      arg2=
-      if test -n "$dest"; then
-	func_append files " $dest"
-	dest=$arg
-	continue
-      fi
-
-      case $arg in
-      -d) isdir=: ;;
-      -f)
-	if $install_cp; then :; else
-	  prev=$arg
-	fi
-	;;
-      -g | -m | -o)
-	prev=$arg
-	;;
-      -s)
-	stripme=" -s"
-	continue
-	;;
-      -*)
-	;;
-      *)
-	# If the previous option needed an argument, then skip it.
-	if test -n "$prev"; then
-	  if test X-m = "X$prev" && test -n "$install_override_mode"; then
-	    arg2=$install_override_mode
-	    no_mode=false
-	  fi
-	  prev=
-	else
-	  dest=$arg
-	  continue
-	fi
-	;;
-      esac
-
-      # Aesthetically quote the argument.
-      func_quote_for_eval "$arg"
-      func_append install_prog " $func_quote_for_eval_result"
-      if test -n "$arg2"; then
-	func_quote_for_eval "$arg2"
-      fi
-      func_append install_shared_prog " $func_quote_for_eval_result"
-    done
-
-    test -z "$install_prog" && \
-      func_fatal_help "you must specify an install program"
-
-    test -n "$prev" && \
-      func_fatal_help "the '$prev' option requires an argument"
-
-    if test -n "$install_override_mode" && $no_mode; then
-      if $install_cp; then :; else
-	func_quote_for_eval "$install_override_mode"
-	func_append install_shared_prog " -m $func_quote_for_eval_result"
-      fi
-    fi
-
-    if test -z "$files"; then
-      if test -z "$dest"; then
-	func_fatal_help "no file or destination specified"
-      else
-	func_fatal_help "you must specify a destination"
-      fi
-    fi
-
-    # Strip any trailing slash from the destination.
-    func_stripname '' '/' "$dest"
-    dest=$func_stripname_result
-
-    # Check to see that the destination is a directory.
-    test -d "$dest" && isdir=:
-    if $isdir; then
-      destdir=$dest
-      destname=
-    else
-      func_dirname_and_basename "$dest" "" "."
-      destdir=$func_dirname_result
-      destname=$func_basename_result
-
-      # Not a directory, so check to see that there is only one file specified.
-      set dummy $files; shift
-      test "$#" -gt 1 && \
-	func_fatal_help "'$dest' is not a directory"
-    fi
-    case $destdir in
-    [\\/]* | [A-Za-z]:[\\/]*) ;;
-    *)
-      for file in $files; do
-	case $file in
-	*.lo) ;;
-	*)
-	  func_fatal_help "'$destdir' must be an absolute directory name"
-	  ;;
-	esac
-      done
-      ;;
-    esac
-
-    # This variable tells wrapper scripts just to set variables rather
-    # than running their programs.
-    libtool_install_magic=$magic
-
-    staticlibs=
-    future_libdirs=
-    current_libdirs=
-    for file in $files; do
-
-      # Do each installation.
-      case $file in
-      *.$libext)
-	# Do the static libraries later.
-	func_append staticlibs " $file"
-	;;
-
-      *.la)
-	func_resolve_sysroot "$file"
-	file=$func_resolve_sysroot_result
-
-	# Check to see that this really is a libtool archive.
-	func_lalib_unsafe_p "$file" \
-	  || func_fatal_help "'$file' is not a valid libtool archive"
-
-	library_names=
-	old_library=
-	relink_command=
-	func_source "$file"
-
-	# Add the libdir to current_libdirs if it is the destination.
-	if test "X$destdir" = "X$libdir"; then
-	  case "$current_libdirs " in
-	  *" $libdir "*) ;;
-	  *) func_append current_libdirs " $libdir" ;;
-	  esac
-	else
-	  # Note the libdir as a future libdir.
-	  case "$future_libdirs " in
-	  *" $libdir "*) ;;
-	  *) func_append future_libdirs " $libdir" ;;
-	  esac
-	fi
-
-	func_dirname "$file" "/" ""
-	dir=$func_dirname_result
-	func_append dir "$objdir"
-
-	if test -n "$relink_command"; then
-	  # Determine the prefix the user has applied to our future dir.
-	  inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
-
-	  # Don't allow the user to place us outside of our expected
-	  # location b/c this prevents finding dependent libraries that
-	  # are installed to the same prefix.
-	  # At present, this check doesn't affect windows .dll's that
-	  # are installed into $libdir/../bin (currently, that works fine)
-	  # but it's something to keep an eye on.
-	  test "$inst_prefix_dir" = "$destdir" && \
-	    func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir"
-
-	  if test -n "$inst_prefix_dir"; then
-	    # Stick the inst_prefix_dir data into the link command.
-	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
-	  else
-	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
-	  fi
-
-	  func_warning "relinking '$file'"
-	  func_show_eval "$relink_command" \
-	    'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"'
-	fi
-
-	# See the names of the shared library.
-	set dummy $library_names; shift
-	if test -n "$1"; then
-	  realname=$1
-	  shift
-
-	  srcname=$realname
-	  test -n "$relink_command" && srcname=${realname}T
-
-	  # Install the shared library and build the symlinks.
-	  func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
-	      'exit $?'
-	  tstripme=$stripme
-	  case $host_os in
-	  cygwin* | mingw* | pw32* | cegcc*)
-	    case $realname in
-	    *.dll.a)
-	      tstripme=
-	      ;;
-	    esac
-	    ;;
-	  os2*)
-	    case $realname in
-	    *_dll.a)
-	      tstripme=
-	      ;;
-	    esac
-	    ;;
-	  esac
-	  if test -n "$tstripme" && test -n "$striplib"; then
-	    func_show_eval "$striplib $destdir/$realname" 'exit $?'
-	  fi
-
-	  if test "$#" -gt 0; then
-	    # Delete the old symlinks, and create new ones.
-	    # Try 'ln -sf' first, because the 'ln' binary might depend on
-	    # the symlink we replace!  Solaris /bin/ln does not understand -f,
-	    # so we also need to try rm && ln -s.
-	    for linkname
-	    do
-	      test "$linkname" != "$realname" \
-		&& func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
-	    done
-	  fi
-
-	  # Do each command in the postinstall commands.
-	  lib=$destdir/$realname
-	  func_execute_cmds "$postinstall_cmds" 'exit $?'
-	fi
-
-	# Install the pseudo-library for information purposes.
-	func_basename "$file"
-	name=$func_basename_result
-	instname=$dir/${name}i
-	func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
-
-	# Maybe install the static library, too.
-	test -n "$old_library" && func_append staticlibs " $dir/$old_library"
-	;;
-
-      *.lo)
-	# Install (i.e. copy) a libtool object.
-
-	# Figure out destination file name, if it wasn't already specified.
-	if test -n "$destname"; then
-	  destfile=$destdir/$destname
-	else
-	  func_basename "$file"
-	  destfile=$func_basename_result
-	  destfile=$destdir/$destfile
-	fi
-
-	# Deduce the name of the destination old-style object file.
-	case $destfile in
-	*.lo)
-	  func_lo2o "$destfile"
-	  staticdest=$func_lo2o_result
-	  ;;
-	*.$objext)
-	  staticdest=$destfile
-	  destfile=
-	  ;;
-	*)
-	  func_fatal_help "cannot copy a libtool object to '$destfile'"
-	  ;;
-	esac
-
-	# Install the libtool object if requested.
-	test -n "$destfile" && \
-	  func_show_eval "$install_prog $file $destfile" 'exit $?'
-
-	# Install the old object if enabled.
-	if test yes = "$build_old_libs"; then
-	  # Deduce the name of the old-style object file.
-	  func_lo2o "$file"
-	  staticobj=$func_lo2o_result
-	  func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
-	fi
-	exit $EXIT_SUCCESS
-	;;
-
-      *)
-	# Figure out destination file name, if it wasn't already specified.
-	if test -n "$destname"; then
-	  destfile=$destdir/$destname
-	else
-	  func_basename "$file"
-	  destfile=$func_basename_result
-	  destfile=$destdir/$destfile
-	fi
-
-	# If the file is missing, and there is a .exe on the end, strip it
-	# because it is most likely a libtool script we actually want to
-	# install
-	stripped_ext=
-	case $file in
-	  *.exe)
-	    if test ! -f "$file"; then
-	      func_stripname '' '.exe' "$file"
-	      file=$func_stripname_result
-	      stripped_ext=.exe
-	    fi
-	    ;;
-	esac
-
-	# Do a test to see if this is really a libtool program.
-	case $host in
-	*cygwin* | *mingw*)
-	    if func_ltwrapper_executable_p "$file"; then
-	      func_ltwrapper_scriptname "$file"
-	      wrapper=$func_ltwrapper_scriptname_result
-	    else
-	      func_stripname '' '.exe' "$file"
-	      wrapper=$func_stripname_result
-	    fi
-	    ;;
-	*)
-	    wrapper=$file
-	    ;;
-	esac
-	if func_ltwrapper_script_p "$wrapper"; then
-	  notinst_deplibs=
-	  relink_command=
-
-	  func_source "$wrapper"
-
-	  # Check the variables that should have been set.
-	  test -z "$generated_by_libtool_version" && \
-	    func_fatal_error "invalid libtool wrapper script '$wrapper'"
-
-	  finalize=:
-	  for lib in $notinst_deplibs; do
-	    # Check to see that each library is installed.
-	    libdir=
-	    if test -f "$lib"; then
-	      func_source "$lib"
-	    fi
-	    libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'`
-	    if test -n "$libdir" && test ! -f "$libfile"; then
-	      func_warning "'$lib' has not been installed in '$libdir'"
-	      finalize=false
-	    fi
-	  done
-
-	  relink_command=
-	  func_source "$wrapper"
-
-	  outputname=
-	  if test no = "$fast_install" && test -n "$relink_command"; then
-	    $opt_dry_run || {
-	      if $finalize; then
-	        tmpdir=`func_mktempdir`
-		func_basename "$file$stripped_ext"
-		file=$func_basename_result
-	        outputname=$tmpdir/$file
-	        # Replace the output file specification.
-	        relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
-
-	        $opt_quiet || {
-	          func_quote_for_expand "$relink_command"
-		  eval "func_echo $func_quote_for_expand_result"
-	        }
-	        if eval "$relink_command"; then :
-	          else
-		  func_error "error: relink '$file' with the above command before installing it"
-		  $opt_dry_run || ${RM}r "$tmpdir"
-		  continue
-	        fi
-	        file=$outputname
-	      else
-	        func_warning "cannot relink '$file'"
-	      fi
-	    }
-	  else
-	    # Install the binary that we compiled earlier.
-	    file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
-	  fi
-	fi
-
-	# remove .exe since cygwin /usr/bin/install will append another
-	# one anyway
-	case $install_prog,$host in
-	*/usr/bin/install*,*cygwin*)
-	  case $file:$destfile in
-	  *.exe:*.exe)
-	    # this is ok
-	    ;;
-	  *.exe:*)
-	    destfile=$destfile.exe
-	    ;;
-	  *:*.exe)
-	    func_stripname '' '.exe' "$destfile"
-	    destfile=$func_stripname_result
-	    ;;
-	  esac
-	  ;;
-	esac
-	func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
-	$opt_dry_run || if test -n "$outputname"; then
-	  ${RM}r "$tmpdir"
-	fi
-	;;
-      esac
-    done
-
-    for file in $staticlibs; do
-      func_basename "$file"
-      name=$func_basename_result
-
-      # Set up the ranlib parameters.
-      oldlib=$destdir/$name
-      func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
-      tool_oldlib=$func_to_tool_file_result
-
-      func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
-
-      if test -n "$stripme" && test -n "$old_striplib"; then
-	func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
-      fi
-
-      # Do each command in the postinstall commands.
-      func_execute_cmds "$old_postinstall_cmds" 'exit $?'
-    done
-
-    test -n "$future_libdirs" && \
-      func_warning "remember to run '$progname --finish$future_libdirs'"
-
-    if test -n "$current_libdirs"; then
-      # Maybe just do a dry run.
-      $opt_dry_run && current_libdirs=" -n$current_libdirs"
-      exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs'
-    else
-      exit $EXIT_SUCCESS
-    fi
-}
-
-test install = "$opt_mode" && func_mode_install ${1+"$@"}
-
-
-# func_generate_dlsyms outputname originator pic_p
-# Extract symbols from dlprefiles and create ${outputname}S.o with
-# a dlpreopen symbol table.
-func_generate_dlsyms ()
-{
-    $debug_cmd
-
-    my_outputname=$1
-    my_originator=$2
-    my_pic_p=${3-false}
-    my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'`
-    my_dlsyms=
-
-    if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
-      if test -n "$NM" && test -n "$global_symbol_pipe"; then
-	my_dlsyms=${my_outputname}S.c
-      else
-	func_error "not configured to extract global symbols from dlpreopened files"
-      fi
-    fi
-
-    if test -n "$my_dlsyms"; then
-      case $my_dlsyms in
-      "") ;;
-      *.c)
-	# Discover the nlist of each of the dlfiles.
-	nlist=$output_objdir/$my_outputname.nm
-
-	func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
-
-	# Parse the name list into a source file.
-	func_verbose "creating $output_objdir/$my_dlsyms"
-
-	$opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
-/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */
-/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */
-
-#ifdef __cplusplus
-extern \"C\" {
-#endif
-
-#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
-#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
-#endif
-
-/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
-#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
-/* DATA imports from DLLs on WIN32 can't be const, because runtime
-   relocations are performed -- see ld's documentation on pseudo-relocs.  */
-# define LT_DLSYM_CONST
-#elif defined __osf__
-/* This system does not cope well with relocations in const data.  */
-# define LT_DLSYM_CONST
-#else
-# define LT_DLSYM_CONST const
-#endif
-
-#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
-
-/* External symbol declarations for the compiler. */\
-"
-
-	if test yes = "$dlself"; then
-	  func_verbose "generating symbol list for '$output'"
-
-	  $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
-
-	  # Add our own program objects to the symbol list.
-	  progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
-	  for progfile in $progfiles; do
-	    func_to_tool_file "$progfile" func_convert_file_msys_to_w32
-	    func_verbose "extracting global C symbols from '$func_to_tool_file_result'"
-	    $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
-	  done
-
-	  if test -n "$exclude_expsyms"; then
-	    $opt_dry_run || {
-	      eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
-	      eval '$MV "$nlist"T "$nlist"'
-	    }
-	  fi
-
-	  if test -n "$export_symbols_regex"; then
-	    $opt_dry_run || {
-	      eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
-	      eval '$MV "$nlist"T "$nlist"'
-	    }
-	  fi
-
-	  # Prepare the list of exported symbols
-	  if test -z "$export_symbols"; then
-	    export_symbols=$output_objdir/$outputname.exp
-	    $opt_dry_run || {
-	      $RM $export_symbols
-	      eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
-	      case $host in
-	      *cygwin* | *mingw* | *cegcc* )
-                eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
-                eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
-	        ;;
-	      esac
-	    }
-	  else
-	    $opt_dry_run || {
-	      eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
-	      eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
-	      eval '$MV "$nlist"T "$nlist"'
-	      case $host in
-	        *cygwin* | *mingw* | *cegcc* )
-	          eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
-	          eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
-	          ;;
-	      esac
-	    }
-	  fi
-	fi
-
-	for dlprefile in $dlprefiles; do
-	  func_verbose "extracting global C symbols from '$dlprefile'"
-	  func_basename "$dlprefile"
-	  name=$func_basename_result
-          case $host in
-	    *cygwin* | *mingw* | *cegcc* )
-	      # if an import library, we need to obtain dlname
-	      if func_win32_import_lib_p "$dlprefile"; then
-	        func_tr_sh "$dlprefile"
-	        eval "curr_lafile=\$libfile_$func_tr_sh_result"
-	        dlprefile_dlbasename=
-	        if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
-	          # Use subshell, to avoid clobbering current variable values
-	          dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
-	          if test -n "$dlprefile_dlname"; then
-	            func_basename "$dlprefile_dlname"
-	            dlprefile_dlbasename=$func_basename_result
-	          else
-	            # no lafile. user explicitly requested -dlpreopen <import library>.
-	            $sharedlib_from_linklib_cmd "$dlprefile"
-	            dlprefile_dlbasename=$sharedlib_from_linklib_result
-	          fi
-	        fi
-	        $opt_dry_run || {
-	          if test -n "$dlprefile_dlbasename"; then
-	            eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
-	          else
-	            func_warning "Could not compute DLL name from $name"
-	            eval '$ECHO ": $name " >> "$nlist"'
-	          fi
-	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
-	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
-	            $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
-	        }
-	      else # not an import lib
-	        $opt_dry_run || {
-	          eval '$ECHO ": $name " >> "$nlist"'
-	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
-	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
-	        }
-	      fi
-	    ;;
-	    *)
-	      $opt_dry_run || {
-	        eval '$ECHO ": $name " >> "$nlist"'
-	        func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
-	        eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
-	      }
-	    ;;
-          esac
-	done
-
-	$opt_dry_run || {
-	  # Make sure we have at least an empty file.
-	  test -f "$nlist" || : > "$nlist"
-
-	  if test -n "$exclude_expsyms"; then
-	    $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
-	    $MV "$nlist"T "$nlist"
-	  fi
-
-	  # Try sorting and uniquifying the output.
-	  if $GREP -v "^: " < "$nlist" |
-	      if sort -k 3 </dev/null >/dev/null 2>&1; then
-		sort -k 3
-	      else
-		sort +2
-	      fi |
-	      uniq > "$nlist"S; then
-	    :
-	  else
-	    $GREP -v "^: " < "$nlist" > "$nlist"S
-	  fi
-
-	  if test -f "$nlist"S; then
-	    eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
-	  else
-	    echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
-	  fi
-
-	  func_show_eval '$RM "${nlist}I"'
-	  if test -n "$global_symbol_to_import"; then
-	    eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I'
-	  fi
-
-	  echo >> "$output_objdir/$my_dlsyms" "\
-
-/* The mapping between symbol names and symbols.  */
-typedef struct {
-  const char *name;
-  void *address;
-} lt_dlsymlist;
-extern LT_DLSYM_CONST lt_dlsymlist
-lt_${my_prefix}_LTX_preloaded_symbols[];\
-"
-
-	  if test -s "$nlist"I; then
-	    echo >> "$output_objdir/$my_dlsyms" "\
-static void lt_syminit(void)
-{
-  LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols;
-  for (; symbol->name; ++symbol)
-    {"
-	    $SED 's/.*/      if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms"
-	    echo >> "$output_objdir/$my_dlsyms" "\
-    }
-}"
-	  fi
-	  echo >> "$output_objdir/$my_dlsyms" "\
-LT_DLSYM_CONST lt_dlsymlist
-lt_${my_prefix}_LTX_preloaded_symbols[] =
-{ {\"$my_originator\", (void *) 0},"
-
-	  if test -s "$nlist"I; then
-	    echo >> "$output_objdir/$my_dlsyms" "\
-  {\"@INIT@\", (void *) &lt_syminit},"
-	  fi
-
-	  case $need_lib_prefix in
-	  no)
-	    eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
-	    ;;
-	  *)
-	    eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
-	    ;;
-	  esac
-	  echo >> "$output_objdir/$my_dlsyms" "\
-  {0, (void *) 0}
-};
-
-/* This works around a problem in FreeBSD linker */
-#ifdef FREEBSD_WORKAROUND
-static const void *lt_preloaded_setup() {
-  return lt_${my_prefix}_LTX_preloaded_symbols;
-}
-#endif
-
-#ifdef __cplusplus
-}
-#endif\
-"
-	} # !$opt_dry_run
-
-	pic_flag_for_symtable=
-	case "$compile_command " in
-	*" -static "*) ;;
-	*)
-	  case $host in
-	  # compiling the symbol table file with pic_flag works around
-	  # a FreeBSD bug that causes programs to crash when -lm is
-	  # linked before any other PIC object.  But we must not use
-	  # pic_flag when linking with -static.  The problem exists in
-	  # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
-	  *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
-	    pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
-	  *-*-hpux*)
-	    pic_flag_for_symtable=" $pic_flag"  ;;
-	  *)
-	    $my_pic_p && pic_flag_for_symtable=" $pic_flag"
-	    ;;
-	  esac
-	  ;;
-	esac
-	symtab_cflags=
-	for arg in $LTCFLAGS; do
-	  case $arg in
-	  -pie | -fpie | -fPIE) ;;
-	  *) func_append symtab_cflags " $arg" ;;
-	  esac
-	done
-
-	# Now compile the dynamic symbol file.
-	func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
-
-	# Clean up the generated files.
-	func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"'
-
-	# Transform the symbol file into the correct name.
-	symfileobj=$output_objdir/${my_outputname}S.$objext
-	case $host in
-	*cygwin* | *mingw* | *cegcc* )
-	  if test -f "$output_objdir/$my_outputname.def"; then
-	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
-	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
-	  else
-	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
-	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
-	  fi
-	  ;;
-	*)
-	  compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
-	  finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
-	  ;;
-	esac
-	;;
-      *)
-	func_fatal_error "unknown suffix for '$my_dlsyms'"
-	;;
-      esac
-    else
-      # We keep going just in case the user didn't refer to
-      # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
-      # really was required.
-
-      # Nullify the symbol file.
-      compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
-      finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
-    fi
-}
-
-# func_cygming_gnu_implib_p ARG
-# This predicate returns with zero status (TRUE) if
-# ARG is a GNU/binutils-style import library. Returns
-# with nonzero status (FALSE) otherwise.
-func_cygming_gnu_implib_p ()
-{
-  $debug_cmd
-
-  func_to_tool_file "$1" func_convert_file_msys_to_w32
-  func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
-  test -n "$func_cygming_gnu_implib_tmp"
-}
-
-# func_cygming_ms_implib_p ARG
-# This predicate returns with zero status (TRUE) if
-# ARG is an MS-style import library. Returns
-# with nonzero status (FALSE) otherwise.
-func_cygming_ms_implib_p ()
-{
-  $debug_cmd
-
-  func_to_tool_file "$1" func_convert_file_msys_to_w32
-  func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
-  test -n "$func_cygming_ms_implib_tmp"
-}
-
-# func_win32_libid arg
-# return the library type of file 'arg'
-#
-# Need a lot of goo to handle *both* DLLs and import libs
-# Has to be a shell function in order to 'eat' the argument
-# that is supplied when $file_magic_command is called.
-# Despite the name, also deal with 64 bit binaries.
-func_win32_libid ()
-{
-  $debug_cmd
-
-  win32_libid_type=unknown
-  win32_fileres=`file -L $1 2>/dev/null`
-  case $win32_fileres in
-  *ar\ archive\ import\ library*) # definitely import
-    win32_libid_type="x86 archive import"
-    ;;
-  *ar\ archive*) # could be an import, or static
-    # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
-    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
-       $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
-      case $nm_interface in
-      "MS dumpbin")
-	if func_cygming_ms_implib_p "$1" ||
-	   func_cygming_gnu_implib_p "$1"
-	then
-	  win32_nmres=import
-	else
-	  win32_nmres=
-	fi
-	;;
-      *)
-	func_to_tool_file "$1" func_convert_file_msys_to_w32
-	win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
-	  $SED -n -e '
-	    1,100{
-		/ I /{
-		    s|.*|import|
-		    p
-		    q
-		}
-	    }'`
-	;;
-      esac
-      case $win32_nmres in
-      import*)  win32_libid_type="x86 archive import";;
-      *)        win32_libid_type="x86 archive static";;
-      esac
-    fi
-    ;;
-  *DLL*)
-    win32_libid_type="x86 DLL"
-    ;;
-  *executable*) # but shell scripts are "executable" too...
-    case $win32_fileres in
-    *MS\ Windows\ PE\ Intel*)
-      win32_libid_type="x86 DLL"
-      ;;
-    esac
-    ;;
-  esac
-  $ECHO "$win32_libid_type"
-}
-
-# func_cygming_dll_for_implib ARG
-#
-# Platform-specific function to extract the
-# name of the DLL associated with the specified
-# import library ARG.
-# Invoked by eval'ing the libtool variable
-#    $sharedlib_from_linklib_cmd
-# Result is available in the variable
-#    $sharedlib_from_linklib_result
-func_cygming_dll_for_implib ()
-{
-  $debug_cmd
-
-  sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
-}
-
-# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
-#
-# The is the core of a fallback implementation of a
-# platform-specific function to extract the name of the
-# DLL associated with the specified import library LIBNAME.
-#
-# SECTION_NAME is either .idata$6 or .idata$7, depending
-# on the platform and compiler that created the implib.
-#
-# Echos the name of the DLL associated with the
-# specified import library.
-func_cygming_dll_for_implib_fallback_core ()
-{
-  $debug_cmd
-
-  match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
-  $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
-    $SED '/^Contents of section '"$match_literal"':/{
-      # Place marker at beginning of archive member dllname section
-      s/.*/====MARK====/
-      p
-      d
-    }
-    # These lines can sometimes be longer than 43 characters, but
-    # are always uninteresting
-    /:[	 ]*file format pe[i]\{,1\}-/d
-    /^In archive [^:]*:/d
-    # Ensure marker is printed
-    /^====MARK====/p
-    # Remove all lines with less than 43 characters
-    /^.\{43\}/!d
-    # From remaining lines, remove first 43 characters
-    s/^.\{43\}//' |
-    $SED -n '
-      # Join marker and all lines until next marker into a single line
-      /^====MARK====/ b para
-      H
-      $ b para
-      b
-      :para
-      x
-      s/\n//g
-      # Remove the marker
-      s/^====MARK====//
-      # Remove trailing dots and whitespace
-      s/[\. \t]*$//
-      # Print
-      /./p' |
-    # we now have a list, one entry per line, of the stringified
-    # contents of the appropriate section of all members of the
-    # archive that possess that section. Heuristic: eliminate
-    # all those that have a first or second character that is
-    # a '.' (that is, objdump's representation of an unprintable
-    # character.) This should work for all archives with less than
-    # 0x302f exports -- but will fail for DLLs whose name actually
-    # begins with a literal '.' or a single character followed by
-    # a '.'.
-    #
-    # Of those that remain, print the first one.
-    $SED -e '/^\./d;/^.\./d;q'
-}
-
-# func_cygming_dll_for_implib_fallback ARG
-# Platform-specific function to extract the
-# name of the DLL associated with the specified
-# import library ARG.
-#
-# This fallback implementation is for use when $DLLTOOL
-# does not support the --identify-strict option.
-# Invoked by eval'ing the libtool variable
-#    $sharedlib_from_linklib_cmd
-# Result is available in the variable
-#    $sharedlib_from_linklib_result
-func_cygming_dll_for_implib_fallback ()
-{
-  $debug_cmd
-
-  if func_cygming_gnu_implib_p "$1"; then
-    # binutils import library
-    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
-  elif func_cygming_ms_implib_p "$1"; then
-    # ms-generated import library
-    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
-  else
-    # unknown
-    sharedlib_from_linklib_result=
-  fi
-}
-
-
-# func_extract_an_archive dir oldlib
-func_extract_an_archive ()
-{
-    $debug_cmd
-
-    f_ex_an_ar_dir=$1; shift
-    f_ex_an_ar_oldlib=$1
-    if test yes = "$lock_old_archive_extraction"; then
-      lockfile=$f_ex_an_ar_oldlib.lock
-      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
-	func_echo "Waiting for $lockfile to be removed"
-	sleep 2
-      done
-    fi
-    func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
-		   'stat=$?; rm -f "$lockfile"; exit $stat'
-    if test yes = "$lock_old_archive_extraction"; then
-      $opt_dry_run || rm -f "$lockfile"
-    fi
-    if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
-     :
-    else
-      func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
-    fi
-}
-
-
-# func_extract_archives gentop oldlib ...
-func_extract_archives ()
-{
-    $debug_cmd
-
-    my_gentop=$1; shift
-    my_oldlibs=${1+"$@"}
-    my_oldobjs=
-    my_xlib=
-    my_xabs=
-    my_xdir=
-
-    for my_xlib in $my_oldlibs; do
-      # Extract the objects.
-      case $my_xlib in
-	[\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;;
-	*) my_xabs=`pwd`"/$my_xlib" ;;
-      esac
-      func_basename "$my_xlib"
-      my_xlib=$func_basename_result
-      my_xlib_u=$my_xlib
-      while :; do
-        case " $extracted_archives " in
-	*" $my_xlib_u "*)
-	  func_arith $extracted_serial + 1
-	  extracted_serial=$func_arith_result
-	  my_xlib_u=lt$extracted_serial-$my_xlib ;;
-	*) break ;;
-	esac
-      done
-      extracted_archives="$extracted_archives $my_xlib_u"
-      my_xdir=$my_gentop/$my_xlib_u
-
-      func_mkdir_p "$my_xdir"
-
-      case $host in
-      *-darwin*)
-	func_verbose "Extracting $my_xabs"
-	# Do not bother doing anything if just a dry run
-	$opt_dry_run || {
-	  darwin_orig_dir=`pwd`
-	  cd $my_xdir || exit $?
-	  darwin_archive=$my_xabs
-	  darwin_curdir=`pwd`
-	  func_basename "$darwin_archive"
-	  darwin_base_archive=$func_basename_result
-	  darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
-	  if test -n "$darwin_arches"; then
-	    darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
-	    darwin_arch=
-	    func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
-	    for darwin_arch in  $darwin_arches; do
-	      func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch"
-	      $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive"
-	      cd "unfat-$$/$darwin_base_archive-$darwin_arch"
-	      func_extract_an_archive "`pwd`" "$darwin_base_archive"
-	      cd "$darwin_curdir"
-	      $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive"
-	    done # $darwin_arches
-            ## Okay now we've a bunch of thin objects, gotta fatten them up :)
-	    darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u`
-	    darwin_file=
-	    darwin_files=
-	    for darwin_file in $darwin_filelist; do
-	      darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
-	      $LIPO -create -output "$darwin_file" $darwin_files
-	    done # $darwin_filelist
-	    $RM -rf unfat-$$
-	    cd "$darwin_orig_dir"
-	  else
-	    cd $darwin_orig_dir
-	    func_extract_an_archive "$my_xdir" "$my_xabs"
-	  fi # $darwin_arches
-	} # !$opt_dry_run
-	;;
-      *)
-        func_extract_an_archive "$my_xdir" "$my_xabs"
-	;;
-      esac
-      my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
-    done
-
-    func_extract_archives_result=$my_oldobjs
-}
-
-
-# func_emit_wrapper [arg=no]
-#
-# Emit a libtool wrapper script on stdout.
-# Don't directly open a file because we may want to
-# incorporate the script contents within a cygwin/mingw
-# wrapper executable.  Must ONLY be called from within
-# func_mode_link because it depends on a number of variables
-# set therein.
-#
-# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
-# variable will take.  If 'yes', then the emitted script
-# will assume that the directory where it is stored is
-# the $objdir directory.  This is a cygwin/mingw-specific
-# behavior.
-func_emit_wrapper ()
-{
-	func_emit_wrapper_arg1=${1-no}
-
-	$ECHO "\
-#! $SHELL
-
-# $output - temporary wrapper script for $objdir/$outputname
-# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
-#
-# The $output program cannot be directly executed until all the libtool
-# libraries that it depends on are installed.
-#
-# This wrapper script should never be moved out of the build directory.
-# If it is, it will not operate correctly.
-
-# Sed substitution that helps us do robust quoting.  It backslashifies
-# metacharacters that are still active within double-quoted strings.
-sed_quote_subst='$sed_quote_subst'
-
-# Be Bourne compatible
-if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
-  emulate sh
-  NULLCMD=:
-  # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
-  # is contrary to our usage.  Disable this feature.
-  alias -g '\${1+\"\$@\"}'='\"\$@\"'
-  setopt NO_GLOB_SUBST
-else
-  case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
-fi
-BIN_SH=xpg4; export BIN_SH # for Tru64
-DUALCASE=1; export DUALCASE # for MKS sh
-
-# The HP-UX ksh and POSIX shell print the target directory to stdout
-# if CDPATH is set.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-relink_command=\"$relink_command\"
-
-# This environment variable determines our operation mode.
-if test \"\$libtool_install_magic\" = \"$magic\"; then
-  # install mode needs the following variables:
-  generated_by_libtool_version='$macro_version'
-  notinst_deplibs='$notinst_deplibs'
-else
-  # When we are sourced in execute mode, \$file and \$ECHO are already set.
-  if test \"\$libtool_execute_magic\" != \"$magic\"; then
-    file=\"\$0\""
-
-    qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
-    $ECHO "\
-
-# A function that is used when there is no print builtin or printf.
-func_fallback_echo ()
-{
-  eval 'cat <<_LTECHO_EOF
-\$1
-_LTECHO_EOF'
-}
-    ECHO=\"$qECHO\"
-  fi
-
-# Very basic option parsing. These options are (a) specific to
-# the libtool wrapper, (b) are identical between the wrapper
-# /script/ and the wrapper /executable/ that is used only on
-# windows platforms, and (c) all begin with the string "--lt-"
-# (application programs are unlikely to have options that match
-# this pattern).
-#
-# There are only two supported options: --lt-debug and
-# --lt-dump-script. There is, deliberately, no --lt-help.
-#
-# The first argument to this parsing function should be the
-# script's $0 value, followed by "$@".
-lt_option_debug=
-func_parse_lt_options ()
-{
-  lt_script_arg0=\$0
-  shift
-  for lt_opt
-  do
-    case \"\$lt_opt\" in
-    --lt-debug) lt_option_debug=1 ;;
-    --lt-dump-script)
-        lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
-        test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
-        lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
-        cat \"\$lt_dump_D/\$lt_dump_F\"
-        exit 0
-      ;;
-    --lt-*)
-        \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
-        exit 1
-      ;;
-    esac
-  done
-
-  # Print the debug banner immediately:
-  if test -n \"\$lt_option_debug\"; then
-    echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2
-  fi
-}
-
-# Used when --lt-debug. Prints its arguments to stdout
-# (redirection is the responsibility of the caller)
-func_lt_dump_args ()
-{
-  lt_dump_args_N=1;
-  for lt_arg
-  do
-    \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\"
-    lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
-  done
-}
-
-# Core function for launching the target application
-func_exec_program_core ()
-{
-"
-  case $host in
-  # Backslashes separate directories on plain windows
-  *-*-mingw | *-*-os2* | *-cegcc*)
-    $ECHO "\
-      if test -n \"\$lt_option_debug\"; then
-        \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2
-        func_lt_dump_args \${1+\"\$@\"} 1>&2
-      fi
-      exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
-"
-    ;;
-
-  *)
-    $ECHO "\
-      if test -n \"\$lt_option_debug\"; then
-        \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2
-        func_lt_dump_args \${1+\"\$@\"} 1>&2
-      fi
-      exec \"\$progdir/\$program\" \${1+\"\$@\"}
-"
-    ;;
-  esac
-  $ECHO "\
-      \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
-      exit 1
-}
-
-# A function to encapsulate launching the target application
-# Strips options in the --lt-* namespace from \$@ and
-# launches target application with the remaining arguments.
-func_exec_program ()
-{
-  case \" \$* \" in
-  *\\ --lt-*)
-    for lt_wr_arg
-    do
-      case \$lt_wr_arg in
-      --lt-*) ;;
-      *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
-      esac
-      shift
-    done ;;
-  esac
-  func_exec_program_core \${1+\"\$@\"}
-}
-
-  # Parse options
-  func_parse_lt_options \"\$0\" \${1+\"\$@\"}
-
-  # Find the directory that this script lives in.
-  thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
-  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
-
-  # Follow symbolic links until we get to the real thisdir.
-  file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
-  while test -n \"\$file\"; do
-    destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
-
-    # If there was a directory component, then change thisdir.
-    if test \"x\$destdir\" != \"x\$file\"; then
-      case \"\$destdir\" in
-      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
-      *) thisdir=\"\$thisdir/\$destdir\" ;;
-      esac
-    fi
-
-    file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
-    file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
-  done
-
-  # Usually 'no', except on cygwin/mingw when embedded into
-  # the cwrapper.
-  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
-  if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
-    # special case for '.'
-    if test \"\$thisdir\" = \".\"; then
-      thisdir=\`pwd\`
-    fi
-    # remove .libs from thisdir
-    case \"\$thisdir\" in
-    *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
-    $objdir )   thisdir=. ;;
-    esac
-  fi
-
-  # Try to get the absolute directory name.
-  absdir=\`cd \"\$thisdir\" && pwd\`
-  test -n \"\$absdir\" && thisdir=\"\$absdir\"
-"
-
-	if test yes = "$fast_install"; then
-	  $ECHO "\
-  program=lt-'$outputname'$exeext
-  progdir=\"\$thisdir/$objdir\"
-
-  if test ! -f \"\$progdir/\$program\" ||
-     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\
-       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
-
-    file=\"\$\$-\$program\"
-
-    if test ! -d \"\$progdir\"; then
-      $MKDIR \"\$progdir\"
-    else
-      $RM \"\$progdir/\$file\"
-    fi"
-
-	  $ECHO "\
-
-    # relink executable if necessary
-    if test -n \"\$relink_command\"; then
-      if relink_command_output=\`eval \$relink_command 2>&1\`; then :
-      else
-	\$ECHO \"\$relink_command_output\" >&2
-	$RM \"\$progdir/\$file\"
-	exit 1
-      fi
-    fi
-
-    $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
-    { $RM \"\$progdir/\$program\";
-      $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
-    $RM \"\$progdir/\$file\"
-  fi"
-	else
-	  $ECHO "\
-  program='$outputname'
-  progdir=\"\$thisdir/$objdir\"
-"
-	fi
-
-	$ECHO "\
-
-  if test -f \"\$progdir/\$program\"; then"
-
-	# fixup the dll searchpath if we need to.
-	#
-	# Fix the DLL searchpath if we need to.  Do this before prepending
-	# to shlibpath, because on Windows, both are PATH and uninstalled
-	# libraries must come first.
-	if test -n "$dllsearchpath"; then
-	  $ECHO "\
-    # Add the dll search path components to the executable PATH
-    PATH=$dllsearchpath:\$PATH
-"
-	fi
-
-	# Export our shlibpath_var if we have one.
-	if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
-	  $ECHO "\
-    # Add our own library path to $shlibpath_var
-    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
-
-    # Some systems cannot cope with colon-terminated $shlibpath_var
-    # The second colon is a workaround for a bug in BeOS R4 sed
-    $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
-
-    export $shlibpath_var
-"
-	fi
-
-	$ECHO "\
-    if test \"\$libtool_execute_magic\" != \"$magic\"; then
-      # Run the actual program with our arguments.
-      func_exec_program \${1+\"\$@\"}
-    fi
-  else
-    # The program doesn't exist.
-    \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2
-    \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
-    \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
-    exit 1
-  fi
-fi\
-"
-}
-
-
-# func_emit_cwrapperexe_src
-# emit the source code for a wrapper executable on stdout
-# Must ONLY be called from within func_mode_link because
-# it depends on a number of variable set therein.
-func_emit_cwrapperexe_src ()
-{
-	cat <<EOF
-
-/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
-   Generated by $PROGRAM (GNU $PACKAGE) $VERSION
-
-   The $output program cannot be directly executed until all the libtool
-   libraries that it depends on are installed.
-
-   This wrapper executable should never be moved out of the build directory.
-   If it is, it will not operate correctly.
-*/
-EOF
-	    cat <<"EOF"
-#ifdef _MSC_VER
-# define _CRT_SECURE_NO_DEPRECATE 1
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef _MSC_VER
-# include <direct.h>
-# include <process.h>
-# include <io.h>
-#else
-# include <unistd.h>
-# include <stdint.h>
-# ifdef __CYGWIN__
-#  include <io.h>
-# endif
-#endif
-#include <malloc.h>
-#include <stdarg.h>
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
-
-/* declarations of non-ANSI functions */
-#if defined __MINGW32__
-# ifdef __STRICT_ANSI__
-int _putenv (const char *);
-# endif
-#elif defined __CYGWIN__
-# ifdef __STRICT_ANSI__
-char *realpath (const char *, char *);
-int putenv (char *);
-int setenv (const char *, const char *, int);
-# endif
-/* #elif defined other_platform || defined ... */
-#endif
-
-/* portability defines, excluding path handling macros */
-#if defined _MSC_VER
-# define setmode _setmode
-# define stat    _stat
-# define chmod   _chmod
-# define getcwd  _getcwd
-# define putenv  _putenv
-# define S_IXUSR _S_IEXEC
-#elif defined __MINGW32__
-# define setmode _setmode
-# define stat    _stat
-# define chmod   _chmod
-# define getcwd  _getcwd
-# define putenv  _putenv
-#elif defined __CYGWIN__
-# define HAVE_SETENV
-# define FOPEN_WB "wb"
-/* #elif defined other platforms ... */
-#endif
-
-#if defined PATH_MAX
-# define LT_PATHMAX PATH_MAX
-#elif defined MAXPATHLEN
-# define LT_PATHMAX MAXPATHLEN
-#else
-# define LT_PATHMAX 1024
-#endif
-
-#ifndef S_IXOTH
-# define S_IXOTH 0
-#endif
-#ifndef S_IXGRP
-# define S_IXGRP 0
-#endif
-
-/* path handling portability macros */
-#ifndef DIR_SEPARATOR
-# define DIR_SEPARATOR '/'
-# define PATH_SEPARATOR ':'
-#endif
-
-#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \
-  defined __OS2__
-# define HAVE_DOS_BASED_FILE_SYSTEM
-# define FOPEN_WB "wb"
-# ifndef DIR_SEPARATOR_2
-#  define DIR_SEPARATOR_2 '\\'
-# endif
-# ifndef PATH_SEPARATOR_2
-#  define PATH_SEPARATOR_2 ';'
-# endif
-#endif
-
-#ifndef DIR_SEPARATOR_2
-# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
-#else /* DIR_SEPARATOR_2 */
-# define IS_DIR_SEPARATOR(ch) \
-	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
-#endif /* DIR_SEPARATOR_2 */
-
-#ifndef PATH_SEPARATOR_2
-# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
-#else /* PATH_SEPARATOR_2 */
-# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
-#endif /* PATH_SEPARATOR_2 */
-
-#ifndef FOPEN_WB
-# define FOPEN_WB "w"
-#endif
-#ifndef _O_BINARY
-# define _O_BINARY 0
-#endif
-
-#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
-#define XFREE(stale) do { \
-  if (stale) { free (stale); stale = 0; } \
-} while (0)
-
-#if defined LT_DEBUGWRAPPER
-static int lt_debug = 1;
-#else
-static int lt_debug = 0;
-#endif
-
-const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
-
-void *xmalloc (size_t num);
-char *xstrdup (const char *string);
-const char *base_name (const char *name);
-char *find_executable (const char *wrapper);
-char *chase_symlinks (const char *pathspec);
-int make_executable (const char *path);
-int check_executable (const char *path);
-char *strendzap (char *str, const char *pat);
-void lt_debugprintf (const char *file, int line, const char *fmt, ...);
-void lt_fatal (const char *file, int line, const char *message, ...);
-static const char *nonnull (const char *s);
-static const char *nonempty (const char *s);
-void lt_setenv (const char *name, const char *value);
-char *lt_extend_str (const char *orig_value, const char *add, int to_end);
-void lt_update_exe_path (const char *name, const char *value);
-void lt_update_lib_path (const char *name, const char *value);
-char **prepare_spawn (char **argv);
-void lt_dump_script (FILE *f);
-EOF
-
-	    cat <<EOF
-#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
-# define externally_visible volatile
-#else
-# define externally_visible __attribute__((externally_visible)) volatile
-#endif
-externally_visible const char * MAGIC_EXE = "$magic_exe";
-const char * LIB_PATH_VARNAME = "$shlibpath_var";
-EOF
-
-	    if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
-              func_to_host_path "$temp_rpath"
-	      cat <<EOF
-const char * LIB_PATH_VALUE   = "$func_to_host_path_result";
-EOF
-	    else
-	      cat <<"EOF"
-const char * LIB_PATH_VALUE   = "";
-EOF
-	    fi
-
-	    if test -n "$dllsearchpath"; then
-              func_to_host_path "$dllsearchpath:"
-	      cat <<EOF
-const char * EXE_PATH_VARNAME = "PATH";
-const char * EXE_PATH_VALUE   = "$func_to_host_path_result";
-EOF
-	    else
-	      cat <<"EOF"
-const char * EXE_PATH_VARNAME = "";
-const char * EXE_PATH_VALUE   = "";
-EOF
-	    fi
-
-	    if test yes = "$fast_install"; then
-	      cat <<EOF
-const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
-EOF
-	    else
-	      cat <<EOF
-const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
-EOF
-	    fi
-
-
-	    cat <<"EOF"
-
-#define LTWRAPPER_OPTION_PREFIX         "--lt-"
-
-static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
-static const char *dumpscript_opt       = LTWRAPPER_OPTION_PREFIX "dump-script";
-static const char *debug_opt            = LTWRAPPER_OPTION_PREFIX "debug";
-
-int
-main (int argc, char *argv[])
-{
-  char **newargz;
-  int  newargc;
-  char *tmp_pathspec;
-  char *actual_cwrapper_path;
-  char *actual_cwrapper_name;
-  char *target_name;
-  char *lt_argv_zero;
-  int rval = 127;
-
-  int i;
-
-  program_name = (char *) xstrdup (base_name (argv[0]));
-  newargz = XMALLOC (char *, (size_t) argc + 1);
-
-  /* very simple arg parsing; don't want to rely on getopt
-   * also, copy all non cwrapper options to newargz, except
-   * argz[0], which is handled differently
-   */
-  newargc=0;
-  for (i = 1; i < argc; i++)
-    {
-      if (STREQ (argv[i], dumpscript_opt))
-	{
-EOF
-	    case $host in
-	      *mingw* | *cygwin* )
-		# make stdout use "unix" line endings
-		echo "          setmode(1,_O_BINARY);"
-		;;
-	      esac
-
-	    cat <<"EOF"
-	  lt_dump_script (stdout);
-	  return 0;
-	}
-      if (STREQ (argv[i], debug_opt))
-	{
-          lt_debug = 1;
-          continue;
-	}
-      if (STREQ (argv[i], ltwrapper_option_prefix))
-        {
-          /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
-             namespace, but it is not one of the ones we know about and
-             have already dealt with, above (inluding dump-script), then
-             report an error. Otherwise, targets might begin to believe
-             they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
-             namespace. The first time any user complains about this, we'll
-             need to make LTWRAPPER_OPTION_PREFIX a configure-time option
-             or a configure.ac-settable value.
-           */
-          lt_fatal (__FILE__, __LINE__,
-		    "unrecognized %s option: '%s'",
-                    ltwrapper_option_prefix, argv[i]);
-        }
-      /* otherwise ... */
-      newargz[++newargc] = xstrdup (argv[i]);
-    }
-  newargz[++newargc] = NULL;
-
-EOF
-	    cat <<EOF
-  /* The GNU banner must be the first non-error debug message */
-  lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE) $VERSION\n");
-EOF
-	    cat <<"EOF"
-  lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
-  lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
-
-  tmp_pathspec = find_executable (argv[0]);
-  if (tmp_pathspec == NULL)
-    lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
-  lt_debugprintf (__FILE__, __LINE__,
-                  "(main) found exe (before symlink chase) at: %s\n",
-		  tmp_pathspec);
-
-  actual_cwrapper_path = chase_symlinks (tmp_pathspec);
-  lt_debugprintf (__FILE__, __LINE__,
-                  "(main) found exe (after symlink chase) at: %s\n",
-		  actual_cwrapper_path);
-  XFREE (tmp_pathspec);
-
-  actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
-  strendzap (actual_cwrapper_path, actual_cwrapper_name);
-
-  /* wrapper name transforms */
-  strendzap (actual_cwrapper_name, ".exe");
-  tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
-  XFREE (actual_cwrapper_name);
-  actual_cwrapper_name = tmp_pathspec;
-  tmp_pathspec = 0;
-
-  /* target_name transforms -- use actual target program name; might have lt- prefix */
-  target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
-  strendzap (target_name, ".exe");
-  tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
-  XFREE (target_name);
-  target_name = tmp_pathspec;
-  tmp_pathspec = 0;
-
-  lt_debugprintf (__FILE__, __LINE__,
-		  "(main) libtool target name: %s\n",
-		  target_name);
-EOF
-
-	    cat <<EOF
-  newargz[0] =
-    XMALLOC (char, (strlen (actual_cwrapper_path) +
-		    strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
-  strcpy (newargz[0], actual_cwrapper_path);
-  strcat (newargz[0], "$objdir");
-  strcat (newargz[0], "/");
-EOF
-
-	    cat <<"EOF"
-  /* stop here, and copy so we don't have to do this twice */
-  tmp_pathspec = xstrdup (newargz[0]);
-
-  /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
-  strcat (newargz[0], actual_cwrapper_name);
-
-  /* DO want the lt- prefix here if it exists, so use target_name */
-  lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
-  XFREE (tmp_pathspec);
-  tmp_pathspec = NULL;
-EOF
-
-	    case $host_os in
-	      mingw*)
-	    cat <<"EOF"
-  {
-    char* p;
-    while ((p = strchr (newargz[0], '\\')) != NULL)
-      {
-	*p = '/';
-      }
-    while ((p = strchr (lt_argv_zero, '\\')) != NULL)
-      {
-	*p = '/';
-      }
-  }
-EOF
-	    ;;
-	    esac
-
-	    cat <<"EOF"
-  XFREE (target_name);
-  XFREE (actual_cwrapper_path);
-  XFREE (actual_cwrapper_name);
-
-  lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
-  lt_setenv ("DUALCASE", "1");  /* for MSK sh */
-  /* Update the DLL searchpath.  EXE_PATH_VALUE ($dllsearchpath) must
-     be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
-     because on Windows, both *_VARNAMEs are PATH but uninstalled
-     libraries must come first. */
-  lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
-  lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
-
-  lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
-		  nonnull (lt_argv_zero));
-  for (i = 0; i < newargc; i++)
-    {
-      lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
-		      i, nonnull (newargz[i]));
-    }
-
-EOF
-
-	    case $host_os in
-	      mingw*)
-		cat <<"EOF"
-  /* execv doesn't actually work on mingw as expected on unix */
-  newargz = prepare_spawn (newargz);
-  rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
-  if (rval == -1)
-    {
-      /* failed to start process */
-      lt_debugprintf (__FILE__, __LINE__,
-		      "(main) failed to launch target \"%s\": %s\n",
-		      lt_argv_zero, nonnull (strerror (errno)));
-      return 127;
-    }
-  return rval;
-EOF
-		;;
-	      *)
-		cat <<"EOF"
-  execv (lt_argv_zero, newargz);
-  return rval; /* =127, but avoids unused variable warning */
-EOF
-		;;
-	    esac
-
-	    cat <<"EOF"
-}
-
-void *
-xmalloc (size_t num)
-{
-  void *p = (void *) malloc (num);
-  if (!p)
-    lt_fatal (__FILE__, __LINE__, "memory exhausted");
-
-  return p;
-}
-
-char *
-xstrdup (const char *string)
-{
-  return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
-			  string) : NULL;
-}
-
-const char *
-base_name (const char *name)
-{
-  const char *base;
-
-#if defined HAVE_DOS_BASED_FILE_SYSTEM
-  /* Skip over the disk name in MSDOS pathnames. */
-  if (isalpha ((unsigned char) name[0]) && name[1] == ':')
-    name += 2;
-#endif
-
-  for (base = name; *name; name++)
-    if (IS_DIR_SEPARATOR (*name))
-      base = name + 1;
-  return base;
-}
-
-int
-check_executable (const char *path)
-{
-  struct stat st;
-
-  lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
-                  nonempty (path));
-  if ((!path) || (!*path))
-    return 0;
-
-  if ((stat (path, &st) >= 0)
-      && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
-    return 1;
-  else
-    return 0;
-}
-
-int
-make_executable (const char *path)
-{
-  int rval = 0;
-  struct stat st;
-
-  lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
-                  nonempty (path));
-  if ((!path) || (!*path))
-    return 0;
-
-  if (stat (path, &st) >= 0)
-    {
-      rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
-    }
-  return rval;
-}
-
-/* Searches for the full path of the wrapper.  Returns
-   newly allocated full path name if found, NULL otherwise
-   Does not chase symlinks, even on platforms that support them.
-*/
-char *
-find_executable (const char *wrapper)
-{
-  int has_slash = 0;
-  const char *p;
-  const char *p_next;
-  /* static buffer for getcwd */
-  char tmp[LT_PATHMAX + 1];
-  size_t tmp_len;
-  char *concat_name;
-
-  lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
-                  nonempty (wrapper));
-
-  if ((wrapper == NULL) || (*wrapper == '\0'))
-    return NULL;
-
-  /* Absolute path? */
-#if defined HAVE_DOS_BASED_FILE_SYSTEM
-  if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
-    {
-      concat_name = xstrdup (wrapper);
-      if (check_executable (concat_name))
-	return concat_name;
-      XFREE (concat_name);
-    }
-  else
-    {
-#endif
-      if (IS_DIR_SEPARATOR (wrapper[0]))
-	{
-	  concat_name = xstrdup (wrapper);
-	  if (check_executable (concat_name))
-	    return concat_name;
-	  XFREE (concat_name);
-	}
-#if defined HAVE_DOS_BASED_FILE_SYSTEM
-    }
-#endif
-
-  for (p = wrapper; *p; p++)
-    if (*p == '/')
-      {
-	has_slash = 1;
-	break;
-      }
-  if (!has_slash)
-    {
-      /* no slashes; search PATH */
-      const char *path = getenv ("PATH");
-      if (path != NULL)
-	{
-	  for (p = path; *p; p = p_next)
-	    {
-	      const char *q;
-	      size_t p_len;
-	      for (q = p; *q; q++)
-		if (IS_PATH_SEPARATOR (*q))
-		  break;
-	      p_len = (size_t) (q - p);
-	      p_next = (*q == '\0' ? q : q + 1);
-	      if (p_len == 0)
-		{
-		  /* empty path: current directory */
-		  if (getcwd (tmp, LT_PATHMAX) == NULL)
-		    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
-                              nonnull (strerror (errno)));
-		  tmp_len = strlen (tmp);
-		  concat_name =
-		    XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
-		  memcpy (concat_name, tmp, tmp_len);
-		  concat_name[tmp_len] = '/';
-		  strcpy (concat_name + tmp_len + 1, wrapper);
-		}
-	      else
-		{
-		  concat_name =
-		    XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
-		  memcpy (concat_name, p, p_len);
-		  concat_name[p_len] = '/';
-		  strcpy (concat_name + p_len + 1, wrapper);
-		}
-	      if (check_executable (concat_name))
-		return concat_name;
-	      XFREE (concat_name);
-	    }
-	}
-      /* not found in PATH; assume curdir */
-    }
-  /* Relative path | not found in path: prepend cwd */
-  if (getcwd (tmp, LT_PATHMAX) == NULL)
-    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
-              nonnull (strerror (errno)));
-  tmp_len = strlen (tmp);
-  concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
-  memcpy (concat_name, tmp, tmp_len);
-  concat_name[tmp_len] = '/';
-  strcpy (concat_name + tmp_len + 1, wrapper);
-
-  if (check_executable (concat_name))
-    return concat_name;
-  XFREE (concat_name);
-  return NULL;
-}
-
-char *
-chase_symlinks (const char *pathspec)
-{
-#ifndef S_ISLNK
-  return xstrdup (pathspec);
-#else
-  char buf[LT_PATHMAX];
-  struct stat s;
-  char *tmp_pathspec = xstrdup (pathspec);
-  char *p;
-  int has_symlinks = 0;
-  while (strlen (tmp_pathspec) && !has_symlinks)
-    {
-      lt_debugprintf (__FILE__, __LINE__,
-		      "checking path component for symlinks: %s\n",
-		      tmp_pathspec);
-      if (lstat (tmp_pathspec, &s) == 0)
-	{
-	  if (S_ISLNK (s.st_mode) != 0)
-	    {
-	      has_symlinks = 1;
-	      break;
-	    }
-
-	  /* search backwards for last DIR_SEPARATOR */
-	  p = tmp_pathspec + strlen (tmp_pathspec) - 1;
-	  while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
-	    p--;
-	  if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
-	    {
-	      /* no more DIR_SEPARATORS left */
-	      break;
-	    }
-	  *p = '\0';
-	}
-      else
-	{
-	  lt_fatal (__FILE__, __LINE__,
-		    "error accessing file \"%s\": %s",
-		    tmp_pathspec, nonnull (strerror (errno)));
-	}
-    }
-  XFREE (tmp_pathspec);
-
-  if (!has_symlinks)
-    {
-      return xstrdup (pathspec);
-    }
-
-  tmp_pathspec = realpath (pathspec, buf);
-  if (tmp_pathspec == 0)
-    {
-      lt_fatal (__FILE__, __LINE__,
-		"could not follow symlinks for %s", pathspec);
-    }
-  return xstrdup (tmp_pathspec);
-#endif
-}
-
-char *
-strendzap (char *str, const char *pat)
-{
-  size_t len, patlen;
-
-  assert (str != NULL);
-  assert (pat != NULL);
-
-  len = strlen (str);
-  patlen = strlen (pat);
-
-  if (patlen <= len)
-    {
-      str += len - patlen;
-      if (STREQ (str, pat))
-	*str = '\0';
-    }
-  return str;
-}
-
-void
-lt_debugprintf (const char *file, int line, const char *fmt, ...)
-{
-  va_list args;
-  if (lt_debug)
-    {
-      (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
-      va_start (args, fmt);
-      (void) vfprintf (stderr, fmt, args);
-      va_end (args);
-    }
-}
-
-static void
-lt_error_core (int exit_status, const char *file,
-	       int line, const char *mode,
-	       const char *message, va_list ap)
-{
-  fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
-  vfprintf (stderr, message, ap);
-  fprintf (stderr, ".\n");
-
-  if (exit_status >= 0)
-    exit (exit_status);
-}
-
-void
-lt_fatal (const char *file, int line, const char *message, ...)
-{
-  va_list ap;
-  va_start (ap, message);
-  lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
-  va_end (ap);
-}
-
-static const char *
-nonnull (const char *s)
-{
-  return s ? s : "(null)";
-}
-
-static const char *
-nonempty (const char *s)
-{
-  return (s && !*s) ? "(empty)" : nonnull (s);
-}
-
-void
-lt_setenv (const char *name, const char *value)
-{
-  lt_debugprintf (__FILE__, __LINE__,
-		  "(lt_setenv) setting '%s' to '%s'\n",
-                  nonnull (name), nonnull (value));
-  {
-#ifdef HAVE_SETENV
-    /* always make a copy, for consistency with !HAVE_SETENV */
-    char *str = xstrdup (value);
-    setenv (name, str, 1);
-#else
-    size_t len = strlen (name) + 1 + strlen (value) + 1;
-    char *str = XMALLOC (char, len);
-    sprintf (str, "%s=%s", name, value);
-    if (putenv (str) != EXIT_SUCCESS)
-      {
-        XFREE (str);
-      }
-#endif
-  }
-}
-
-char *
-lt_extend_str (const char *orig_value, const char *add, int to_end)
-{
-  char *new_value;
-  if (orig_value && *orig_value)
-    {
-      size_t orig_value_len = strlen (orig_value);
-      size_t add_len = strlen (add);
-      new_value = XMALLOC (char, add_len + orig_value_len + 1);
-      if (to_end)
-        {
-          strcpy (new_value, orig_value);
-          strcpy (new_value + orig_value_len, add);
-        }
-      else
-        {
-          strcpy (new_value, add);
-          strcpy (new_value + add_len, orig_value);
-        }
-    }
-  else
-    {
-      new_value = xstrdup (add);
-    }
-  return new_value;
-}
-
-void
-lt_update_exe_path (const char *name, const char *value)
-{
-  lt_debugprintf (__FILE__, __LINE__,
-		  "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
-                  nonnull (name), nonnull (value));
-
-  if (name && *name && value && *value)
-    {
-      char *new_value = lt_extend_str (getenv (name), value, 0);
-      /* some systems can't cope with a ':'-terminated path #' */
-      size_t len = strlen (new_value);
-      while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
-        {
-          new_value[--len] = '\0';
-        }
-      lt_setenv (name, new_value);
-      XFREE (new_value);
-    }
-}
-
-void
-lt_update_lib_path (const char *name, const char *value)
-{
-  lt_debugprintf (__FILE__, __LINE__,
-		  "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
-                  nonnull (name), nonnull (value));
-
-  if (name && *name && value && *value)
-    {
-      char *new_value = lt_extend_str (getenv (name), value, 0);
-      lt_setenv (name, new_value);
-      XFREE (new_value);
-    }
-}
-
-EOF
-	    case $host_os in
-	      mingw*)
-		cat <<"EOF"
-
-/* Prepares an argument vector before calling spawn().
-   Note that spawn() does not by itself call the command interpreter
-     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
-      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-         GetVersionEx(&v);
-         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
-      }) ? "cmd.exe" : "command.com").
-   Instead it simply concatenates the arguments, separated by ' ', and calls
-   CreateProcess().  We must quote the arguments since Win32 CreateProcess()
-   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
-   special way:
-   - Space and tab are interpreted as delimiters. They are not treated as
-     delimiters if they are surrounded by double quotes: "...".
-   - Unescaped double quotes are removed from the input. Their only effect is
-     that within double quotes, space and tab are treated like normal
-     characters.
-   - Backslashes not followed by double quotes are not special.
-   - But 2*n+1 backslashes followed by a double quote become
-     n backslashes followed by a double quote (n >= 0):
-       \" -> "
-       \\\" -> \"
-       \\\\\" -> \\"
- */
-#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
-#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
-char **
-prepare_spawn (char **argv)
-{
-  size_t argc;
-  char **new_argv;
-  size_t i;
-
-  /* Count number of arguments.  */
-  for (argc = 0; argv[argc] != NULL; argc++)
-    ;
-
-  /* Allocate new argument vector.  */
-  new_argv = XMALLOC (char *, argc + 1);
-
-  /* Put quoted arguments into the new argument vector.  */
-  for (i = 0; i < argc; i++)
-    {
-      const char *string = argv[i];
-
-      if (string[0] == '\0')
-	new_argv[i] = xstrdup ("\"\"");
-      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
-	{
-	  int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
-	  size_t length;
-	  unsigned int backslashes;
-	  const char *s;
-	  char *quoted_string;
-	  char *p;
-
-	  length = 0;
-	  backslashes = 0;
-	  if (quote_around)
-	    length++;
-	  for (s = string; *s != '\0'; s++)
-	    {
-	      char c = *s;
-	      if (c == '"')
-		length += backslashes + 1;
-	      length++;
-	      if (c == '\\')
-		backslashes++;
-	      else
-		backslashes = 0;
-	    }
-	  if (quote_around)
-	    length += backslashes + 1;
-
-	  quoted_string = XMALLOC (char, length + 1);
-
-	  p = quoted_string;
-	  backslashes = 0;
-	  if (quote_around)
-	    *p++ = '"';
-	  for (s = string; *s != '\0'; s++)
-	    {
-	      char c = *s;
-	      if (c == '"')
-		{
-		  unsigned int j;
-		  for (j = backslashes + 1; j > 0; j--)
-		    *p++ = '\\';
-		}
-	      *p++ = c;
-	      if (c == '\\')
-		backslashes++;
-	      else
-		backslashes = 0;
-	    }
-	  if (quote_around)
-	    {
-	      unsigned int j;
-	      for (j = backslashes; j > 0; j--)
-		*p++ = '\\';
-	      *p++ = '"';
-	    }
-	  *p = '\0';
-
-	  new_argv[i] = quoted_string;
-	}
-      else
-	new_argv[i] = (char *) string;
-    }
-  new_argv[argc] = NULL;
-
-  return new_argv;
-}
-EOF
-		;;
-	    esac
-
-            cat <<"EOF"
-void lt_dump_script (FILE* f)
-{
-EOF
-	    func_emit_wrapper yes |
-	      $SED -n -e '
-s/^\(.\{79\}\)\(..*\)/\1\
-\2/
-h
-s/\([\\"]\)/\\\1/g
-s/$/\\n/
-s/\([^\n]*\).*/  fputs ("\1", f);/p
-g
-D'
-            cat <<"EOF"
-}
-EOF
-}
-# end: func_emit_cwrapperexe_src
-
-# func_win32_import_lib_p ARG
-# True if ARG is an import lib, as indicated by $file_magic_cmd
-func_win32_import_lib_p ()
-{
-    $debug_cmd
-
-    case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
-    *import*) : ;;
-    *) false ;;
-    esac
-}
-
-# func_suncc_cstd_abi
-# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!!
-# Several compiler flags select an ABI that is incompatible with the
-# Cstd library. Avoid specifying it if any are in CXXFLAGS.
-func_suncc_cstd_abi ()
-{
-    $debug_cmd
-
-    case " $compile_command " in
-    *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*)
-      suncc_use_cstd_abi=no
-      ;;
-    *)
-      suncc_use_cstd_abi=yes
-      ;;
-    esac
-}
-
-# func_mode_link arg...
-func_mode_link ()
-{
-    $debug_cmd
-
-    case $host in
-    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
-      # It is impossible to link a dll without this setting, and
-      # we shouldn't force the makefile maintainer to figure out
-      # what system we are compiling for in order to pass an extra
-      # flag for every libtool invocation.
-      # allow_undefined=no
-
-      # FIXME: Unfortunately, there are problems with the above when trying
-      # to make a dll that has undefined symbols, in which case not
-      # even a static library is built.  For now, we need to specify
-      # -no-undefined on the libtool link line when we can be certain
-      # that all symbols are satisfied, otherwise we get a static library.
-      allow_undefined=yes
-      ;;
-    *)
-      allow_undefined=yes
-      ;;
-    esac
-    libtool_args=$nonopt
-    base_compile="$nonopt $@"
-    compile_command=$nonopt
-    finalize_command=$nonopt
-
-    compile_rpath=
-    finalize_rpath=
-    compile_shlibpath=
-    finalize_shlibpath=
-    convenience=
-    old_convenience=
-    deplibs=
-    old_deplibs=
-    compiler_flags=
-    linker_flags=
-    dllsearchpath=
-    lib_search_path=`pwd`
-    inst_prefix_dir=
-    new_inherited_linker_flags=
-
-    avoid_version=no
-    bindir=
-    dlfiles=
-    dlprefiles=
-    dlself=no
-    export_dynamic=no
-    export_symbols=
-    export_symbols_regex=
-    generated=
-    libobjs=
-    ltlibs=
-    module=no
-    no_install=no
-    objs=
-    os2dllname=
-    non_pic_objects=
-    precious_files_regex=
-    prefer_static_libs=no
-    preload=false
-    prev=
-    prevarg=
-    release=
-    rpath=
-    xrpath=
-    perm_rpath=
-    temp_rpath=
-    thread_safe=no
-    vinfo=
-    vinfo_number=no
-    weak_libs=
-    single_module=$wl-single_module
-    func_infer_tag $base_compile
-
-    # We need to know -static, to get the right output filenames.
-    for arg
-    do
-      case $arg in
-      -shared)
-	test yes != "$build_libtool_libs" \
-	  && func_fatal_configuration "cannot build a shared library"
-	build_old_libs=no
-	break
-	;;
-      -all-static | -static | -static-libtool-libs)
-	case $arg in
-	-all-static)
-	  if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then
-	    func_warning "complete static linking is impossible in this configuration"
-	  fi
-	  if test -n "$link_static_flag"; then
-	    dlopen_self=$dlopen_self_static
-	  fi
-	  prefer_static_libs=yes
-	  ;;
-	-static)
-	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
-	    dlopen_self=$dlopen_self_static
-	  fi
-	  prefer_static_libs=built
-	  ;;
-	-static-libtool-libs)
-	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
-	    dlopen_self=$dlopen_self_static
-	  fi
-	  prefer_static_libs=yes
-	  ;;
-	esac
-	build_libtool_libs=no
-	build_old_libs=yes
-	break
-	;;
-      esac
-    done
-
-    # See if our shared archives depend on static archives.
-    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
-
-    # Go through the arguments, transforming them on the way.
-    while test "$#" -gt 0; do
-      arg=$1
-      shift
-      func_quote_for_eval "$arg"
-      qarg=$func_quote_for_eval_unquoted_result
-      func_append libtool_args " $func_quote_for_eval_result"
-
-      # If the previous option needs an argument, assign it.
-      if test -n "$prev"; then
-	case $prev in
-	output)
-	  func_append compile_command " @OUTPUT@"
-	  func_append finalize_command " @OUTPUT@"
-	  ;;
-	esac
-
-	case $prev in
-	bindir)
-	  bindir=$arg
-	  prev=
-	  continue
-	  ;;
-	dlfiles|dlprefiles)
-	  $preload || {
-	    # Add the symbol object into the linking commands.
-	    func_append compile_command " @SYMFILE@"
-	    func_append finalize_command " @SYMFILE@"
-	    preload=:
-	  }
-	  case $arg in
-	  *.la | *.lo) ;;  # We handle these cases below.
-	  force)
-	    if test no = "$dlself"; then
-	      dlself=needless
-	      export_dynamic=yes
-	    fi
-	    prev=
-	    continue
-	    ;;
-	  self)
-	    if test dlprefiles = "$prev"; then
-	      dlself=yes
-	    elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then
-	      dlself=yes
-	    else
-	      dlself=needless
-	      export_dynamic=yes
-	    fi
-	    prev=
-	    continue
-	    ;;
-	  *)
-	    if test dlfiles = "$prev"; then
-	      func_append dlfiles " $arg"
-	    else
-	      func_append dlprefiles " $arg"
-	    fi
-	    prev=
-	    continue
-	    ;;
-	  esac
-	  ;;
-	expsyms)
-	  export_symbols=$arg
-	  test -f "$arg" \
-	    || func_fatal_error "symbol file '$arg' does not exist"
-	  prev=
-	  continue
-	  ;;
-	expsyms_regex)
-	  export_symbols_regex=$arg
-	  prev=
-	  continue
-	  ;;
-	framework)
-	  case $host in
-	    *-*-darwin*)
-	      case "$deplibs " in
-		*" $qarg.ltframework "*) ;;
-		*) func_append deplibs " $qarg.ltframework" # this is fixed later
-		   ;;
-	      esac
-	      ;;
-	  esac
-	  prev=
-	  continue
-	  ;;
-	inst_prefix)
-	  inst_prefix_dir=$arg
-	  prev=
-	  continue
-	  ;;
-	mllvm)
-	  # Clang does not use LLVM to link, so we can simply discard any
-	  # '-mllvm $arg' options when doing the link step.
-	  prev=
-	  continue
-	  ;;
-	objectlist)
-	  if test -f "$arg"; then
-	    save_arg=$arg
-	    moreargs=
-	    for fil in `cat "$save_arg"`
-	    do
-#	      func_append moreargs " $fil"
-	      arg=$fil
-	      # A libtool-controlled object.
-
-	      # Check to see that this really is a libtool object.
-	      if func_lalib_unsafe_p "$arg"; then
-		pic_object=
-		non_pic_object=
-
-		# Read the .lo file
-		func_source "$arg"
-
-		if test -z "$pic_object" ||
-		   test -z "$non_pic_object" ||
-		   test none = "$pic_object" &&
-		   test none = "$non_pic_object"; then
-		  func_fatal_error "cannot find name of object for '$arg'"
-		fi
-
-		# Extract subdirectory from the argument.
-		func_dirname "$arg" "/" ""
-		xdir=$func_dirname_result
-
-		if test none != "$pic_object"; then
-		  # Prepend the subdirectory the object is found in.
-		  pic_object=$xdir$pic_object
-
-		  if test dlfiles = "$prev"; then
-		    if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
-		      func_append dlfiles " $pic_object"
-		      prev=
-		      continue
-		    else
-		      # If libtool objects are unsupported, then we need to preload.
-		      prev=dlprefiles
-		    fi
-		  fi
-
-		  # CHECK ME:  I think I busted this.  -Ossama
-		  if test dlprefiles = "$prev"; then
-		    # Preload the old-style object.
-		    func_append dlprefiles " $pic_object"
-		    prev=
-		  fi
-
-		  # A PIC object.
-		  func_append libobjs " $pic_object"
-		  arg=$pic_object
-		fi
-
-		# Non-PIC object.
-		if test none != "$non_pic_object"; then
-		  # Prepend the subdirectory the object is found in.
-		  non_pic_object=$xdir$non_pic_object
-
-		  # A standard non-PIC object
-		  func_append non_pic_objects " $non_pic_object"
-		  if test -z "$pic_object" || test none = "$pic_object"; then
-		    arg=$non_pic_object
-		  fi
-		else
-		  # If the PIC object exists, use it instead.
-		  # $xdir was prepended to $pic_object above.
-		  non_pic_object=$pic_object
-		  func_append non_pic_objects " $non_pic_object"
-		fi
-	      else
-		# Only an error if not doing a dry-run.
-		if $opt_dry_run; then
-		  # Extract subdirectory from the argument.
-		  func_dirname "$arg" "/" ""
-		  xdir=$func_dirname_result
-
-		  func_lo2o "$arg"
-		  pic_object=$xdir$objdir/$func_lo2o_result
-		  non_pic_object=$xdir$func_lo2o_result
-		  func_append libobjs " $pic_object"
-		  func_append non_pic_objects " $non_pic_object"
-	        else
-		  func_fatal_error "'$arg' is not a valid libtool object"
-		fi
-	      fi
-	    done
-	  else
-	    func_fatal_error "link input file '$arg' does not exist"
-	  fi
-	  arg=$save_arg
-	  prev=
-	  continue
-	  ;;
-	os2dllname)
-	  os2dllname=$arg
-	  prev=
-	  continue
-	  ;;
-	precious_regex)
-	  precious_files_regex=$arg
-	  prev=
-	  continue
-	  ;;
-	release)
-	  release=-$arg
-	  prev=
-	  continue
-	  ;;
-	rpath | xrpath)
-	  # We need an absolute path.
-	  case $arg in
-	  [\\/]* | [A-Za-z]:[\\/]*) ;;
-	  *)
-	    func_fatal_error "only absolute run-paths are allowed"
-	    ;;
-	  esac
-	  if test rpath = "$prev"; then
-	    case "$rpath " in
-	    *" $arg "*) ;;
-	    *) func_append rpath " $arg" ;;
-	    esac
-	  else
-	    case "$xrpath " in
-	    *" $arg "*) ;;
-	    *) func_append xrpath " $arg" ;;
-	    esac
-	  fi
-	  prev=
-	  continue
-	  ;;
-	shrext)
-	  shrext_cmds=$arg
-	  prev=
-	  continue
-	  ;;
-	weak)
-	  func_append weak_libs " $arg"
-	  prev=
-	  continue
-	  ;;
-	xcclinker)
-	  func_append linker_flags " $qarg"
-	  func_append compiler_flags " $qarg"
-	  prev=
-	  func_append compile_command " $qarg"
-	  func_append finalize_command " $qarg"
-	  continue
-	  ;;
-	xcompiler)
-	  func_append compiler_flags " $qarg"
-	  prev=
-	  func_append compile_command " $qarg"
-	  func_append finalize_command " $qarg"
-	  continue
-	  ;;
-	xlinker)
-	  func_append linker_flags " $qarg"
-	  func_append compiler_flags " $wl$qarg"
-	  prev=
-	  func_append compile_command " $wl$qarg"
-	  func_append finalize_command " $wl$qarg"
-	  continue
-	  ;;
-	*)
-	  eval "$prev=\"\$arg\""
-	  prev=
-	  continue
-	  ;;
-	esac
-      fi # test -n "$prev"
-
-      prevarg=$arg
-
-      case $arg in
-      -all-static)
-	if test -n "$link_static_flag"; then
-	  # See comment for -static flag below, for more details.
-	  func_append compile_command " $link_static_flag"
-	  func_append finalize_command " $link_static_flag"
-	fi
-	continue
-	;;
-
-      -allow-undefined)
-	# FIXME: remove this flag sometime in the future.
-	func_fatal_error "'-allow-undefined' must not be used because it is the default"
-	;;
-
-      -avoid-version)
-	avoid_version=yes
-	continue
-	;;
-
-      -bindir)
-	prev=bindir
-	continue
-	;;
-
-      -dlopen)
-	prev=dlfiles
-	continue
-	;;
-
-      -dlpreopen)
-	prev=dlprefiles
-	continue
-	;;
-
-      -export-dynamic)
-	export_dynamic=yes
-	continue
-	;;
-
-      -export-symbols | -export-symbols-regex)
-	if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
-	  func_fatal_error "more than one -exported-symbols argument is not allowed"
-	fi
-	if test X-export-symbols = "X$arg"; then
-	  prev=expsyms
-	else
-	  prev=expsyms_regex
-	fi
-	continue
-	;;
-
-      -framework)
-	prev=framework
-	continue
-	;;
-
-      -inst-prefix-dir)
-	prev=inst_prefix
-	continue
-	;;
-
-      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
-      # so, if we see these flags be careful not to treat them like -L
-      -L[A-Z][A-Z]*:*)
-	case $with_gcc/$host in
-	no/*-*-irix* | /*-*-irix*)
-	  func_append compile_command " $arg"
-	  func_append finalize_command " $arg"
-	  ;;
-	esac
-	continue
-	;;
-
-      -L*)
-	func_stripname "-L" '' "$arg"
-	if test -z "$func_stripname_result"; then
-	  if test "$#" -gt 0; then
-	    func_fatal_error "require no space between '-L' and '$1'"
-	  else
-	    func_fatal_error "need path for '-L' option"
-	  fi
-	fi
-	func_resolve_sysroot "$func_stripname_result"
-	dir=$func_resolve_sysroot_result
-	# We need an absolute path.
-	case $dir in
-	[\\/]* | [A-Za-z]:[\\/]*) ;;
-	*)
-	  absdir=`cd "$dir" && pwd`
-	  test -z "$absdir" && \
-	    func_fatal_error "cannot determine absolute directory name of '$dir'"
-	  dir=$absdir
-	  ;;
-	esac
-	case "$deplibs " in
-	*" -L$dir "* | *" $arg "*)
-	  # Will only happen for absolute or sysroot arguments
-	  ;;
-	*)
-	  # Preserve sysroot, but never include relative directories
-	  case $dir in
-	    [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
-	    *) func_append deplibs " -L$dir" ;;
-	  esac
-	  func_append lib_search_path " $dir"
-	  ;;
-	esac
-	case $host in
-	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
-	  testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
-	  case :$dllsearchpath: in
-	  *":$dir:"*) ;;
-	  ::) dllsearchpath=$dir;;
-	  *) func_append dllsearchpath ":$dir";;
-	  esac
-	  case :$dllsearchpath: in
-	  *":$testbindir:"*) ;;
-	  ::) dllsearchpath=$testbindir;;
-	  *) func_append dllsearchpath ":$testbindir";;
-	  esac
-	  ;;
-	esac
-	continue
-	;;
-
-      -l*)
-	if test X-lc = "X$arg" || test X-lm = "X$arg"; then
-	  case $host in
-	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
-	    # These systems don't actually have a C or math library (as such)
-	    continue
-	    ;;
-	  *-*-os2*)
-	    # These systems don't actually have a C library (as such)
-	    test X-lc = "X$arg" && continue
-	    ;;
-	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
-	    # Do not include libc due to us having libc/libc_r.
-	    test X-lc = "X$arg" && continue
-	    ;;
-	  *-*-rhapsody* | *-*-darwin1.[012])
-	    # Rhapsody C and math libraries are in the System framework
-	    func_append deplibs " System.ltframework"
-	    continue
-	    ;;
-	  *-*-sco3.2v5* | *-*-sco5v6*)
-	    # Causes problems with __ctype
-	    test X-lc = "X$arg" && continue
-	    ;;
-	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
-	    # Compiler inserts libc in the correct place for threads to work
-	    test X-lc = "X$arg" && continue
-	    ;;
-	  esac
-	elif test X-lc_r = "X$arg"; then
-	 case $host in
-	 *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
-	   # Do not include libc_r directly, use -pthread flag.
-	   continue
-	   ;;
-	 esac
-	fi
-	func_append deplibs " $arg"
-	continue
-	;;
-
-      -mllvm)
-	prev=mllvm
-	continue
-	;;
-
-      -module)
-	module=yes
-	continue
-	;;
-
-      # Tru64 UNIX uses -model [arg] to determine the layout of C++
-      # classes, name mangling, and exception handling.
-      # Darwin uses the -arch flag to determine output architecture.
-      -model|-arch|-isysroot|--sysroot)
-	func_append compiler_flags " $arg"
-	func_append compile_command " $arg"
-	func_append finalize_command " $arg"
-	prev=xcompiler
-	continue
-	;;
-
-      -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
-      |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
-	func_append compiler_flags " $arg"
-	func_append compile_command " $arg"
-	func_append finalize_command " $arg"
-	case "$new_inherited_linker_flags " in
-	    *" $arg "*) ;;
-	    * ) func_append new_inherited_linker_flags " $arg" ;;
-	esac
-	continue
-	;;
-
-      -multi_module)
-	single_module=$wl-multi_module
-	continue
-	;;
-
-      -no-fast-install)
-	fast_install=no
-	continue
-	;;
-
-      -no-install)
-	case $host in
-	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
-	  # The PATH hackery in wrapper scripts is required on Windows
-	  # and Darwin in order for the loader to find any dlls it needs.
-	  func_warning "'-no-install' is ignored for $host"
-	  func_warning "assuming '-no-fast-install' instead"
-	  fast_install=no
-	  ;;
-	*) no_install=yes ;;
-	esac
-	continue
-	;;
-
-      -no-undefined)
-	allow_undefined=no
-	continue
-	;;
-
-      -objectlist)
-	prev=objectlist
-	continue
-	;;
-
-      -os2dllname)
-	prev=os2dllname
-	continue
-	;;
-
-      -o) prev=output ;;
-
-      -precious-files-regex)
-	prev=precious_regex
-	continue
-	;;
-
-      -release)
-	prev=release
-	continue
-	;;
-
-      -rpath)
-	prev=rpath
-	continue
-	;;
-
-      -R)
-	prev=xrpath
-	continue
-	;;
-
-      -R*)
-	func_stripname '-R' '' "$arg"
-	dir=$func_stripname_result
-	# We need an absolute path.
-	case $dir in
-	[\\/]* | [A-Za-z]:[\\/]*) ;;
-	=*)
-	  func_stripname '=' '' "$dir"
-	  dir=$lt_sysroot$func_stripname_result
-	  ;;
-	*)
-	  func_fatal_error "only absolute run-paths are allowed"
-	  ;;
-	esac
-	case "$xrpath " in
-	*" $dir "*) ;;
-	*) func_append xrpath " $dir" ;;
-	esac
-	continue
-	;;
-
-      -shared)
-	# The effects of -shared are defined in a previous loop.
-	continue
-	;;
-
-      -shrext)
-	prev=shrext
-	continue
-	;;
-
-      -static | -static-libtool-libs)
-	# The effects of -static are defined in a previous loop.
-	# We used to do the same as -all-static on platforms that
-	# didn't have a PIC flag, but the assumption that the effects
-	# would be equivalent was wrong.  It would break on at least
-	# Digital Unix and AIX.
-	continue
-	;;
-
-      -thread-safe)
-	thread_safe=yes
-	continue
-	;;
-
-      -version-info)
-	prev=vinfo
-	continue
-	;;
-
-      -version-number)
-	prev=vinfo
-	vinfo_number=yes
-	continue
-	;;
-
-      -weak)
-        prev=weak
-	continue
-	;;
-
-      -Wc,*)
-	func_stripname '-Wc,' '' "$arg"
-	args=$func_stripname_result
-	arg=
-	save_ifs=$IFS; IFS=,
-	for flag in $args; do
-	  IFS=$save_ifs
-          func_quote_for_eval "$flag"
-	  func_append arg " $func_quote_for_eval_result"
-	  func_append compiler_flags " $func_quote_for_eval_result"
-	done
-	IFS=$save_ifs
-	func_stripname ' ' '' "$arg"
-	arg=$func_stripname_result
-	;;
-
-      -Wl,*)
-	func_stripname '-Wl,' '' "$arg"
-	args=$func_stripname_result
-	arg=
-	save_ifs=$IFS; IFS=,
-	for flag in $args; do
-	  IFS=$save_ifs
-          func_quote_for_eval "$flag"
-	  func_append arg " $wl$func_quote_for_eval_result"
-	  func_append compiler_flags " $wl$func_quote_for_eval_result"
-	  func_append linker_flags " $func_quote_for_eval_result"
-	done
-	IFS=$save_ifs
-	func_stripname ' ' '' "$arg"
-	arg=$func_stripname_result
-	;;
-
-      -Xcompiler)
-	prev=xcompiler
-	continue
-	;;
-
-      -Xlinker)
-	prev=xlinker
-	continue
-	;;
-
-      -XCClinker)
-	prev=xcclinker
-	continue
-	;;
-
-      # -msg_* for osf cc
-      -msg_*)
-	func_quote_for_eval "$arg"
-	arg=$func_quote_for_eval_result
-	;;
-
-      # Flags to be passed through unchanged, with rationale:
-      # -64, -mips[0-9]      enable 64-bit mode for the SGI compiler
-      # -r[0-9][0-9]*        specify processor for the SGI compiler
-      # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
-      # +DA*, +DD*           enable 64-bit mode for the HP compiler
-      # -q*                  compiler args for the IBM compiler
-      # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
-      # -F/path              path to uninstalled frameworks, gcc on darwin
-      # -p, -pg, --coverage, -fprofile-*  profiling flags for GCC
-      # -fstack-protector*   stack protector flags for GCC
-      # @file                GCC response files
-      # -tp=*                Portland pgcc target processor selection
-      # --sysroot=*          for sysroot support
-      # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
-      # -stdlib=*            select c++ std lib with clang
-      -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
-      -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
-      -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*)
-        func_quote_for_eval "$arg"
-	arg=$func_quote_for_eval_result
-        func_append compile_command " $arg"
-        func_append finalize_command " $arg"
-        func_append compiler_flags " $arg"
-        continue
-        ;;
-
-      -Z*)
-        if test os2 = "`expr $host : '.*\(os2\)'`"; then
-          # OS/2 uses -Zxxx to specify OS/2-specific options
-	  compiler_flags="$compiler_flags $arg"
-	  func_append compile_command " $arg"
-	  func_append finalize_command " $arg"
-	  case $arg in
-	  -Zlinker | -Zstack)
-	    prev=xcompiler
-	    ;;
-	  esac
-	  continue
-        else
-	  # Otherwise treat like 'Some other compiler flag' below
-	  func_quote_for_eval "$arg"
-	  arg=$func_quote_for_eval_result
-        fi
-	;;
-
-      # Some other compiler flag.
-      -* | +*)
-        func_quote_for_eval "$arg"
-	arg=$func_quote_for_eval_result
-	;;
-
-      *.$objext)
-	# A standard object.
-	func_append objs " $arg"
-	;;
-
-      *.lo)
-	# A libtool-controlled object.
-
-	# Check to see that this really is a libtool object.
-	if func_lalib_unsafe_p "$arg"; then
-	  pic_object=
-	  non_pic_object=
-
-	  # Read the .lo file
-	  func_source "$arg"
-
-	  if test -z "$pic_object" ||
-	     test -z "$non_pic_object" ||
-	     test none = "$pic_object" &&
-	     test none = "$non_pic_object"; then
-	    func_fatal_error "cannot find name of object for '$arg'"
-	  fi
-
-	  # Extract subdirectory from the argument.
-	  func_dirname "$arg" "/" ""
-	  xdir=$func_dirname_result
-
-	  test none = "$pic_object" || {
-	    # Prepend the subdirectory the object is found in.
-	    pic_object=$xdir$pic_object
-
-	    if test dlfiles = "$prev"; then
-	      if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
-		func_append dlfiles " $pic_object"
-		prev=
-		continue
-	      else
-		# If libtool objects are unsupported, then we need to preload.
-		prev=dlprefiles
-	      fi
-	    fi
-
-	    # CHECK ME:  I think I busted this.  -Ossama
-	    if test dlprefiles = "$prev"; then
-	      # Preload the old-style object.
-	      func_append dlprefiles " $pic_object"
-	      prev=
-	    fi
-
-	    # A PIC object.
-	    func_append libobjs " $pic_object"
-	    arg=$pic_object
-	  }
-
-	  # Non-PIC object.
-	  if test none != "$non_pic_object"; then
-	    # Prepend the subdirectory the object is found in.
-	    non_pic_object=$xdir$non_pic_object
-
-	    # A standard non-PIC object
-	    func_append non_pic_objects " $non_pic_object"
-	    if test -z "$pic_object" || test none = "$pic_object"; then
-	      arg=$non_pic_object
-	    fi
-	  else
-	    # If the PIC object exists, use it instead.
-	    # $xdir was prepended to $pic_object above.
-	    non_pic_object=$pic_object
-	    func_append non_pic_objects " $non_pic_object"
-	  fi
-	else
-	  # Only an error if not doing a dry-run.
-	  if $opt_dry_run; then
-	    # Extract subdirectory from the argument.
-	    func_dirname "$arg" "/" ""
-	    xdir=$func_dirname_result
-
-	    func_lo2o "$arg"
-	    pic_object=$xdir$objdir/$func_lo2o_result
-	    non_pic_object=$xdir$func_lo2o_result
-	    func_append libobjs " $pic_object"
-	    func_append non_pic_objects " $non_pic_object"
-	  else
-	    func_fatal_error "'$arg' is not a valid libtool object"
-	  fi
-	fi
-	;;
-
-      *.$libext)
-	# An archive.
-	func_append deplibs " $arg"
-	func_append old_deplibs " $arg"
-	continue
-	;;
-
-      *.la)
-	# A libtool-controlled library.
-
-	func_resolve_sysroot "$arg"
-	if test dlfiles = "$prev"; then
-	  # This library was specified with -dlopen.
-	  func_append dlfiles " $func_resolve_sysroot_result"
-	  prev=
-	elif test dlprefiles = "$prev"; then
-	  # The library was specified with -dlpreopen.
-	  func_append dlprefiles " $func_resolve_sysroot_result"
-	  prev=
-	else
-	  func_append deplibs " $func_resolve_sysroot_result"
-	fi
-	continue
-	;;
-
-      # Some other compiler argument.
-      *)
-	# Unknown arguments in both finalize_command and compile_command need
-	# to be aesthetically quoted because they are evaled later.
-	func_quote_for_eval "$arg"
-	arg=$func_quote_for_eval_result
-	;;
-      esac # arg
-
-      # Now actually substitute the argument into the commands.
-      if test -n "$arg"; then
-	func_append compile_command " $arg"
-	func_append finalize_command " $arg"
-      fi
-    done # argument parsing loop
-
-    test -n "$prev" && \
-      func_fatal_help "the '$prevarg' option requires an argument"
-
-    if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then
-      eval arg=\"$export_dynamic_flag_spec\"
-      func_append compile_command " $arg"
-      func_append finalize_command " $arg"
-    fi
-
-    oldlibs=
-    # calculate the name of the file, without its directory
-    func_basename "$output"
-    outputname=$func_basename_result
-    libobjs_save=$libobjs
-
-    if test -n "$shlibpath_var"; then
-      # get the directories listed in $shlibpath_var
-      eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\`
-    else
-      shlib_search_path=
-    fi
-    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
-    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
-
-    # Definition is injected by LT_CONFIG during libtool generation.
-    func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH"
-
-    func_dirname "$output" "/" ""
-    output_objdir=$func_dirname_result$objdir
-    func_to_tool_file "$output_objdir/"
-    tool_output_objdir=$func_to_tool_file_result
-    # Create the object directory.
-    func_mkdir_p "$output_objdir"
-
-    # Determine the type of output
-    case $output in
-    "")
-      func_fatal_help "you must specify an output file"
-      ;;
-    *.$libext) linkmode=oldlib ;;
-    *.lo | *.$objext) linkmode=obj ;;
-    *.la) linkmode=lib ;;
-    *) linkmode=prog ;; # Anything else should be a program.
-    esac
-
-    specialdeplibs=
-
-    libs=
-    # Find all interdependent deplibs by searching for libraries
-    # that are linked more than once (e.g. -la -lb -la)
-    for deplib in $deplibs; do
-      if $opt_preserve_dup_deps; then
-	case "$libs " in
-	*" $deplib "*) func_append specialdeplibs " $deplib" ;;
-	esac
-      fi
-      func_append libs " $deplib"
-    done
-
-    if test lib = "$linkmode"; then
-      libs="$predeps $libs $compiler_lib_search_path $postdeps"
-
-      # Compute libraries that are listed more than once in $predeps
-      # $postdeps and mark them as special (i.e., whose duplicates are
-      # not to be eliminated).
-      pre_post_deps=
-      if $opt_duplicate_compiler_generated_deps; then
-	for pre_post_dep in $predeps $postdeps; do
-	  case "$pre_post_deps " in
-	  *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
-	  esac
-	  func_append pre_post_deps " $pre_post_dep"
-	done
-      fi
-      pre_post_deps=
-    fi
-
-    deplibs=
-    newdependency_libs=
-    newlib_search_path=
-    need_relink=no # whether we're linking any uninstalled libtool libraries
-    notinst_deplibs= # not-installed libtool libraries
-    notinst_path= # paths that contain not-installed libtool libraries
-
-    case $linkmode in
-    lib)
-	passes="conv dlpreopen link"
-	for file in $dlfiles $dlprefiles; do
-	  case $file in
-	  *.la) ;;
-	  *)
-	    func_fatal_help "libraries can '-dlopen' only libtool libraries: $file"
-	    ;;
-	  esac
-	done
-	;;
-    prog)
-	compile_deplibs=
-	finalize_deplibs=
-	alldeplibs=false
-	newdlfiles=
-	newdlprefiles=
-	passes="conv scan dlopen dlpreopen link"
-	;;
-    *)  passes="conv"
-	;;
-    esac
-
-    for pass in $passes; do
-      # The preopen pass in lib mode reverses $deplibs; put it back here
-      # so that -L comes before libs that need it for instance...
-      if test lib,link = "$linkmode,$pass"; then
-	## FIXME: Find the place where the list is rebuilt in the wrong
-	##        order, and fix it there properly
-        tmp_deplibs=
-	for deplib in $deplibs; do
-	  tmp_deplibs="$deplib $tmp_deplibs"
-	done
-	deplibs=$tmp_deplibs
-      fi
-
-      if test lib,link = "$linkmode,$pass" ||
-	 test prog,scan = "$linkmode,$pass"; then
-	libs=$deplibs
-	deplibs=
-      fi
-      if test prog = "$linkmode"; then
-	case $pass in
-	dlopen) libs=$dlfiles ;;
-	dlpreopen) libs=$dlprefiles ;;
-	link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
-	esac
-      fi
-      if test lib,dlpreopen = "$linkmode,$pass"; then
-	# Collect and forward deplibs of preopened libtool libs
-	for lib in $dlprefiles; do
-	  # Ignore non-libtool-libs
-	  dependency_libs=
-	  func_resolve_sysroot "$lib"
-	  case $lib in
-	  *.la)	func_source "$func_resolve_sysroot_result" ;;
-	  esac
-
-	  # Collect preopened libtool deplibs, except any this library
-	  # has declared as weak libs
-	  for deplib in $dependency_libs; do
-	    func_basename "$deplib"
-            deplib_base=$func_basename_result
-	    case " $weak_libs " in
-	    *" $deplib_base "*) ;;
-	    *) func_append deplibs " $deplib" ;;
-	    esac
-	  done
-	done
-	libs=$dlprefiles
-      fi
-      if test dlopen = "$pass"; then
-	# Collect dlpreopened libraries
-	save_deplibs=$deplibs
-	deplibs=
-      fi
-
-      for deplib in $libs; do
-	lib=
-	found=false
-	case $deplib in
-	-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
-        |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
-	  if test prog,link = "$linkmode,$pass"; then
-	    compile_deplibs="$deplib $compile_deplibs"
-	    finalize_deplibs="$deplib $finalize_deplibs"
-	  else
-	    func_append compiler_flags " $deplib"
-	    if test lib = "$linkmode"; then
-		case "$new_inherited_linker_flags " in
-		    *" $deplib "*) ;;
-		    * ) func_append new_inherited_linker_flags " $deplib" ;;
-		esac
-	    fi
-	  fi
-	  continue
-	  ;;
-	-l*)
-	  if test lib != "$linkmode" && test prog != "$linkmode"; then
-	    func_warning "'-l' is ignored for archives/objects"
-	    continue
-	  fi
-	  func_stripname '-l' '' "$deplib"
-	  name=$func_stripname_result
-	  if test lib = "$linkmode"; then
-	    searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
-	  else
-	    searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
-	  fi
-	  for searchdir in $searchdirs; do
-	    for search_ext in .la $std_shrext .so .a; do
-	      # Search the libtool library
-	      lib=$searchdir/lib$name$search_ext
-	      if test -f "$lib"; then
-		if test .la = "$search_ext"; then
-		  found=:
-		else
-		  found=false
-		fi
-		break 2
-	      fi
-	    done
-	  done
-	  if $found; then
-	    # deplib is a libtool library
-	    # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
-	    # We need to do some special things here, and not later.
-	    if test yes = "$allow_libtool_libs_with_static_runtimes"; then
-	      case " $predeps $postdeps " in
-	      *" $deplib "*)
-		if func_lalib_p "$lib"; then
-		  library_names=
-		  old_library=
-		  func_source "$lib"
-		  for l in $old_library $library_names; do
-		    ll=$l
-		  done
-		  if test "X$ll" = "X$old_library"; then # only static version available
-		    found=false
-		    func_dirname "$lib" "" "."
-		    ladir=$func_dirname_result
-		    lib=$ladir/$old_library
-		    if test prog,link = "$linkmode,$pass"; then
-		      compile_deplibs="$deplib $compile_deplibs"
-		      finalize_deplibs="$deplib $finalize_deplibs"
-		    else
-		      deplibs="$deplib $deplibs"
-		      test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
-		    fi
-		    continue
-		  fi
-		fi
-		;;
-	      *) ;;
-	      esac
-	    fi
-	  else
-	    # deplib doesn't seem to be a libtool library
-	    if test prog,link = "$linkmode,$pass"; then
-	      compile_deplibs="$deplib $compile_deplibs"
-	      finalize_deplibs="$deplib $finalize_deplibs"
-	    else
-	      deplibs="$deplib $deplibs"
-	      test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
-	    fi
-	    continue
-	  fi
-	  ;; # -l
-	*.ltframework)
-	  if test prog,link = "$linkmode,$pass"; then
-	    compile_deplibs="$deplib $compile_deplibs"
-	    finalize_deplibs="$deplib $finalize_deplibs"
-	  else
-	    deplibs="$deplib $deplibs"
-	    if test lib = "$linkmode"; then
-		case "$new_inherited_linker_flags " in
-		    *" $deplib "*) ;;
-		    * ) func_append new_inherited_linker_flags " $deplib" ;;
-		esac
-	    fi
-	  fi
-	  continue
-	  ;;
-	-L*)
-	  case $linkmode in
-	  lib)
-	    deplibs="$deplib $deplibs"
-	    test conv = "$pass" && continue
-	    newdependency_libs="$deplib $newdependency_libs"
-	    func_stripname '-L' '' "$deplib"
-	    func_resolve_sysroot "$func_stripname_result"
-	    func_append newlib_search_path " $func_resolve_sysroot_result"
-	    ;;
-	  prog)
-	    if test conv = "$pass"; then
-	      deplibs="$deplib $deplibs"
-	      continue
-	    fi
-	    if test scan = "$pass"; then
-	      deplibs="$deplib $deplibs"
-	    else
-	      compile_deplibs="$deplib $compile_deplibs"
-	      finalize_deplibs="$deplib $finalize_deplibs"
-	    fi
-	    func_stripname '-L' '' "$deplib"
-	    func_resolve_sysroot "$func_stripname_result"
-	    func_append newlib_search_path " $func_resolve_sysroot_result"
-	    ;;
-	  *)
-	    func_warning "'-L' is ignored for archives/objects"
-	    ;;
-	  esac # linkmode
-	  continue
-	  ;; # -L
-	-R*)
-	  if test link = "$pass"; then
-	    func_stripname '-R' '' "$deplib"
-	    func_resolve_sysroot "$func_stripname_result"
-	    dir=$func_resolve_sysroot_result
-	    # Make sure the xrpath contains only unique directories.
-	    case "$xrpath " in
-	    *" $dir "*) ;;
-	    *) func_append xrpath " $dir" ;;
-	    esac
-	  fi
-	  deplibs="$deplib $deplibs"
-	  continue
-	  ;;
-	*.la)
-	  func_resolve_sysroot "$deplib"
-	  lib=$func_resolve_sysroot_result
-	  ;;
-	*.$libext)
-	  if test conv = "$pass"; then
-	    deplibs="$deplib $deplibs"
-	    continue
-	  fi
-	  case $linkmode in
-	  lib)
-	    # Linking convenience modules into shared libraries is allowed,
-	    # but linking other static libraries is non-portable.
-	    case " $dlpreconveniencelibs " in
-	    *" $deplib "*) ;;
-	    *)
-	      valid_a_lib=false
-	      case $deplibs_check_method in
-		match_pattern*)
-		  set dummy $deplibs_check_method; shift
-		  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
-		  if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
-		    | $EGREP "$match_pattern_regex" > /dev/null; then
-		    valid_a_lib=:
-		  fi
-		;;
-		pass_all)
-		  valid_a_lib=:
-		;;
-	      esac
-	      if $valid_a_lib; then
-		echo
-		$ECHO "*** Warning: Linking the shared library $output against the"
-		$ECHO "*** static library $deplib is not portable!"
-		deplibs="$deplib $deplibs"
-	      else
-		echo
-		$ECHO "*** Warning: Trying to link with static lib archive $deplib."
-		echo "*** I have the capability to make that library automatically link in when"
-		echo "*** you link to this library.  But I can only do this if you have a"
-		echo "*** shared version of the library, which you do not appear to have"
-		echo "*** because the file extensions .$libext of this argument makes me believe"
-		echo "*** that it is just a static archive that I should not use here."
-	      fi
-	      ;;
-	    esac
-	    continue
-	    ;;
-	  prog)
-	    if test link != "$pass"; then
-	      deplibs="$deplib $deplibs"
-	    else
-	      compile_deplibs="$deplib $compile_deplibs"
-	      finalize_deplibs="$deplib $finalize_deplibs"
-	    fi
-	    continue
-	    ;;
-	  esac # linkmode
-	  ;; # *.$libext
-	*.lo | *.$objext)
-	  if test conv = "$pass"; then
-	    deplibs="$deplib $deplibs"
-	  elif test prog = "$linkmode"; then
-	    if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then
-	      # If there is no dlopen support or we're linking statically,
-	      # we need to preload.
-	      func_append newdlprefiles " $deplib"
-	      compile_deplibs="$deplib $compile_deplibs"
-	      finalize_deplibs="$deplib $finalize_deplibs"
-	    else
-	      func_append newdlfiles " $deplib"
-	    fi
-	  fi
-	  continue
-	  ;;
-	%DEPLIBS%)
-	  alldeplibs=:
-	  continue
-	  ;;
-	esac # case $deplib
-
-	$found || test -f "$lib" \
-	  || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'"
-
-	# Check to see that this really is a libtool archive.
-	func_lalib_unsafe_p "$lib" \
-	  || func_fatal_error "'$lib' is not a valid libtool archive"
-
-	func_dirname "$lib" "" "."
-	ladir=$func_dirname_result
-
-	dlname=
-	dlopen=
-	dlpreopen=
-	libdir=
-	library_names=
-	old_library=
-	inherited_linker_flags=
-	# If the library was installed with an old release of libtool,
-	# it will not redefine variables installed, or shouldnotlink
-	installed=yes
-	shouldnotlink=no
-	avoidtemprpath=
-
-
-	# Read the .la file
-	func_source "$lib"
-
-	# Convert "-framework foo" to "foo.ltframework"
-	if test -n "$inherited_linker_flags"; then
-	  tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
-	  for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
-	    case " $new_inherited_linker_flags " in
-	      *" $tmp_inherited_linker_flag "*) ;;
-	      *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
-	    esac
-	  done
-	fi
-	dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
-	if test lib,link = "$linkmode,$pass" ||
-	   test prog,scan = "$linkmode,$pass" ||
-	   { test prog != "$linkmode" && test lib != "$linkmode"; }; then
-	  test -n "$dlopen" && func_append dlfiles " $dlopen"
-	  test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
-	fi
-
-	if test conv = "$pass"; then
-	  # Only check for convenience libraries
-	  deplibs="$lib $deplibs"
-	  if test -z "$libdir"; then
-	    if test -z "$old_library"; then
-	      func_fatal_error "cannot find name of link library for '$lib'"
-	    fi
-	    # It is a libtool convenience library, so add in its objects.
-	    func_append convenience " $ladir/$objdir/$old_library"
-	    func_append old_convenience " $ladir/$objdir/$old_library"
-	  elif test prog != "$linkmode" && test lib != "$linkmode"; then
-	    func_fatal_error "'$lib' is not a convenience library"
-	  fi
-	  tmp_libs=
-	  for deplib in $dependency_libs; do
-	    deplibs="$deplib $deplibs"
-	    if $opt_preserve_dup_deps; then
-	      case "$tmp_libs " in
-	      *" $deplib "*) func_append specialdeplibs " $deplib" ;;
-	      esac
-	    fi
-	    func_append tmp_libs " $deplib"
-	  done
-	  continue
-	fi # $pass = conv
-
-
-	# Get the name of the library we link against.
-	linklib=
-	if test -n "$old_library" &&
-	   { test yes = "$prefer_static_libs" ||
-	     test built,no = "$prefer_static_libs,$installed"; }; then
-	  linklib=$old_library
-	else
-	  for l in $old_library $library_names; do
-	    linklib=$l
-	  done
-	fi
-	if test -z "$linklib"; then
-	  func_fatal_error "cannot find name of link library for '$lib'"
-	fi
-
-	# This library was specified with -dlopen.
-	if test dlopen = "$pass"; then
-	  test -z "$libdir" \
-	    && func_fatal_error "cannot -dlopen a convenience library: '$lib'"
-	  if test -z "$dlname" ||
-	     test yes != "$dlopen_support" ||
-	     test no = "$build_libtool_libs"
-	  then
-	    # If there is no dlname, no dlopen support or we're linking
-	    # statically, we need to preload.  We also need to preload any
-	    # dependent libraries so libltdl's deplib preloader doesn't
-	    # bomb out in the load deplibs phase.
-	    func_append dlprefiles " $lib $dependency_libs"
-	  else
-	    func_append newdlfiles " $lib"
-	  fi
-	  continue
-	fi # $pass = dlopen
-
-	# We need an absolute path.
-	case $ladir in
-	[\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;;
-	*)
-	  abs_ladir=`cd "$ladir" && pwd`
-	  if test -z "$abs_ladir"; then
-	    func_warning "cannot determine absolute directory name of '$ladir'"
-	    func_warning "passing it literally to the linker, although it might fail"
-	    abs_ladir=$ladir
-	  fi
-	  ;;
-	esac
-	func_basename "$lib"
-	laname=$func_basename_result
-
-	# Find the relevant object directory and library name.
-	if test yes = "$installed"; then
-	  if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
-	    func_warning "library '$lib' was moved."
-	    dir=$ladir
-	    absdir=$abs_ladir
-	    libdir=$abs_ladir
-	  else
-	    dir=$lt_sysroot$libdir
-	    absdir=$lt_sysroot$libdir
-	  fi
-	  test yes = "$hardcode_automatic" && avoidtemprpath=yes
-	else
-	  if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
-	    dir=$ladir
-	    absdir=$abs_ladir
-	    # Remove this search path later
-	    func_append notinst_path " $abs_ladir"
-	  else
-	    dir=$ladir/$objdir
-	    absdir=$abs_ladir/$objdir
-	    # Remove this search path later
-	    func_append notinst_path " $abs_ladir"
-	  fi
-	fi # $installed = yes
-	func_stripname 'lib' '.la' "$laname"
-	name=$func_stripname_result
-
-	# This library was specified with -dlpreopen.
-	if test dlpreopen = "$pass"; then
-	  if test -z "$libdir" && test prog = "$linkmode"; then
-	    func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'"
-	  fi
-	  case $host in
-	    # special handling for platforms with PE-DLLs.
-	    *cygwin* | *mingw* | *cegcc* )
-	      # Linker will automatically link against shared library if both
-	      # static and shared are present.  Therefore, ensure we extract
-	      # symbols from the import library if a shared library is present
-	      # (otherwise, the dlopen module name will be incorrect).  We do
-	      # this by putting the import library name into $newdlprefiles.
-	      # We recover the dlopen module name by 'saving' the la file
-	      # name in a special purpose variable, and (later) extracting the
-	      # dlname from the la file.
-	      if test -n "$dlname"; then
-	        func_tr_sh "$dir/$linklib"
-	        eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
-	        func_append newdlprefiles " $dir/$linklib"
-	      else
-	        func_append newdlprefiles " $dir/$old_library"
-	        # Keep a list of preopened convenience libraries to check
-	        # that they are being used correctly in the link pass.
-	        test -z "$libdir" && \
-	          func_append dlpreconveniencelibs " $dir/$old_library"
-	      fi
-	    ;;
-	    * )
-	      # Prefer using a static library (so that no silly _DYNAMIC symbols
-	      # are required to link).
-	      if test -n "$old_library"; then
-	        func_append newdlprefiles " $dir/$old_library"
-	        # Keep a list of preopened convenience libraries to check
-	        # that they are being used correctly in the link pass.
-	        test -z "$libdir" && \
-	          func_append dlpreconveniencelibs " $dir/$old_library"
-	      # Otherwise, use the dlname, so that lt_dlopen finds it.
-	      elif test -n "$dlname"; then
-	        func_append newdlprefiles " $dir/$dlname"
-	      else
-	        func_append newdlprefiles " $dir/$linklib"
-	      fi
-	    ;;
-	  esac
-	fi # $pass = dlpreopen
-
-	if test -z "$libdir"; then
-	  # Link the convenience library
-	  if test lib = "$linkmode"; then
-	    deplibs="$dir/$old_library $deplibs"
-	  elif test prog,link = "$linkmode,$pass"; then
-	    compile_deplibs="$dir/$old_library $compile_deplibs"
-	    finalize_deplibs="$dir/$old_library $finalize_deplibs"
-	  else
-	    deplibs="$lib $deplibs" # used for prog,scan pass
-	  fi
-	  continue
-	fi
-
-
-	if test prog = "$linkmode" && test link != "$pass"; then
-	  func_append newlib_search_path " $ladir"
-	  deplibs="$lib $deplibs"
-
-	  linkalldeplibs=false
-	  if test no != "$link_all_deplibs" || test -z "$library_names" ||
-	     test no = "$build_libtool_libs"; then
-	    linkalldeplibs=:
-	  fi
-
-	  tmp_libs=
-	  for deplib in $dependency_libs; do
-	    case $deplib in
-	    -L*) func_stripname '-L' '' "$deplib"
-	         func_resolve_sysroot "$func_stripname_result"
-	         func_append newlib_search_path " $func_resolve_sysroot_result"
-		 ;;
-	    esac
-	    # Need to link against all dependency_libs?
-	    if $linkalldeplibs; then
-	      deplibs="$deplib $deplibs"
-	    else
-	      # Need to hardcode shared library paths
-	      # or/and link against static libraries
-	      newdependency_libs="$deplib $newdependency_libs"
-	    fi
-	    if $opt_preserve_dup_deps; then
-	      case "$tmp_libs " in
-	      *" $deplib "*) func_append specialdeplibs " $deplib" ;;
-	      esac
-	    fi
-	    func_append tmp_libs " $deplib"
-	  done # for deplib
-	  continue
-	fi # $linkmode = prog...
-
-	if test prog,link = "$linkmode,$pass"; then
-	  if test -n "$library_names" &&
-	     { { test no = "$prefer_static_libs" ||
-	         test built,yes = "$prefer_static_libs,$installed"; } ||
-	       test -z "$old_library"; }; then
-	    # We need to hardcode the library path
-	    if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then
-	      # Make sure the rpath contains only unique directories.
-	      case $temp_rpath: in
-	      *"$absdir:"*) ;;
-	      *) func_append temp_rpath "$absdir:" ;;
-	      esac
-	    fi
-
-	    # Hardcode the library path.
-	    # Skip directories that are in the system default run-time
-	    # search path.
-	    case " $sys_lib_dlsearch_path " in
-	    *" $absdir "*) ;;
-	    *)
-	      case "$compile_rpath " in
-	      *" $absdir "*) ;;
-	      *) func_append compile_rpath " $absdir" ;;
-	      esac
-	      ;;
-	    esac
-	    case " $sys_lib_dlsearch_path " in
-	    *" $libdir "*) ;;
-	    *)
-	      case "$finalize_rpath " in
-	      *" $libdir "*) ;;
-	      *) func_append finalize_rpath " $libdir" ;;
-	      esac
-	      ;;
-	    esac
-	  fi # $linkmode,$pass = prog,link...
-
-	  if $alldeplibs &&
-	     { test pass_all = "$deplibs_check_method" ||
-	       { test yes = "$build_libtool_libs" &&
-		 test -n "$library_names"; }; }; then
-	    # We only need to search for static libraries
-	    continue
-	  fi
-	fi
-
-	link_static=no # Whether the deplib will be linked statically
-	use_static_libs=$prefer_static_libs
-	if test built = "$use_static_libs" && test yes = "$installed"; then
-	  use_static_libs=no
-	fi
-	if test -n "$library_names" &&
-	   { test no = "$use_static_libs" || test -z "$old_library"; }; then
-	  case $host in
-	  *cygwin* | *mingw* | *cegcc* | *os2*)
-	      # No point in relinking DLLs because paths are not encoded
-	      func_append notinst_deplibs " $lib"
-	      need_relink=no
-	    ;;
-	  *)
-	    if test no = "$installed"; then
-	      func_append notinst_deplibs " $lib"
-	      need_relink=yes
-	    fi
-	    ;;
-	  esac
-	  # This is a shared library
-
-	  # Warn about portability, can't link against -module's on some
-	  # systems (darwin).  Don't bleat about dlopened modules though!
-	  dlopenmodule=
-	  for dlpremoduletest in $dlprefiles; do
-	    if test "X$dlpremoduletest" = "X$lib"; then
-	      dlopenmodule=$dlpremoduletest
-	      break
-	    fi
-	  done
-	  if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then
-	    echo
-	    if test prog = "$linkmode"; then
-	      $ECHO "*** Warning: Linking the executable $output against the loadable module"
-	    else
-	      $ECHO "*** Warning: Linking the shared library $output against the loadable module"
-	    fi
-	    $ECHO "*** $linklib is not portable!"
-	  fi
-	  if test lib = "$linkmode" &&
-	     test yes = "$hardcode_into_libs"; then
-	    # Hardcode the library path.
-	    # Skip directories that are in the system default run-time
-	    # search path.
-	    case " $sys_lib_dlsearch_path " in
-	    *" $absdir "*) ;;
-	    *)
-	      case "$compile_rpath " in
-	      *" $absdir "*) ;;
-	      *) func_append compile_rpath " $absdir" ;;
-	      esac
-	      ;;
-	    esac
-	    case " $sys_lib_dlsearch_path " in
-	    *" $libdir "*) ;;
-	    *)
-	      case "$finalize_rpath " in
-	      *" $libdir "*) ;;
-	      *) func_append finalize_rpath " $libdir" ;;
-	      esac
-	      ;;
-	    esac
-	  fi
-
-	  if test -n "$old_archive_from_expsyms_cmds"; then
-	    # figure out the soname
-	    set dummy $library_names
-	    shift
-	    realname=$1
-	    shift
-	    libname=`eval "\\$ECHO \"$libname_spec\""`
-	    # use dlname if we got it. it's perfectly good, no?
-	    if test -n "$dlname"; then
-	      soname=$dlname
-	    elif test -n "$soname_spec"; then
-	      # bleh windows
-	      case $host in
-	      *cygwin* | mingw* | *cegcc* | *os2*)
-	        func_arith $current - $age
-		major=$func_arith_result
-		versuffix=-$major
-		;;
-	      esac
-	      eval soname=\"$soname_spec\"
-	    else
-	      soname=$realname
-	    fi
-
-	    # Make a new name for the extract_expsyms_cmds to use
-	    soroot=$soname
-	    func_basename "$soroot"
-	    soname=$func_basename_result
-	    func_stripname 'lib' '.dll' "$soname"
-	    newlib=libimp-$func_stripname_result.a
-
-	    # If the library has no export list, then create one now
-	    if test -f "$output_objdir/$soname-def"; then :
-	    else
-	      func_verbose "extracting exported symbol list from '$soname'"
-	      func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
-	    fi
-
-	    # Create $newlib
-	    if test -f "$output_objdir/$newlib"; then :; else
-	      func_verbose "generating import library for '$soname'"
-	      func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
-	    fi
-	    # make sure the library variables are pointing to the new library
-	    dir=$output_objdir
-	    linklib=$newlib
-	  fi # test -n "$old_archive_from_expsyms_cmds"
-
-	  if test prog = "$linkmode" || test relink != "$opt_mode"; then
-	    add_shlibpath=
-	    add_dir=
-	    add=
-	    lib_linked=yes
-	    case $hardcode_action in
-	    immediate | unsupported)
-	      if test no = "$hardcode_direct"; then
-		add=$dir/$linklib
-		case $host in
-		  *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
-		  *-*-sysv4*uw2*) add_dir=-L$dir ;;
-		  *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
-		    *-*-unixware7*) add_dir=-L$dir ;;
-		  *-*-darwin* )
-		    # if the lib is a (non-dlopened) module then we cannot
-		    # link against it, someone is ignoring the earlier warnings
-		    if /usr/bin/file -L $add 2> /dev/null |
-			 $GREP ": [^:]* bundle" >/dev/null; then
-		      if test "X$dlopenmodule" != "X$lib"; then
-			$ECHO "*** Warning: lib $linklib is a module, not a shared library"
-			if test -z "$old_library"; then
-			  echo
-			  echo "*** And there doesn't seem to be a static archive available"
-			  echo "*** The link will probably fail, sorry"
-			else
-			  add=$dir/$old_library
-			fi
-		      elif test -n "$old_library"; then
-			add=$dir/$old_library
-		      fi
-		    fi
-		esac
-	      elif test no = "$hardcode_minus_L"; then
-		case $host in
-		*-*-sunos*) add_shlibpath=$dir ;;
-		esac
-		add_dir=-L$dir
-		add=-l$name
-	      elif test no = "$hardcode_shlibpath_var"; then
-		add_shlibpath=$dir
-		add=-l$name
-	      else
-		lib_linked=no
-	      fi
-	      ;;
-	    relink)
-	      if test yes = "$hardcode_direct" &&
-	         test no = "$hardcode_direct_absolute"; then
-		add=$dir/$linklib
-	      elif test yes = "$hardcode_minus_L"; then
-		add_dir=-L$absdir
-		# Try looking first in the location we're being installed to.
-		if test -n "$inst_prefix_dir"; then
-		  case $libdir in
-		    [\\/]*)
-		      func_append add_dir " -L$inst_prefix_dir$libdir"
-		      ;;
-		  esac
-		fi
-		add=-l$name
-	      elif test yes = "$hardcode_shlibpath_var"; then
-		add_shlibpath=$dir
-		add=-l$name
-	      else
-		lib_linked=no
-	      fi
-	      ;;
-	    *) lib_linked=no ;;
-	    esac
-
-	    if test yes != "$lib_linked"; then
-	      func_fatal_configuration "unsupported hardcode properties"
-	    fi
-
-	    if test -n "$add_shlibpath"; then
-	      case :$compile_shlibpath: in
-	      *":$add_shlibpath:"*) ;;
-	      *) func_append compile_shlibpath "$add_shlibpath:" ;;
-	      esac
-	    fi
-	    if test prog = "$linkmode"; then
-	      test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
-	      test -n "$add" && compile_deplibs="$add $compile_deplibs"
-	    else
-	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
-	      test -n "$add" && deplibs="$add $deplibs"
-	      if test yes != "$hardcode_direct" &&
-		 test yes != "$hardcode_minus_L" &&
-		 test yes = "$hardcode_shlibpath_var"; then
-		case :$finalize_shlibpath: in
-		*":$libdir:"*) ;;
-		*) func_append finalize_shlibpath "$libdir:" ;;
-		esac
-	      fi
-	    fi
-	  fi
-
-	  if test prog = "$linkmode" || test relink = "$opt_mode"; then
-	    add_shlibpath=
-	    add_dir=
-	    add=
-	    # Finalize command for both is simple: just hardcode it.
-	    if test yes = "$hardcode_direct" &&
-	       test no = "$hardcode_direct_absolute"; then
-	      add=$libdir/$linklib
-	    elif test yes = "$hardcode_minus_L"; then
-	      add_dir=-L$libdir
-	      add=-l$name
-	    elif test yes = "$hardcode_shlibpath_var"; then
-	      case :$finalize_shlibpath: in
-	      *":$libdir:"*) ;;
-	      *) func_append finalize_shlibpath "$libdir:" ;;
-	      esac
-	      add=-l$name
-	    elif test yes = "$hardcode_automatic"; then
-	      if test -n "$inst_prefix_dir" &&
-		 test -f "$inst_prefix_dir$libdir/$linklib"; then
-		add=$inst_prefix_dir$libdir/$linklib
-	      else
-		add=$libdir/$linklib
-	      fi
-	    else
-	      # We cannot seem to hardcode it, guess we'll fake it.
-	      add_dir=-L$libdir
-	      # Try looking first in the location we're being installed to.
-	      if test -n "$inst_prefix_dir"; then
-		case $libdir in
-		  [\\/]*)
-		    func_append add_dir " -L$inst_prefix_dir$libdir"
-		    ;;
-		esac
-	      fi
-	      add=-l$name
-	    fi
-
-	    if test prog = "$linkmode"; then
-	      test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
-	      test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
-	    else
-	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
-	      test -n "$add" && deplibs="$add $deplibs"
-	    fi
-	  fi
-	elif test prog = "$linkmode"; then
-	  # Here we assume that one of hardcode_direct or hardcode_minus_L
-	  # is not unsupported.  This is valid on all known static and
-	  # shared platforms.
-	  if test unsupported != "$hardcode_direct"; then
-	    test -n "$old_library" && linklib=$old_library
-	    compile_deplibs="$dir/$linklib $compile_deplibs"
-	    finalize_deplibs="$dir/$linklib $finalize_deplibs"
-	  else
-	    compile_deplibs="-l$name -L$dir $compile_deplibs"
-	    finalize_deplibs="-l$name -L$dir $finalize_deplibs"
-	  fi
-	elif test yes = "$build_libtool_libs"; then
-	  # Not a shared library
-	  if test pass_all != "$deplibs_check_method"; then
-	    # We're trying link a shared library against a static one
-	    # but the system doesn't support it.
-
-	    # Just print a warning and add the library to dependency_libs so
-	    # that the program can be linked against the static library.
-	    echo
-	    $ECHO "*** Warning: This system cannot link to static lib archive $lib."
-	    echo "*** I have the capability to make that library automatically link in when"
-	    echo "*** you link to this library.  But I can only do this if you have a"
-	    echo "*** shared version of the library, which you do not appear to have."
-	    if test yes = "$module"; then
-	      echo "*** But as you try to build a module library, libtool will still create "
-	      echo "*** a static module, that should work as long as the dlopening application"
-	      echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
-	      if test -z "$global_symbol_pipe"; then
-		echo
-		echo "*** However, this would only work if libtool was able to extract symbol"
-		echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
-		echo "*** not find such a program.  So, this module is probably useless."
-		echo "*** 'nm' from GNU binutils and a full rebuild may help."
-	      fi
-	      if test no = "$build_old_libs"; then
-		build_libtool_libs=module
-		build_old_libs=yes
-	      else
-		build_libtool_libs=no
-	      fi
-	    fi
-	  else
-	    deplibs="$dir/$old_library $deplibs"
-	    link_static=yes
-	  fi
-	fi # link shared/static library?
-
-	if test lib = "$linkmode"; then
-	  if test -n "$dependency_libs" &&
-	     { test yes != "$hardcode_into_libs" ||
-	       test yes = "$build_old_libs" ||
-	       test yes = "$link_static"; }; then
-	    # Extract -R from dependency_libs
-	    temp_deplibs=
-	    for libdir in $dependency_libs; do
-	      case $libdir in
-	      -R*) func_stripname '-R' '' "$libdir"
-	           temp_xrpath=$func_stripname_result
-		   case " $xrpath " in
-		   *" $temp_xrpath "*) ;;
-		   *) func_append xrpath " $temp_xrpath";;
-		   esac;;
-	      *) func_append temp_deplibs " $libdir";;
-	      esac
-	    done
-	    dependency_libs=$temp_deplibs
-	  fi
-
-	  func_append newlib_search_path " $absdir"
-	  # Link against this library
-	  test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
-	  # ... and its dependency_libs
-	  tmp_libs=
-	  for deplib in $dependency_libs; do
-	    newdependency_libs="$deplib $newdependency_libs"
-	    case $deplib in
-              -L*) func_stripname '-L' '' "$deplib"
-                   func_resolve_sysroot "$func_stripname_result";;
-              *) func_resolve_sysroot "$deplib" ;;
-            esac
-	    if $opt_preserve_dup_deps; then
-	      case "$tmp_libs " in
-	      *" $func_resolve_sysroot_result "*)
-                func_append specialdeplibs " $func_resolve_sysroot_result" ;;
-	      esac
-	    fi
-	    func_append tmp_libs " $func_resolve_sysroot_result"
-	  done
-
-	  if test no != "$link_all_deplibs"; then
-	    # Add the search paths of all dependency libraries
-	    for deplib in $dependency_libs; do
-	      path=
-	      case $deplib in
-	      -L*) path=$deplib ;;
-	      *.la)
-	        func_resolve_sysroot "$deplib"
-	        deplib=$func_resolve_sysroot_result
-	        func_dirname "$deplib" "" "."
-		dir=$func_dirname_result
-		# We need an absolute path.
-		case $dir in
-		[\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;;
-		*)
-		  absdir=`cd "$dir" && pwd`
-		  if test -z "$absdir"; then
-		    func_warning "cannot determine absolute directory name of '$dir'"
-		    absdir=$dir
-		  fi
-		  ;;
-		esac
-		if $GREP "^installed=no" $deplib > /dev/null; then
-		case $host in
-		*-*-darwin*)
-		  depdepl=
-		  eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
-		  if test -n "$deplibrary_names"; then
-		    for tmp in $deplibrary_names; do
-		      depdepl=$tmp
-		    done
-		    if test -f "$absdir/$objdir/$depdepl"; then
-		      depdepl=$absdir/$objdir/$depdepl
-		      darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
-                      if test -z "$darwin_install_name"; then
-                          darwin_install_name=`$OTOOL64 -L $depdepl  | awk '{if (NR == 2) {print $1;exit}}'`
-                      fi
-		      func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl"
-		      func_append linker_flags " -dylib_file $darwin_install_name:$depdepl"
-		      path=
-		    fi
-		  fi
-		  ;;
-		*)
-		  path=-L$absdir/$objdir
-		  ;;
-		esac
-		else
-		  eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
-		  test -z "$libdir" && \
-		    func_fatal_error "'$deplib' is not a valid libtool archive"
-		  test "$absdir" != "$libdir" && \
-		    func_warning "'$deplib' seems to be moved"
-
-		  path=-L$absdir
-		fi
-		;;
-	      esac
-	      case " $deplibs " in
-	      *" $path "*) ;;
-	      *) deplibs="$path $deplibs" ;;
-	      esac
-	    done
-	  fi # link_all_deplibs != no
-	fi # linkmode = lib
-      done # for deplib in $libs
-      if test link = "$pass"; then
-	if test prog = "$linkmode"; then
-	  compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
-	  finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
-	else
-	  compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
-	fi
-      fi
-      dependency_libs=$newdependency_libs
-      if test dlpreopen = "$pass"; then
-	# Link the dlpreopened libraries before other libraries
-	for deplib in $save_deplibs; do
-	  deplibs="$deplib $deplibs"
-	done
-      fi
-      if test dlopen != "$pass"; then
-	test conv = "$pass" || {
-	  # Make sure lib_search_path contains only unique directories.
-	  lib_search_path=
-	  for dir in $newlib_search_path; do
-	    case "$lib_search_path " in
-	    *" $dir "*) ;;
-	    *) func_append lib_search_path " $dir" ;;
-	    esac
-	  done
-	  newlib_search_path=
-	}
-
-	if test prog,link = "$linkmode,$pass"; then
-	  vars="compile_deplibs finalize_deplibs"
-	else
-	  vars=deplibs
-	fi
-	for var in $vars dependency_libs; do
-	  # Add libraries to $var in reverse order
-	  eval tmp_libs=\"\$$var\"
-	  new_libs=
-	  for deplib in $tmp_libs; do
-	    # FIXME: Pedantically, this is the right thing to do, so
-	    #        that some nasty dependency loop isn't accidentally
-	    #        broken:
-	    #new_libs="$deplib $new_libs"
-	    # Pragmatically, this seems to cause very few problems in
-	    # practice:
-	    case $deplib in
-	    -L*) new_libs="$deplib $new_libs" ;;
-	    -R*) ;;
-	    *)
-	      # And here is the reason: when a library appears more
-	      # than once as an explicit dependence of a library, or
-	      # is implicitly linked in more than once by the
-	      # compiler, it is considered special, and multiple
-	      # occurrences thereof are not removed.  Compare this
-	      # with having the same library being listed as a
-	      # dependency of multiple other libraries: in this case,
-	      # we know (pedantically, we assume) the library does not
-	      # need to be listed more than once, so we keep only the
-	      # last copy.  This is not always right, but it is rare
-	      # enough that we require users that really mean to play
-	      # such unportable linking tricks to link the library
-	      # using -Wl,-lname, so that libtool does not consider it
-	      # for duplicate removal.
-	      case " $specialdeplibs " in
-	      *" $deplib "*) new_libs="$deplib $new_libs" ;;
-	      *)
-		case " $new_libs " in
-		*" $deplib "*) ;;
-		*) new_libs="$deplib $new_libs" ;;
-		esac
-		;;
-	      esac
-	      ;;
-	    esac
-	  done
-	  tmp_libs=
-	  for deplib in $new_libs; do
-	    case $deplib in
-	    -L*)
-	      case " $tmp_libs " in
-	      *" $deplib "*) ;;
-	      *) func_append tmp_libs " $deplib" ;;
-	      esac
-	      ;;
-	    *) func_append tmp_libs " $deplib" ;;
-	    esac
-	  done
-	  eval $var=\"$tmp_libs\"
-	done # for var
-      fi
-
-      # Add Sun CC postdeps if required:
-      test CXX = "$tagname" && {
-        case $host_os in
-        linux*)
-          case `$CC -V 2>&1 | sed 5q` in
-          *Sun\ C*) # Sun C++ 5.9
-            func_suncc_cstd_abi
-
-            if test no != "$suncc_use_cstd_abi"; then
-              func_append postdeps ' -library=Cstd -library=Crun'
-            fi
-            ;;
-          esac
-          ;;
-
-        solaris*)
-          func_cc_basename "$CC"
-          case $func_cc_basename_result in
-          CC* | sunCC*)
-            func_suncc_cstd_abi
-
-            if test no != "$suncc_use_cstd_abi"; then
-              func_append postdeps ' -library=Cstd -library=Crun'
-            fi
-            ;;
-          esac
-          ;;
-        esac
-      }
-
-      # Last step: remove runtime libs from dependency_libs
-      # (they stay in deplibs)
-      tmp_libs=
-      for i in $dependency_libs; do
-	case " $predeps $postdeps $compiler_lib_search_path " in
-	*" $i "*)
-	  i=
-	  ;;
-	esac
-	if test -n "$i"; then
-	  func_append tmp_libs " $i"
-	fi
-      done
-      dependency_libs=$tmp_libs
-    done # for pass
-    if test prog = "$linkmode"; then
-      dlfiles=$newdlfiles
-    fi
-    if test prog = "$linkmode" || test lib = "$linkmode"; then
-      dlprefiles=$newdlprefiles
-    fi
-
-    case $linkmode in
-    oldlib)
-      if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
-	func_warning "'-dlopen' is ignored for archives"
-      fi
-
-      case " $deplibs" in
-      *\ -l* | *\ -L*)
-	func_warning "'-l' and '-L' are ignored for archives" ;;
-      esac
-
-      test -n "$rpath" && \
-	func_warning "'-rpath' is ignored for archives"
-
-      test -n "$xrpath" && \
-	func_warning "'-R' is ignored for archives"
-
-      test -n "$vinfo" && \
-	func_warning "'-version-info/-version-number' is ignored for archives"
-
-      test -n "$release" && \
-	func_warning "'-release' is ignored for archives"
-
-      test -n "$export_symbols$export_symbols_regex" && \
-	func_warning "'-export-symbols' is ignored for archives"
-
-      # Now set the variables for building old libraries.
-      build_libtool_libs=no
-      oldlibs=$output
-      func_append objs "$old_deplibs"
-      ;;
-
-    lib)
-      # Make sure we only generate libraries of the form 'libNAME.la'.
-      case $outputname in
-      lib*)
-	func_stripname 'lib' '.la' "$outputname"
-	name=$func_stripname_result
-	eval shared_ext=\"$shrext_cmds\"
-	eval libname=\"$libname_spec\"
-	;;
-      *)
-	test no = "$module" \
-	  && func_fatal_help "libtool library '$output' must begin with 'lib'"
-
-	if test no != "$need_lib_prefix"; then
-	  # Add the "lib" prefix for modules if required
-	  func_stripname '' '.la' "$outputname"
-	  name=$func_stripname_result
-	  eval shared_ext=\"$shrext_cmds\"
-	  eval libname=\"$libname_spec\"
-	else
-	  func_stripname '' '.la' "$outputname"
-	  libname=$func_stripname_result
-	fi
-	;;
-      esac
-
-      if test -n "$objs"; then
-	if test pass_all != "$deplibs_check_method"; then
-	  func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs"
-	else
-	  echo
-	  $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
-	  $ECHO "*** objects $objs is not portable!"
-	  func_append libobjs " $objs"
-	fi
-      fi
-
-      test no = "$dlself" \
-	|| func_warning "'-dlopen self' is ignored for libtool libraries"
-
-      set dummy $rpath
-      shift
-      test 1 -lt "$#" \
-	&& func_warning "ignoring multiple '-rpath's for a libtool library"
-
-      install_libdir=$1
-
-      oldlibs=
-      if test -z "$rpath"; then
-	if test yes = "$build_libtool_libs"; then
-	  # Building a libtool convenience library.
-	  # Some compilers have problems with a '.al' extension so
-	  # convenience libraries should have the same extension an
-	  # archive normally would.
-	  oldlibs="$output_objdir/$libname.$libext $oldlibs"
-	  build_libtool_libs=convenience
-	  build_old_libs=yes
-	fi
-
-	test -n "$vinfo" && \
-	  func_warning "'-version-info/-version-number' is ignored for convenience libraries"
-
-	test -n "$release" && \
-	  func_warning "'-release' is ignored for convenience libraries"
-      else
-
-	# Parse the version information argument.
-	save_ifs=$IFS; IFS=:
-	set dummy $vinfo 0 0 0
-	shift
-	IFS=$save_ifs
-
-	test -n "$7" && \
-	  func_fatal_help "too many parameters to '-version-info'"
-
-	# convert absolute version numbers to libtool ages
-	# this retains compatibility with .la files and attempts
-	# to make the code below a bit more comprehensible
-
-	case $vinfo_number in
-	yes)
-	  number_major=$1
-	  number_minor=$2
-	  number_revision=$3
-	  #
-	  # There are really only two kinds -- those that
-	  # use the current revision as the major version
-	  # and those that subtract age and use age as
-	  # a minor version.  But, then there is irix
-	  # that has an extra 1 added just for fun
-	  #
-	  case $version_type in
-	  # correct linux to gnu/linux during the next big refactor
-	  darwin|freebsd-elf|linux|osf|windows|none)
-	    func_arith $number_major + $number_minor
-	    current=$func_arith_result
-	    age=$number_minor
-	    revision=$number_revision
-	    ;;
-	  freebsd-aout|qnx|sunos)
-	    current=$number_major
-	    revision=$number_minor
-	    age=0
-	    ;;
-	  irix|nonstopux)
-	    func_arith $number_major + $number_minor
-	    current=$func_arith_result
-	    age=$number_minor
-	    revision=$number_minor
-	    lt_irix_increment=no
-	    ;;
-	  esac
-	  ;;
-	no)
-	  current=$1
-	  revision=$2
-	  age=$3
-	  ;;
-	esac
-
-	# Check that each of the things are valid numbers.
-	case $current in
-	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
-	*)
-	  func_error "CURRENT '$current' must be a nonnegative integer"
-	  func_fatal_error "'$vinfo' is not valid version information"
-	  ;;
-	esac
-
-	case $revision in
-	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
-	*)
-	  func_error "REVISION '$revision' must be a nonnegative integer"
-	  func_fatal_error "'$vinfo' is not valid version information"
-	  ;;
-	esac
-
-	case $age in
-	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
-	*)
-	  func_error "AGE '$age' must be a nonnegative integer"
-	  func_fatal_error "'$vinfo' is not valid version information"
-	  ;;
-	esac
-
-	if test "$age" -gt "$current"; then
-	  func_error "AGE '$age' is greater than the current interface number '$current'"
-	  func_fatal_error "'$vinfo' is not valid version information"
-	fi
-
-	# Calculate the version variables.
-	major=
-	versuffix=
-	verstring=
-	case $version_type in
-	none) ;;
-
-	darwin)
-	  # Like Linux, but with the current version available in
-	  # verstring for coding it into the library header
-	  func_arith $current - $age
-	  major=.$func_arith_result
-	  versuffix=$major.$age.$revision
-	  # Darwin ld doesn't like 0 for these options...
-	  func_arith $current + 1
-	  minor_current=$func_arith_result
-	  xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
-	  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
-          # On Darwin other compilers
-          case $CC in
-              nagfor*)
-                  verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
-                  ;;
-              *)
-                  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
-                  ;;
-          esac
-	  ;;
-
-	freebsd-aout)
-	  major=.$current
-	  versuffix=.$current.$revision
-	  ;;
-
-	freebsd-elf)
-	  func_arith $current - $age
-	  major=.$func_arith_result
-	  versuffix=$major.$age.$revision
-	  ;;
-
-	irix | nonstopux)
-	  if test no = "$lt_irix_increment"; then
-	    func_arith $current - $age
-	  else
-	    func_arith $current - $age + 1
-	  fi
-	  major=$func_arith_result
-
-	  case $version_type in
-	    nonstopux) verstring_prefix=nonstopux ;;
-	    *)         verstring_prefix=sgi ;;
-	  esac
-	  verstring=$verstring_prefix$major.$revision
-
-	  # Add in all the interfaces that we are compatible with.
-	  loop=$revision
-	  while test 0 -ne "$loop"; do
-	    func_arith $revision - $loop
-	    iface=$func_arith_result
-	    func_arith $loop - 1
-	    loop=$func_arith_result
-	    verstring=$verstring_prefix$major.$iface:$verstring
-	  done
-
-	  # Before this point, $major must not contain '.'.
-	  major=.$major
-	  versuffix=$major.$revision
-	  ;;
-
-	linux) # correct to gnu/linux during the next big refactor
-	  func_arith $current - $age
-	  major=.$func_arith_result
-	  versuffix=$major.$age.$revision
-	  ;;
-
-	osf)
-	  func_arith $current - $age
-	  major=.$func_arith_result
-	  versuffix=.$current.$age.$revision
-	  verstring=$current.$age.$revision
-
-	  # Add in all the interfaces that we are compatible with.
-	  loop=$age
-	  while test 0 -ne "$loop"; do
-	    func_arith $current - $loop
-	    iface=$func_arith_result
-	    func_arith $loop - 1
-	    loop=$func_arith_result
-	    verstring=$verstring:$iface.0
-	  done
-
-	  # Make executables depend on our current version.
-	  func_append verstring ":$current.0"
-	  ;;
-
-	qnx)
-	  major=.$current
-	  versuffix=.$current
-	  ;;
-
-	sco)
-	  major=.$current
-	  versuffix=.$current
-	  ;;
-
-	sunos)
-	  major=.$current
-	  versuffix=.$current.$revision
-	  ;;
-
-	windows)
-	  # Use '-' rather than '.', since we only want one
-	  # extension on DOS 8.3 file systems.
-	  func_arith $current - $age
-	  major=$func_arith_result
-	  versuffix=-$major
-	  ;;
-
-	*)
-	  func_fatal_configuration "unknown library version type '$version_type'"
-	  ;;
-	esac
-
-	# Clear the version info if we defaulted, and they specified a release.
-	if test -z "$vinfo" && test -n "$release"; then
-	  major=
-	  case $version_type in
-	  darwin)
-	    # we can't check for "0.0" in archive_cmds due to quoting
-	    # problems, so we reset it completely
-	    verstring=
-	    ;;
-	  *)
-	    verstring=0.0
-	    ;;
-	  esac
-	  if test no = "$need_version"; then
-	    versuffix=
-	  else
-	    versuffix=.0.0
-	  fi
-	fi
-
-	# Remove version info from name if versioning should be avoided
-	if test yes,no = "$avoid_version,$need_version"; then
-	  major=
-	  versuffix=
-	  verstring=
-	fi
-
-	# Check to see if the archive will have undefined symbols.
-	if test yes = "$allow_undefined"; then
-	  if test unsupported = "$allow_undefined_flag"; then
-	    if test yes = "$build_old_libs"; then
-	      func_warning "undefined symbols not allowed in $host shared libraries; building static only"
-	      build_libtool_libs=no
-	    else
-	      func_fatal_error "can't build $host shared library unless -no-undefined is specified"
-	    fi
-	  fi
-	else
-	  # Don't allow undefined symbols.
-	  allow_undefined_flag=$no_undefined_flag
-	fi
-
-      fi
-
-      func_generate_dlsyms "$libname" "$libname" :
-      func_append libobjs " $symfileobj"
-      test " " = "$libobjs" && libobjs=
-
-      if test relink != "$opt_mode"; then
-	# Remove our outputs, but don't remove object files since they
-	# may have been created when compiling PIC objects.
-	removelist=
-	tempremovelist=`$ECHO "$output_objdir/*"`
-	for p in $tempremovelist; do
-	  case $p in
-	    *.$objext | *.gcno)
-	       ;;
-	    $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*)
-	       if test -n "$precious_files_regex"; then
-		 if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
-		 then
-		   continue
-		 fi
-	       fi
-	       func_append removelist " $p"
-	       ;;
-	    *) ;;
-	  esac
-	done
-	test -n "$removelist" && \
-	  func_show_eval "${RM}r \$removelist"
-      fi
-
-      # Now set the variables for building old libraries.
-      if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then
-	func_append oldlibs " $output_objdir/$libname.$libext"
-
-	# Transform .lo files to .o files.
-	oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP`
-      fi
-
-      # Eliminate all temporary directories.
-      #for path in $notinst_path; do
-      #	lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
-      #	deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
-      #	dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
-      #done
-
-      if test -n "$xrpath"; then
-	# If the user specified any rpath flags, then add them.
-	temp_xrpath=
-	for libdir in $xrpath; do
-	  func_replace_sysroot "$libdir"
-	  func_append temp_xrpath " -R$func_replace_sysroot_result"
-	  case "$finalize_rpath " in
-	  *" $libdir "*) ;;
-	  *) func_append finalize_rpath " $libdir" ;;
-	  esac
-	done
-	if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then
-	  dependency_libs="$temp_xrpath $dependency_libs"
-	fi
-      fi
-
-      # Make sure dlfiles contains only unique files that won't be dlpreopened
-      old_dlfiles=$dlfiles
-      dlfiles=
-      for lib in $old_dlfiles; do
-	case " $dlprefiles $dlfiles " in
-	*" $lib "*) ;;
-	*) func_append dlfiles " $lib" ;;
-	esac
-      done
-
-      # Make sure dlprefiles contains only unique files
-      old_dlprefiles=$dlprefiles
-      dlprefiles=
-      for lib in $old_dlprefiles; do
-	case "$dlprefiles " in
-	*" $lib "*) ;;
-	*) func_append dlprefiles " $lib" ;;
-	esac
-      done
-
-      if test yes = "$build_libtool_libs"; then
-	if test -n "$rpath"; then
-	  case $host in
-	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
-	    # these systems don't actually have a c library (as such)!
-	    ;;
-	  *-*-rhapsody* | *-*-darwin1.[012])
-	    # Rhapsody C library is in the System framework
-	    func_append deplibs " System.ltframework"
-	    ;;
-	  *-*-netbsd*)
-	    # Don't link with libc until the a.out ld.so is fixed.
-	    ;;
-	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
-	    # Do not include libc due to us having libc/libc_r.
-	    ;;
-	  *-*-sco3.2v5* | *-*-sco5v6*)
-	    # Causes problems with __ctype
-	    ;;
-	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
-	    # Compiler inserts libc in the correct place for threads to work
-	    ;;
-	  *)
-	    # Add libc to deplibs on all other systems if necessary.
-	    if test yes = "$build_libtool_need_lc"; then
-	      func_append deplibs " -lc"
-	    fi
-	    ;;
-	  esac
-	fi
-
-	# Transform deplibs into only deplibs that can be linked in shared.
-	name_save=$name
-	libname_save=$libname
-	release_save=$release
-	versuffix_save=$versuffix
-	major_save=$major
-	# I'm not sure if I'm treating the release correctly.  I think
-	# release should show up in the -l (ie -lgmp5) so we don't want to
-	# add it in twice.  Is that correct?
-	release=
-	versuffix=
-	major=
-	newdeplibs=
-	droppeddeps=no
-	case $deplibs_check_method in
-	pass_all)
-	  # Don't check for shared/static.  Everything works.
-	  # This might be a little naive.  We might want to check
-	  # whether the library exists or not.  But this is on
-	  # osf3 & osf4 and I'm not really sure... Just
-	  # implementing what was already the behavior.
-	  newdeplibs=$deplibs
-	  ;;
-	test_compile)
-	  # This code stresses the "libraries are programs" paradigm to its
-	  # limits. Maybe even breaks it.  We compile a program, linking it
-	  # against the deplibs as a proxy for the library.  Then we can check
-	  # whether they linked in statically or dynamically with ldd.
-	  $opt_dry_run || $RM conftest.c
-	  cat > conftest.c <<EOF
-	  int main() { return 0; }
-EOF
-	  $opt_dry_run || $RM conftest
-	  if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
-	    ldd_output=`ldd conftest`
-	    for i in $deplibs; do
-	      case $i in
-	      -l*)
-		func_stripname -l '' "$i"
-		name=$func_stripname_result
-		if test yes = "$allow_libtool_libs_with_static_runtimes"; then
-		  case " $predeps $postdeps " in
-		  *" $i "*)
-		    func_append newdeplibs " $i"
-		    i=
-		    ;;
-		  esac
-		fi
-		if test -n "$i"; then
-		  libname=`eval "\\$ECHO \"$libname_spec\""`
-		  deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
-		  set dummy $deplib_matches; shift
-		  deplib_match=$1
-		  if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
-		    func_append newdeplibs " $i"
-		  else
-		    droppeddeps=yes
-		    echo
-		    $ECHO "*** Warning: dynamic linker does not accept needed library $i."
-		    echo "*** I have the capability to make that library automatically link in when"
-		    echo "*** you link to this library.  But I can only do this if you have a"
-		    echo "*** shared version of the library, which I believe you do not have"
-		    echo "*** because a test_compile did reveal that the linker did not use it for"
-		    echo "*** its dynamic dependency list that programs get resolved with at runtime."
-		  fi
-		fi
-		;;
-	      *)
-		func_append newdeplibs " $i"
-		;;
-	      esac
-	    done
-	  else
-	    # Error occurred in the first compile.  Let's try to salvage
-	    # the situation: Compile a separate program for each library.
-	    for i in $deplibs; do
-	      case $i in
-	      -l*)
-		func_stripname -l '' "$i"
-		name=$func_stripname_result
-		$opt_dry_run || $RM conftest
-		if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
-		  ldd_output=`ldd conftest`
-		  if test yes = "$allow_libtool_libs_with_static_runtimes"; then
-		    case " $predeps $postdeps " in
-		    *" $i "*)
-		      func_append newdeplibs " $i"
-		      i=
-		      ;;
-		    esac
-		  fi
-		  if test -n "$i"; then
-		    libname=`eval "\\$ECHO \"$libname_spec\""`
-		    deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
-		    set dummy $deplib_matches; shift
-		    deplib_match=$1
-		    if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
-		      func_append newdeplibs " $i"
-		    else
-		      droppeddeps=yes
-		      echo
-		      $ECHO "*** Warning: dynamic linker does not accept needed library $i."
-		      echo "*** I have the capability to make that library automatically link in when"
-		      echo "*** you link to this library.  But I can only do this if you have a"
-		      echo "*** shared version of the library, which you do not appear to have"
-		      echo "*** because a test_compile did reveal that the linker did not use this one"
-		      echo "*** as a dynamic dependency that programs can get resolved with at runtime."
-		    fi
-		  fi
-		else
-		  droppeddeps=yes
-		  echo
-		  $ECHO "*** Warning!  Library $i is needed by this library but I was not able to"
-		  echo "*** make it link in!  You will probably need to install it or some"
-		  echo "*** library that it depends on before this library will be fully"
-		  echo "*** functional.  Installing it before continuing would be even better."
-		fi
-		;;
-	      *)
-		func_append newdeplibs " $i"
-		;;
-	      esac
-	    done
-	  fi
-	  ;;
-	file_magic*)
-	  set dummy $deplibs_check_method; shift
-	  file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
-	  for a_deplib in $deplibs; do
-	    case $a_deplib in
-	    -l*)
-	      func_stripname -l '' "$a_deplib"
-	      name=$func_stripname_result
-	      if test yes = "$allow_libtool_libs_with_static_runtimes"; then
-		case " $predeps $postdeps " in
-		*" $a_deplib "*)
-		  func_append newdeplibs " $a_deplib"
-		  a_deplib=
-		  ;;
-		esac
-	      fi
-	      if test -n "$a_deplib"; then
-		libname=`eval "\\$ECHO \"$libname_spec\""`
-		if test -n "$file_magic_glob"; then
-		  libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
-		else
-		  libnameglob=$libname
-		fi
-		test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob`
-		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
-		  if test yes = "$want_nocaseglob"; then
-		    shopt -s nocaseglob
-		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
-		    $nocaseglob
-		  else
-		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
-		  fi
-		  for potent_lib in $potential_libs; do
-		      # Follow soft links.
-		      if ls -lLd "$potent_lib" 2>/dev/null |
-			 $GREP " -> " >/dev/null; then
-			continue
-		      fi
-		      # The statement above tries to avoid entering an
-		      # endless loop below, in case of cyclic links.
-		      # We might still enter an endless loop, since a link
-		      # loop can be closed while we follow links,
-		      # but so what?
-		      potlib=$potent_lib
-		      while test -h "$potlib" 2>/dev/null; do
-			potliblink=`ls -ld $potlib | $SED 's/.* -> //'`
-			case $potliblink in
-			[\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;;
-			*) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";;
-			esac
-		      done
-		      if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
-			 $SED -e 10q |
-			 $EGREP "$file_magic_regex" > /dev/null; then
-			func_append newdeplibs " $a_deplib"
-			a_deplib=
-			break 2
-		      fi
-		  done
-		done
-	      fi
-	      if test -n "$a_deplib"; then
-		droppeddeps=yes
-		echo
-		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
-		echo "*** I have the capability to make that library automatically link in when"
-		echo "*** you link to this library.  But I can only do this if you have a"
-		echo "*** shared version of the library, which you do not appear to have"
-		echo "*** because I did check the linker path looking for a file starting"
-		if test -z "$potlib"; then
-		  $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
-		else
-		  $ECHO "*** with $libname and none of the candidates passed a file format test"
-		  $ECHO "*** using a file magic. Last file checked: $potlib"
-		fi
-	      fi
-	      ;;
-	    *)
-	      # Add a -L argument.
-	      func_append newdeplibs " $a_deplib"
-	      ;;
-	    esac
-	  done # Gone through all deplibs.
-	  ;;
-	match_pattern*)
-	  set dummy $deplibs_check_method; shift
-	  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
-	  for a_deplib in $deplibs; do
-	    case $a_deplib in
-	    -l*)
-	      func_stripname -l '' "$a_deplib"
-	      name=$func_stripname_result
-	      if test yes = "$allow_libtool_libs_with_static_runtimes"; then
-		case " $predeps $postdeps " in
-		*" $a_deplib "*)
-		  func_append newdeplibs " $a_deplib"
-		  a_deplib=
-		  ;;
-		esac
-	      fi
-	      if test -n "$a_deplib"; then
-		libname=`eval "\\$ECHO \"$libname_spec\""`
-		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
-		  potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
-		  for potent_lib in $potential_libs; do
-		    potlib=$potent_lib # see symlink-check above in file_magic test
-		    if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
-		       $EGREP "$match_pattern_regex" > /dev/null; then
-		      func_append newdeplibs " $a_deplib"
-		      a_deplib=
-		      break 2
-		    fi
-		  done
-		done
-	      fi
-	      if test -n "$a_deplib"; then
-		droppeddeps=yes
-		echo
-		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
-		echo "*** I have the capability to make that library automatically link in when"
-		echo "*** you link to this library.  But I can only do this if you have a"
-		echo "*** shared version of the library, which you do not appear to have"
-		echo "*** because I did check the linker path looking for a file starting"
-		if test -z "$potlib"; then
-		  $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
-		else
-		  $ECHO "*** with $libname and none of the candidates passed a file format test"
-		  $ECHO "*** using a regex pattern. Last file checked: $potlib"
-		fi
-	      fi
-	      ;;
-	    *)
-	      # Add a -L argument.
-	      func_append newdeplibs " $a_deplib"
-	      ;;
-	    esac
-	  done # Gone through all deplibs.
-	  ;;
-	none | unknown | *)
-	  newdeplibs=
-	  tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
-	  if test yes = "$allow_libtool_libs_with_static_runtimes"; then
-	    for i in $predeps $postdeps; do
-	      # can't use Xsed below, because $i might contain '/'
-	      tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"`
-	    done
-	  fi
-	  case $tmp_deplibs in
-	  *[!\	\ ]*)
-	    echo
-	    if test none = "$deplibs_check_method"; then
-	      echo "*** Warning: inter-library dependencies are not supported in this platform."
-	    else
-	      echo "*** Warning: inter-library dependencies are not known to be supported."
-	    fi
-	    echo "*** All declared inter-library dependencies are being dropped."
-	    droppeddeps=yes
-	    ;;
-	  esac
-	  ;;
-	esac
-	versuffix=$versuffix_save
-	major=$major_save
-	release=$release_save
-	libname=$libname_save
-	name=$name_save
-
-	case $host in
-	*-*-rhapsody* | *-*-darwin1.[012])
-	  # On Rhapsody replace the C library with the System framework
-	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
-	  ;;
-	esac
-
-	if test yes = "$droppeddeps"; then
-	  if test yes = "$module"; then
-	    echo
-	    echo "*** Warning: libtool could not satisfy all declared inter-library"
-	    $ECHO "*** dependencies of module $libname.  Therefore, libtool will create"
-	    echo "*** a static module, that should work as long as the dlopening"
-	    echo "*** application is linked with the -dlopen flag."
-	    if test -z "$global_symbol_pipe"; then
-	      echo
-	      echo "*** However, this would only work if libtool was able to extract symbol"
-	      echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
-	      echo "*** not find such a program.  So, this module is probably useless."
-	      echo "*** 'nm' from GNU binutils and a full rebuild may help."
-	    fi
-	    if test no = "$build_old_libs"; then
-	      oldlibs=$output_objdir/$libname.$libext
-	      build_libtool_libs=module
-	      build_old_libs=yes
-	    else
-	      build_libtool_libs=no
-	    fi
-	  else
-	    echo "*** The inter-library dependencies that have been dropped here will be"
-	    echo "*** automatically added whenever a program is linked with this library"
-	    echo "*** or is declared to -dlopen it."
-
-	    if test no = "$allow_undefined"; then
-	      echo
-	      echo "*** Since this library must not contain undefined symbols,"
-	      echo "*** because either the platform does not support them or"
-	      echo "*** it was explicitly requested with -no-undefined,"
-	      echo "*** libtool will only create a static version of it."
-	      if test no = "$build_old_libs"; then
-		oldlibs=$output_objdir/$libname.$libext
-		build_libtool_libs=module
-		build_old_libs=yes
-	      else
-		build_libtool_libs=no
-	      fi
-	    fi
-	  fi
-	fi
-	# Done checking deplibs!
-	deplibs=$newdeplibs
-      fi
-      # Time to change all our "foo.ltframework" stuff back to "-framework foo"
-      case $host in
-	*-*-darwin*)
-	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
-	  new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
-	  deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
-	  ;;
-      esac
-
-      # move library search paths that coincide with paths to not yet
-      # installed libraries to the beginning of the library search list
-      new_libs=
-      for path in $notinst_path; do
-	case " $new_libs " in
-	*" -L$path/$objdir "*) ;;
-	*)
-	  case " $deplibs " in
-	  *" -L$path/$objdir "*)
-	    func_append new_libs " -L$path/$objdir" ;;
-	  esac
-	  ;;
-	esac
-      done
-      for deplib in $deplibs; do
-	case $deplib in
-	-L*)
-	  case " $new_libs " in
-	  *" $deplib "*) ;;
-	  *) func_append new_libs " $deplib" ;;
-	  esac
-	  ;;
-	*) func_append new_libs " $deplib" ;;
-	esac
-      done
-      deplibs=$new_libs
-
-      # All the library-specific variables (install_libdir is set above).
-      library_names=
-      old_library=
-      dlname=
-
-      # Test again, we may have decided not to build it any more
-      if test yes = "$build_libtool_libs"; then
-	# Remove $wl instances when linking with ld.
-	# FIXME: should test the right _cmds variable.
-	case $archive_cmds in
-	  *\$LD\ *) wl= ;;
-        esac
-	if test yes = "$hardcode_into_libs"; then
-	  # Hardcode the library paths
-	  hardcode_libdirs=
-	  dep_rpath=
-	  rpath=$finalize_rpath
-	  test relink = "$opt_mode" || rpath=$compile_rpath$rpath
-	  for libdir in $rpath; do
-	    if test -n "$hardcode_libdir_flag_spec"; then
-	      if test -n "$hardcode_libdir_separator"; then
-		func_replace_sysroot "$libdir"
-		libdir=$func_replace_sysroot_result
-		if test -z "$hardcode_libdirs"; then
-		  hardcode_libdirs=$libdir
-		else
-		  # Just accumulate the unique libdirs.
-		  case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
-		  *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
-		    ;;
-		  *)
-		    func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
-		    ;;
-		  esac
-		fi
-	      else
-		eval flag=\"$hardcode_libdir_flag_spec\"
-		func_append dep_rpath " $flag"
-	      fi
-	    elif test -n "$runpath_var"; then
-	      case "$perm_rpath " in
-	      *" $libdir "*) ;;
-	      *) func_append perm_rpath " $libdir" ;;
-	      esac
-	    fi
-	  done
-	  # Substitute the hardcoded libdirs into the rpath.
-	  if test -n "$hardcode_libdir_separator" &&
-	     test -n "$hardcode_libdirs"; then
-	    libdir=$hardcode_libdirs
-	    eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
-	  fi
-	  if test -n "$runpath_var" && test -n "$perm_rpath"; then
-	    # We should set the runpath_var.
-	    rpath=
-	    for dir in $perm_rpath; do
-	      func_append rpath "$dir:"
-	    done
-	    eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
-	  fi
-	  test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
-	fi
-
-	shlibpath=$finalize_shlibpath
-	test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath
-	if test -n "$shlibpath"; then
-	  eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
-	fi
-
-	# Get the real and link names of the library.
-	eval shared_ext=\"$shrext_cmds\"
-	eval library_names=\"$library_names_spec\"
-	set dummy $library_names
-	shift
-	realname=$1
-	shift
-
-	if test -n "$soname_spec"; then
-	  eval soname=\"$soname_spec\"
-	else
-	  soname=$realname
-	fi
-	if test -z "$dlname"; then
-	  dlname=$soname
-	fi
-
-	lib=$output_objdir/$realname
-	linknames=
-	for link
-	do
-	  func_append linknames " $link"
-	done
-
-	# Use standard objects if they are pic
-	test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
-	test "X$libobjs" = "X " && libobjs=
-
-	delfiles=
-	if test -n "$export_symbols" && test -n "$include_expsyms"; then
-	  $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
-	  export_symbols=$output_objdir/$libname.uexp
-	  func_append delfiles " $export_symbols"
-	fi
-
-	orig_export_symbols=
-	case $host_os in
-	cygwin* | mingw* | cegcc*)
-	  if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
-	    # exporting using user supplied symfile
-	    func_dll_def_p "$export_symbols" || {
-	      # and it's NOT already a .def file. Must figure out
-	      # which of the given symbols are data symbols and tag
-	      # them as such. So, trigger use of export_symbols_cmds.
-	      # export_symbols gets reassigned inside the "prepare
-	      # the list of exported symbols" if statement, so the
-	      # include_expsyms logic still works.
-	      orig_export_symbols=$export_symbols
-	      export_symbols=
-	      always_export_symbols=yes
-	    }
-	  fi
-	  ;;
-	esac
-
-	# Prepare the list of exported symbols
-	if test -z "$export_symbols"; then
-	  if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then
-	    func_verbose "generating symbol list for '$libname.la'"
-	    export_symbols=$output_objdir/$libname.exp
-	    $opt_dry_run || $RM $export_symbols
-	    cmds=$export_symbols_cmds
-	    save_ifs=$IFS; IFS='~'
-	    for cmd1 in $cmds; do
-	      IFS=$save_ifs
-	      # Take the normal branch if the nm_file_list_spec branch
-	      # doesn't work or if tool conversion is not needed.
-	      case $nm_file_list_spec~$to_tool_file_cmd in
-		*~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
-		  try_normal_branch=yes
-		  eval cmd=\"$cmd1\"
-		  func_len " $cmd"
-		  len=$func_len_result
-		  ;;
-		*)
-		  try_normal_branch=no
-		  ;;
-	      esac
-	      if test yes = "$try_normal_branch" \
-		 && { test "$len" -lt "$max_cmd_len" \
-		      || test "$max_cmd_len" -le -1; }
-	      then
-		func_show_eval "$cmd" 'exit $?'
-		skipped_export=false
-	      elif test -n "$nm_file_list_spec"; then
-		func_basename "$output"
-		output_la=$func_basename_result
-		save_libobjs=$libobjs
-		save_output=$output
-		output=$output_objdir/$output_la.nm
-		func_to_tool_file "$output"
-		libobjs=$nm_file_list_spec$func_to_tool_file_result
-		func_append delfiles " $output"
-		func_verbose "creating $NM input file list: $output"
-		for obj in $save_libobjs; do
-		  func_to_tool_file "$obj"
-		  $ECHO "$func_to_tool_file_result"
-		done > "$output"
-		eval cmd=\"$cmd1\"
-		func_show_eval "$cmd" 'exit $?'
-		output=$save_output
-		libobjs=$save_libobjs
-		skipped_export=false
-	      else
-		# The command line is too long to execute in one step.
-		func_verbose "using reloadable object file for export list..."
-		skipped_export=:
-		# Break out early, otherwise skipped_export may be
-		# set to false by a later but shorter cmd.
-		break
-	      fi
-	    done
-	    IFS=$save_ifs
-	    if test -n "$export_symbols_regex" && test : != "$skipped_export"; then
-	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
-	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
-	    fi
-	  fi
-	fi
-
-	if test -n "$export_symbols" && test -n "$include_expsyms"; then
-	  tmp_export_symbols=$export_symbols
-	  test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
-	  $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
-	fi
-
-	if test : != "$skipped_export" && test -n "$orig_export_symbols"; then
-	  # The given exports_symbols file has to be filtered, so filter it.
-	  func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
-	  # FIXME: $output_objdir/$libname.filter potentially contains lots of
-	  # 's' commands, which not all seds can handle. GNU sed should be fine
-	  # though. Also, the filter scales superlinearly with the number of
-	  # global variables. join(1) would be nice here, but unfortunately
-	  # isn't a blessed tool.
-	  $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
-	  func_append delfiles " $export_symbols $output_objdir/$libname.filter"
-	  export_symbols=$output_objdir/$libname.def
-	  $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
-	fi
-
-	tmp_deplibs=
-	for test_deplib in $deplibs; do
-	  case " $convenience " in
-	  *" $test_deplib "*) ;;
-	  *)
-	    func_append tmp_deplibs " $test_deplib"
-	    ;;
-	  esac
-	done
-	deplibs=$tmp_deplibs
-
-	if test -n "$convenience"; then
-	  if test -n "$whole_archive_flag_spec" &&
-	    test yes = "$compiler_needs_object" &&
-	    test -z "$libobjs"; then
-	    # extract the archives, so we have objects to list.
-	    # TODO: could optimize this to just extract one archive.
-	    whole_archive_flag_spec=
-	  fi
-	  if test -n "$whole_archive_flag_spec"; then
-	    save_libobjs=$libobjs
-	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
-	    test "X$libobjs" = "X " && libobjs=
-	  else
-	    gentop=$output_objdir/${outputname}x
-	    func_append generated " $gentop"
-
-	    func_extract_archives $gentop $convenience
-	    func_append libobjs " $func_extract_archives_result"
-	    test "X$libobjs" = "X " && libobjs=
-	  fi
-	fi
-
-	if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then
-	  eval flag=\"$thread_safe_flag_spec\"
-	  func_append linker_flags " $flag"
-	fi
-
-	# Make a backup of the uninstalled library when relinking
-	if test relink = "$opt_mode"; then
-	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
-	fi
-
-	# Do each of the archive commands.
-	if test yes = "$module" && test -n "$module_cmds"; then
-	  if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
-	    eval test_cmds=\"$module_expsym_cmds\"
-	    cmds=$module_expsym_cmds
-	  else
-	    eval test_cmds=\"$module_cmds\"
-	    cmds=$module_cmds
-	  fi
-	else
-	  if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
-	    eval test_cmds=\"$archive_expsym_cmds\"
-	    cmds=$archive_expsym_cmds
-	  else
-	    eval test_cmds=\"$archive_cmds\"
-	    cmds=$archive_cmds
-	  fi
-	fi
-
-	if test : != "$skipped_export" &&
-	   func_len " $test_cmds" &&
-	   len=$func_len_result &&
-	   test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
-	  :
-	else
-	  # The command line is too long to link in one step, link piecewise
-	  # or, if using GNU ld and skipped_export is not :, use a linker
-	  # script.
-
-	  # Save the value of $output and $libobjs because we want to
-	  # use them later.  If we have whole_archive_flag_spec, we
-	  # want to use save_libobjs as it was before
-	  # whole_archive_flag_spec was expanded, because we can't
-	  # assume the linker understands whole_archive_flag_spec.
-	  # This may have to be revisited, in case too many
-	  # convenience libraries get linked in and end up exceeding
-	  # the spec.
-	  if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
-	    save_libobjs=$libobjs
-	  fi
-	  save_output=$output
-	  func_basename "$output"
-	  output_la=$func_basename_result
-
-	  # Clear the reloadable object creation command queue and
-	  # initialize k to one.
-	  test_cmds=
-	  concat_cmds=
-	  objlist=
-	  last_robj=
-	  k=1
-
-	  if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
-	    output=$output_objdir/$output_la.lnkscript
-	    func_verbose "creating GNU ld script: $output"
-	    echo 'INPUT (' > $output
-	    for obj in $save_libobjs
-	    do
-	      func_to_tool_file "$obj"
-	      $ECHO "$func_to_tool_file_result" >> $output
-	    done
-	    echo ')' >> $output
-	    func_append delfiles " $output"
-	    func_to_tool_file "$output"
-	    output=$func_to_tool_file_result
-	  elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
-	    output=$output_objdir/$output_la.lnk
-	    func_verbose "creating linker input file list: $output"
-	    : > $output
-	    set x $save_libobjs
-	    shift
-	    firstobj=
-	    if test yes = "$compiler_needs_object"; then
-	      firstobj="$1 "
-	      shift
-	    fi
-	    for obj
-	    do
-	      func_to_tool_file "$obj"
-	      $ECHO "$func_to_tool_file_result" >> $output
-	    done
-	    func_append delfiles " $output"
-	    func_to_tool_file "$output"
-	    output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
-	  else
-	    if test -n "$save_libobjs"; then
-	      func_verbose "creating reloadable object files..."
-	      output=$output_objdir/$output_la-$k.$objext
-	      eval test_cmds=\"$reload_cmds\"
-	      func_len " $test_cmds"
-	      len0=$func_len_result
-	      len=$len0
-
-	      # Loop over the list of objects to be linked.
-	      for obj in $save_libobjs
-	      do
-		func_len " $obj"
-		func_arith $len + $func_len_result
-		len=$func_arith_result
-		if test -z "$objlist" ||
-		   test "$len" -lt "$max_cmd_len"; then
-		  func_append objlist " $obj"
-		else
-		  # The command $test_cmds is almost too long, add a
-		  # command to the queue.
-		  if test 1 -eq "$k"; then
-		    # The first file doesn't have a previous command to add.
-		    reload_objs=$objlist
-		    eval concat_cmds=\"$reload_cmds\"
-		  else
-		    # All subsequent reloadable object files will link in
-		    # the last one created.
-		    reload_objs="$objlist $last_robj"
-		    eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
-		  fi
-		  last_robj=$output_objdir/$output_la-$k.$objext
-		  func_arith $k + 1
-		  k=$func_arith_result
-		  output=$output_objdir/$output_la-$k.$objext
-		  objlist=" $obj"
-		  func_len " $last_robj"
-		  func_arith $len0 + $func_len_result
-		  len=$func_arith_result
-		fi
-	      done
-	      # Handle the remaining objects by creating one last
-	      # reloadable object file.  All subsequent reloadable object
-	      # files will link in the last one created.
-	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
-	      reload_objs="$objlist $last_robj"
-	      eval concat_cmds=\"\$concat_cmds$reload_cmds\"
-	      if test -n "$last_robj"; then
-	        eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
-	      fi
-	      func_append delfiles " $output"
-
-	    else
-	      output=
-	    fi
-
-	    ${skipped_export-false} && {
-	      func_verbose "generating symbol list for '$libname.la'"
-	      export_symbols=$output_objdir/$libname.exp
-	      $opt_dry_run || $RM $export_symbols
-	      libobjs=$output
-	      # Append the command to create the export file.
-	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
-	      eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
-	      if test -n "$last_robj"; then
-		eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
-	      fi
-	    }
-
-	    test -n "$save_libobjs" &&
-	      func_verbose "creating a temporary reloadable object file: $output"
-
-	    # Loop through the commands generated above and execute them.
-	    save_ifs=$IFS; IFS='~'
-	    for cmd in $concat_cmds; do
-	      IFS=$save_ifs
-	      $opt_quiet || {
-		  func_quote_for_expand "$cmd"
-		  eval "func_echo $func_quote_for_expand_result"
-	      }
-	      $opt_dry_run || eval "$cmd" || {
-		lt_exit=$?
-
-		# Restore the uninstalled library and exit
-		if test relink = "$opt_mode"; then
-		  ( cd "$output_objdir" && \
-		    $RM "${realname}T" && \
-		    $MV "${realname}U" "$realname" )
-		fi
-
-		exit $lt_exit
-	      }
-	    done
-	    IFS=$save_ifs
-
-	    if test -n "$export_symbols_regex" && ${skipped_export-false}; then
-	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
-	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
-	    fi
-	  fi
-
-          ${skipped_export-false} && {
-	    if test -n "$export_symbols" && test -n "$include_expsyms"; then
-	      tmp_export_symbols=$export_symbols
-	      test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
-	      $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
-	    fi
-
-	    if test -n "$orig_export_symbols"; then
-	      # The given exports_symbols file has to be filtered, so filter it.
-	      func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
-	      # FIXME: $output_objdir/$libname.filter potentially contains lots of
-	      # 's' commands, which not all seds can handle. GNU sed should be fine
-	      # though. Also, the filter scales superlinearly with the number of
-	      # global variables. join(1) would be nice here, but unfortunately
-	      # isn't a blessed tool.
-	      $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
-	      func_append delfiles " $export_symbols $output_objdir/$libname.filter"
-	      export_symbols=$output_objdir/$libname.def
-	      $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
-	    fi
-	  }
-
-	  libobjs=$output
-	  # Restore the value of output.
-	  output=$save_output
-
-	  if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
-	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
-	    test "X$libobjs" = "X " && libobjs=
-	  fi
-	  # Expand the library linking commands again to reset the
-	  # value of $libobjs for piecewise linking.
-
-	  # Do each of the archive commands.
-	  if test yes = "$module" && test -n "$module_cmds"; then
-	    if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
-	      cmds=$module_expsym_cmds
-	    else
-	      cmds=$module_cmds
-	    fi
-	  else
-	    if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
-	      cmds=$archive_expsym_cmds
-	    else
-	      cmds=$archive_cmds
-	    fi
-	  fi
-	fi
-
-	if test -n "$delfiles"; then
-	  # Append the command to remove temporary files to $cmds.
-	  eval cmds=\"\$cmds~\$RM $delfiles\"
-	fi
-
-	# Add any objects from preloaded convenience libraries
-	if test -n "$dlprefiles"; then
-	  gentop=$output_objdir/${outputname}x
-	  func_append generated " $gentop"
-
-	  func_extract_archives $gentop $dlprefiles
-	  func_append libobjs " $func_extract_archives_result"
-	  test "X$libobjs" = "X " && libobjs=
-	fi
-
-	save_ifs=$IFS; IFS='~'
-	for cmd in $cmds; do
-	  IFS=$sp$nl
-	  eval cmd=\"$cmd\"
-	  IFS=$save_ifs
-	  $opt_quiet || {
-	    func_quote_for_expand "$cmd"
-	    eval "func_echo $func_quote_for_expand_result"
-	  }
-	  $opt_dry_run || eval "$cmd" || {
-	    lt_exit=$?
-
-	    # Restore the uninstalled library and exit
-	    if test relink = "$opt_mode"; then
-	      ( cd "$output_objdir" && \
-	        $RM "${realname}T" && \
-		$MV "${realname}U" "$realname" )
-	    fi
-
-	    exit $lt_exit
-	  }
-	done
-	IFS=$save_ifs
-
-	# Restore the uninstalled library and exit
-	if test relink = "$opt_mode"; then
-	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
-
-	  if test -n "$convenience"; then
-	    if test -z "$whole_archive_flag_spec"; then
-	      func_show_eval '${RM}r "$gentop"'
-	    fi
-	  fi
-
-	  exit $EXIT_SUCCESS
-	fi
-
-	# Create links to the real library.
-	for linkname in $linknames; do
-	  if test "$realname" != "$linkname"; then
-	    func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
-	  fi
-	done
-
-	# If -module or -export-dynamic was specified, set the dlname.
-	if test yes = "$module" || test yes = "$export_dynamic"; then
-	  # On all known operating systems, these are identical.
-	  dlname=$soname
-	fi
-      fi
-      ;;
-
-    obj)
-      if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
-	func_warning "'-dlopen' is ignored for objects"
-      fi
-
-      case " $deplibs" in
-      *\ -l* | *\ -L*)
-	func_warning "'-l' and '-L' are ignored for objects" ;;
-      esac
-
-      test -n "$rpath" && \
-	func_warning "'-rpath' is ignored for objects"
-
-      test -n "$xrpath" && \
-	func_warning "'-R' is ignored for objects"
-
-      test -n "$vinfo" && \
-	func_warning "'-version-info' is ignored for objects"
-
-      test -n "$release" && \
-	func_warning "'-release' is ignored for objects"
-
-      case $output in
-      *.lo)
-	test -n "$objs$old_deplibs" && \
-	  func_fatal_error "cannot build library object '$output' from non-libtool objects"
-
-	libobj=$output
-	func_lo2o "$libobj"
-	obj=$func_lo2o_result
-	;;
-      *)
-	libobj=
-	obj=$output
-	;;
-      esac
-
-      # Delete the old objects.
-      $opt_dry_run || $RM $obj $libobj
-
-      # Objects from convenience libraries.  This assumes
-      # single-version convenience libraries.  Whenever we create
-      # different ones for PIC/non-PIC, this we'll have to duplicate
-      # the extraction.
-      reload_conv_objs=
-      gentop=
-      # if reload_cmds runs $LD directly, get rid of -Wl from
-      # whole_archive_flag_spec and hope we can get by with turning comma
-      # into space.
-      case $reload_cmds in
-        *\$LD[\ \$]*) wl= ;;
-      esac
-      if test -n "$convenience"; then
-	if test -n "$whole_archive_flag_spec"; then
-	  eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
-	  test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
-	  reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags
-	else
-	  gentop=$output_objdir/${obj}x
-	  func_append generated " $gentop"
-
-	  func_extract_archives $gentop $convenience
-	  reload_conv_objs="$reload_objs $func_extract_archives_result"
-	fi
-      fi
-
-      # If we're not building shared, we need to use non_pic_objs
-      test yes = "$build_libtool_libs" || libobjs=$non_pic_objects
-
-      # Create the old-style object.
-      reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs
-
-      output=$obj
-      func_execute_cmds "$reload_cmds" 'exit $?'
-
-      # Exit if we aren't doing a library object file.
-      if test -z "$libobj"; then
-	if test -n "$gentop"; then
-	  func_show_eval '${RM}r "$gentop"'
-	fi
-
-	exit $EXIT_SUCCESS
-      fi
-
-      test yes = "$build_libtool_libs" || {
-	if test -n "$gentop"; then
-	  func_show_eval '${RM}r "$gentop"'
-	fi
-
-	# Create an invalid libtool object if no PIC, so that we don't
-	# accidentally link it into a program.
-	# $show "echo timestamp > $libobj"
-	# $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
-	exit $EXIT_SUCCESS
-      }
-
-      if test -n "$pic_flag" || test default != "$pic_mode"; then
-	# Only do commands if we really have different PIC objects.
-	reload_objs="$libobjs $reload_conv_objs"
-	output=$libobj
-	func_execute_cmds "$reload_cmds" 'exit $?'
-      fi
-
-      if test -n "$gentop"; then
-	func_show_eval '${RM}r "$gentop"'
-      fi
-
-      exit $EXIT_SUCCESS
-      ;;
-
-    prog)
-      case $host in
-	*cygwin*) func_stripname '' '.exe' "$output"
-	          output=$func_stripname_result.exe;;
-      esac
-      test -n "$vinfo" && \
-	func_warning "'-version-info' is ignored for programs"
-
-      test -n "$release" && \
-	func_warning "'-release' is ignored for programs"
-
-      $preload \
-	&& test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \
-	&& func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support."
-
-      case $host in
-      *-*-rhapsody* | *-*-darwin1.[012])
-	# On Rhapsody replace the C library is the System framework
-	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
-	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
-	;;
-      esac
-
-      case $host in
-      *-*-darwin*)
-	# Don't allow lazy linking, it breaks C++ global constructors
-	# But is supposedly fixed on 10.4 or later (yay!).
-	if test CXX = "$tagname"; then
-	  case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
-	    10.[0123])
-	      func_append compile_command " $wl-bind_at_load"
-	      func_append finalize_command " $wl-bind_at_load"
-	    ;;
-	  esac
-	fi
-	# Time to change all our "foo.ltframework" stuff back to "-framework foo"
-	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
-	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
-	;;
-      esac
-
-
-      # move library search paths that coincide with paths to not yet
-      # installed libraries to the beginning of the library search list
-      new_libs=
-      for path in $notinst_path; do
-	case " $new_libs " in
-	*" -L$path/$objdir "*) ;;
-	*)
-	  case " $compile_deplibs " in
-	  *" -L$path/$objdir "*)
-	    func_append new_libs " -L$path/$objdir" ;;
-	  esac
-	  ;;
-	esac
-      done
-      for deplib in $compile_deplibs; do
-	case $deplib in
-	-L*)
-	  case " $new_libs " in
-	  *" $deplib "*) ;;
-	  *) func_append new_libs " $deplib" ;;
-	  esac
-	  ;;
-	*) func_append new_libs " $deplib" ;;
-	esac
-      done
-      compile_deplibs=$new_libs
-
-
-      func_append compile_command " $compile_deplibs"
-      func_append finalize_command " $finalize_deplibs"
-
-      if test -n "$rpath$xrpath"; then
-	# If the user specified any rpath flags, then add them.
-	for libdir in $rpath $xrpath; do
-	  # This is the magic to use -rpath.
-	  case "$finalize_rpath " in
-	  *" $libdir "*) ;;
-	  *) func_append finalize_rpath " $libdir" ;;
-	  esac
-	done
-      fi
-
-      # Now hardcode the library paths
-      rpath=
-      hardcode_libdirs=
-      for libdir in $compile_rpath $finalize_rpath; do
-	if test -n "$hardcode_libdir_flag_spec"; then
-	  if test -n "$hardcode_libdir_separator"; then
-	    if test -z "$hardcode_libdirs"; then
-	      hardcode_libdirs=$libdir
-	    else
-	      # Just accumulate the unique libdirs.
-	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
-	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
-		;;
-	      *)
-		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
-		;;
-	      esac
-	    fi
-	  else
-	    eval flag=\"$hardcode_libdir_flag_spec\"
-	    func_append rpath " $flag"
-	  fi
-	elif test -n "$runpath_var"; then
-	  case "$perm_rpath " in
-	  *" $libdir "*) ;;
-	  *) func_append perm_rpath " $libdir" ;;
-	  esac
-	fi
-	case $host in
-	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
-	  testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
-	  case :$dllsearchpath: in
-	  *":$libdir:"*) ;;
-	  ::) dllsearchpath=$libdir;;
-	  *) func_append dllsearchpath ":$libdir";;
-	  esac
-	  case :$dllsearchpath: in
-	  *":$testbindir:"*) ;;
-	  ::) dllsearchpath=$testbindir;;
-	  *) func_append dllsearchpath ":$testbindir";;
-	  esac
-	  ;;
-	esac
-      done
-      # Substitute the hardcoded libdirs into the rpath.
-      if test -n "$hardcode_libdir_separator" &&
-	 test -n "$hardcode_libdirs"; then
-	libdir=$hardcode_libdirs
-	eval rpath=\" $hardcode_libdir_flag_spec\"
-      fi
-      compile_rpath=$rpath
-
-      rpath=
-      hardcode_libdirs=
-      for libdir in $finalize_rpath; do
-	if test -n "$hardcode_libdir_flag_spec"; then
-	  if test -n "$hardcode_libdir_separator"; then
-	    if test -z "$hardcode_libdirs"; then
-	      hardcode_libdirs=$libdir
-	    else
-	      # Just accumulate the unique libdirs.
-	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
-	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
-		;;
-	      *)
-		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
-		;;
-	      esac
-	    fi
-	  else
-	    eval flag=\"$hardcode_libdir_flag_spec\"
-	    func_append rpath " $flag"
-	  fi
-	elif test -n "$runpath_var"; then
-	  case "$finalize_perm_rpath " in
-	  *" $libdir "*) ;;
-	  *) func_append finalize_perm_rpath " $libdir" ;;
-	  esac
-	fi
-      done
-      # Substitute the hardcoded libdirs into the rpath.
-      if test -n "$hardcode_libdir_separator" &&
-	 test -n "$hardcode_libdirs"; then
-	libdir=$hardcode_libdirs
-	eval rpath=\" $hardcode_libdir_flag_spec\"
-      fi
-      finalize_rpath=$rpath
-
-      if test -n "$libobjs" && test yes = "$build_old_libs"; then
-	# Transform all the library objects into standard objects.
-	compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
-	finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
-      fi
-
-      func_generate_dlsyms "$outputname" "@PROGRAM@" false
-
-      # template prelinking step
-      if test -n "$prelink_cmds"; then
-	func_execute_cmds "$prelink_cmds" 'exit $?'
-      fi
-
-      wrappers_required=:
-      case $host in
-      *cegcc* | *mingw32ce*)
-        # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
-        wrappers_required=false
-        ;;
-      *cygwin* | *mingw* )
-        test yes = "$build_libtool_libs" || wrappers_required=false
-        ;;
-      *)
-        if test no = "$need_relink" || test yes != "$build_libtool_libs"; then
-          wrappers_required=false
-        fi
-        ;;
-      esac
-      $wrappers_required || {
-	# Replace the output file specification.
-	compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
-	link_command=$compile_command$compile_rpath
-
-	# We have no uninstalled library dependencies, so finalize right now.
-	exit_status=0
-	func_show_eval "$link_command" 'exit_status=$?'
-
-	if test -n "$postlink_cmds"; then
-	  func_to_tool_file "$output"
-	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
-	  func_execute_cmds "$postlink_cmds" 'exit $?'
-	fi
-
-	# Delete the generated files.
-	if test -f "$output_objdir/${outputname}S.$objext"; then
-	  func_show_eval '$RM "$output_objdir/${outputname}S.$objext"'
-	fi
-
-	exit $exit_status
-      }
-
-      if test -n "$compile_shlibpath$finalize_shlibpath"; then
-	compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
-      fi
-      if test -n "$finalize_shlibpath"; then
-	finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
-      fi
-
-      compile_var=
-      finalize_var=
-      if test -n "$runpath_var"; then
-	if test -n "$perm_rpath"; then
-	  # We should set the runpath_var.
-	  rpath=
-	  for dir in $perm_rpath; do
-	    func_append rpath "$dir:"
-	  done
-	  compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
-	fi
-	if test -n "$finalize_perm_rpath"; then
-	  # We should set the runpath_var.
-	  rpath=
-	  for dir in $finalize_perm_rpath; do
-	    func_append rpath "$dir:"
-	  done
-	  finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
-	fi
-      fi
-
-      if test yes = "$no_install"; then
-	# We don't need to create a wrapper script.
-	link_command=$compile_var$compile_command$compile_rpath
-	# Replace the output file specification.
-	link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
-	# Delete the old output file.
-	$opt_dry_run || $RM $output
-	# Link the executable and exit
-	func_show_eval "$link_command" 'exit $?'
-
-	if test -n "$postlink_cmds"; then
-	  func_to_tool_file "$output"
-	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
-	  func_execute_cmds "$postlink_cmds" 'exit $?'
-	fi
-
-	exit $EXIT_SUCCESS
-      fi
-
-      case $hardcode_action,$fast_install in
-        relink,*)
-	  # Fast installation is not supported
-	  link_command=$compile_var$compile_command$compile_rpath
-	  relink_command=$finalize_var$finalize_command$finalize_rpath
-
-	  func_warning "this platform does not like uninstalled shared libraries"
-	  func_warning "'$output' will be relinked during installation"
-	  ;;
-        *,yes)
-	  link_command=$finalize_var$compile_command$finalize_rpath
-	  relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
-          ;;
-	*,no)
-	  link_command=$compile_var$compile_command$compile_rpath
-	  relink_command=$finalize_var$finalize_command$finalize_rpath
-          ;;
-	*,needless)
-	  link_command=$finalize_var$compile_command$finalize_rpath
-	  relink_command=
-          ;;
-      esac
-
-      # Replace the output file specification.
-      link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
-
-      # Delete the old output files.
-      $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
-
-      func_show_eval "$link_command" 'exit $?'
-
-      if test -n "$postlink_cmds"; then
-	func_to_tool_file "$output_objdir/$outputname"
-	postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
-	func_execute_cmds "$postlink_cmds" 'exit $?'
-      fi
-
-      # Now create the wrapper script.
-      func_verbose "creating $output"
-
-      # Quote the relink command for shipping.
-      if test -n "$relink_command"; then
-	# Preserve any variables that may affect compiler behavior
-	for var in $variables_saved_for_relink; do
-	  if eval test -z \"\${$var+set}\"; then
-	    relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
-	  elif eval var_value=\$$var; test -z "$var_value"; then
-	    relink_command="$var=; export $var; $relink_command"
-	  else
-	    func_quote_for_eval "$var_value"
-	    relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
-	  fi
-	done
-	relink_command="(cd `pwd`; $relink_command)"
-	relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
-      fi
-
-      # Only actually do things if not in dry run mode.
-      $opt_dry_run || {
-	# win32 will think the script is a binary if it has
-	# a .exe suffix, so we strip it off here.
-	case $output in
-	  *.exe) func_stripname '' '.exe' "$output"
-	         output=$func_stripname_result ;;
-	esac
-	# test for cygwin because mv fails w/o .exe extensions
-	case $host in
-	  *cygwin*)
-	    exeext=.exe
-	    func_stripname '' '.exe' "$outputname"
-	    outputname=$func_stripname_result ;;
-	  *) exeext= ;;
-	esac
-	case $host in
-	  *cygwin* | *mingw* )
-	    func_dirname_and_basename "$output" "" "."
-	    output_name=$func_basename_result
-	    output_path=$func_dirname_result
-	    cwrappersource=$output_path/$objdir/lt-$output_name.c
-	    cwrapper=$output_path/$output_name.exe
-	    $RM $cwrappersource $cwrapper
-	    trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
-
-	    func_emit_cwrapperexe_src > $cwrappersource
-
-	    # The wrapper executable is built using the $host compiler,
-	    # because it contains $host paths and files. If cross-
-	    # compiling, it, like the target executable, must be
-	    # executed on the $host or under an emulation environment.
-	    $opt_dry_run || {
-	      $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
-	      $STRIP $cwrapper
-	    }
-
-	    # Now, create the wrapper script for func_source use:
-	    func_ltwrapper_scriptname $cwrapper
-	    $RM $func_ltwrapper_scriptname_result
-	    trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
-	    $opt_dry_run || {
-	      # note: this script will not be executed, so do not chmod.
-	      if test "x$build" = "x$host"; then
-		$cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
-	      else
-		func_emit_wrapper no > $func_ltwrapper_scriptname_result
-	      fi
-	    }
-	  ;;
-	  * )
-	    $RM $output
-	    trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
-
-	    func_emit_wrapper no > $output
-	    chmod +x $output
-	  ;;
-	esac
-      }
-      exit $EXIT_SUCCESS
-      ;;
-    esac
-
-    # See if we need to build an old-fashioned archive.
-    for oldlib in $oldlibs; do
-
-      case $build_libtool_libs in
-        convenience)
-	  oldobjs="$libobjs_save $symfileobj"
-	  addlibs=$convenience
-	  build_libtool_libs=no
-	  ;;
-	module)
-	  oldobjs=$libobjs_save
-	  addlibs=$old_convenience
-	  build_libtool_libs=no
-          ;;
-	*)
-	  oldobjs="$old_deplibs $non_pic_objects"
-	  $preload && test -f "$symfileobj" \
-	    && func_append oldobjs " $symfileobj"
-	  addlibs=$old_convenience
-	  ;;
-      esac
-
-      if test -n "$addlibs"; then
-	gentop=$output_objdir/${outputname}x
-	func_append generated " $gentop"
-
-	func_extract_archives $gentop $addlibs
-	func_append oldobjs " $func_extract_archives_result"
-      fi
-
-      # Do each command in the archive commands.
-      if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then
-	cmds=$old_archive_from_new_cmds
-      else
-
-	# Add any objects from preloaded convenience libraries
-	if test -n "$dlprefiles"; then
-	  gentop=$output_objdir/${outputname}x
-	  func_append generated " $gentop"
-
-	  func_extract_archives $gentop $dlprefiles
-	  func_append oldobjs " $func_extract_archives_result"
-	fi
-
-	# POSIX demands no paths to be encoded in archives.  We have
-	# to avoid creating archives with duplicate basenames if we
-	# might have to extract them afterwards, e.g., when creating a
-	# static archive out of a convenience library, or when linking
-	# the entirety of a libtool archive into another (currently
-	# not supported by libtool).
-	if (for obj in $oldobjs
-	    do
-	      func_basename "$obj"
-	      $ECHO "$func_basename_result"
-	    done | sort | sort -uc >/dev/null 2>&1); then
-	  :
-	else
-	  echo "copying selected object files to avoid basename conflicts..."
-	  gentop=$output_objdir/${outputname}x
-	  func_append generated " $gentop"
-	  func_mkdir_p "$gentop"
-	  save_oldobjs=$oldobjs
-	  oldobjs=
-	  counter=1
-	  for obj in $save_oldobjs
-	  do
-	    func_basename "$obj"
-	    objbase=$func_basename_result
-	    case " $oldobjs " in
-	    " ") oldobjs=$obj ;;
-	    *[\ /]"$objbase "*)
-	      while :; do
-		# Make sure we don't pick an alternate name that also
-		# overlaps.
-		newobj=lt$counter-$objbase
-		func_arith $counter + 1
-		counter=$func_arith_result
-		case " $oldobjs " in
-		*[\ /]"$newobj "*) ;;
-		*) if test ! -f "$gentop/$newobj"; then break; fi ;;
-		esac
-	      done
-	      func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
-	      func_append oldobjs " $gentop/$newobj"
-	      ;;
-	    *) func_append oldobjs " $obj" ;;
-	    esac
-	  done
-	fi
-	func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
-	tool_oldlib=$func_to_tool_file_result
-	eval cmds=\"$old_archive_cmds\"
-
-	func_len " $cmds"
-	len=$func_len_result
-	if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
-	  cmds=$old_archive_cmds
-	elif test -n "$archiver_list_spec"; then
-	  func_verbose "using command file archive linking..."
-	  for obj in $oldobjs
-	  do
-	    func_to_tool_file "$obj"
-	    $ECHO "$func_to_tool_file_result"
-	  done > $output_objdir/$libname.libcmd
-	  func_to_tool_file "$output_objdir/$libname.libcmd"
-	  oldobjs=" $archiver_list_spec$func_to_tool_file_result"
-	  cmds=$old_archive_cmds
-	else
-	  # the command line is too long to link in one step, link in parts
-	  func_verbose "using piecewise archive linking..."
-	  save_RANLIB=$RANLIB
-	  RANLIB=:
-	  objlist=
-	  concat_cmds=
-	  save_oldobjs=$oldobjs
-	  oldobjs=
-	  # Is there a better way of finding the last object in the list?
-	  for obj in $save_oldobjs
-	  do
-	    last_oldobj=$obj
-	  done
-	  eval test_cmds=\"$old_archive_cmds\"
-	  func_len " $test_cmds"
-	  len0=$func_len_result
-	  len=$len0
-	  for obj in $save_oldobjs
-	  do
-	    func_len " $obj"
-	    func_arith $len + $func_len_result
-	    len=$func_arith_result
-	    func_append objlist " $obj"
-	    if test "$len" -lt "$max_cmd_len"; then
-	      :
-	    else
-	      # the above command should be used before it gets too long
-	      oldobjs=$objlist
-	      if test "$obj" = "$last_oldobj"; then
-		RANLIB=$save_RANLIB
-	      fi
-	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
-	      eval concat_cmds=\"\$concat_cmds$old_archive_cmds\"
-	      objlist=
-	      len=$len0
-	    fi
-	  done
-	  RANLIB=$save_RANLIB
-	  oldobjs=$objlist
-	  if test -z "$oldobjs"; then
-	    eval cmds=\"\$concat_cmds\"
-	  else
-	    eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
-	  fi
-	fi
-      fi
-      func_execute_cmds "$cmds" 'exit $?'
-    done
-
-    test -n "$generated" && \
-      func_show_eval "${RM}r$generated"
-
-    # Now create the libtool archive.
-    case $output in
-    *.la)
-      old_library=
-      test yes = "$build_old_libs" && old_library=$libname.$libext
-      func_verbose "creating $output"
-
-      # Preserve any variables that may affect compiler behavior
-      for var in $variables_saved_for_relink; do
-	if eval test -z \"\${$var+set}\"; then
-	  relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
-	elif eval var_value=\$$var; test -z "$var_value"; then
-	  relink_command="$var=; export $var; $relink_command"
-	else
-	  func_quote_for_eval "$var_value"
-	  relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
-	fi
-      done
-      # Quote the link command for shipping.
-      relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
-      relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
-      if test yes = "$hardcode_automatic"; then
-	relink_command=
-      fi
-
-      # Only create the output if not a dry run.
-      $opt_dry_run || {
-	for installed in no yes; do
-	  if test yes = "$installed"; then
-	    if test -z "$install_libdir"; then
-	      break
-	    fi
-	    output=$output_objdir/${outputname}i
-	    # Replace all uninstalled libtool libraries with the installed ones
-	    newdependency_libs=
-	    for deplib in $dependency_libs; do
-	      case $deplib in
-	      *.la)
-		func_basename "$deplib"
-		name=$func_basename_result
-		func_resolve_sysroot "$deplib"
-		eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
-		test -z "$libdir" && \
-		  func_fatal_error "'$deplib' is not a valid libtool archive"
-		func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
-		;;
-	      -L*)
-		func_stripname -L '' "$deplib"
-		func_replace_sysroot "$func_stripname_result"
-		func_append newdependency_libs " -L$func_replace_sysroot_result"
-		;;
-	      -R*)
-		func_stripname -R '' "$deplib"
-		func_replace_sysroot "$func_stripname_result"
-		func_append newdependency_libs " -R$func_replace_sysroot_result"
-		;;
-	      *) func_append newdependency_libs " $deplib" ;;
-	      esac
-	    done
-	    dependency_libs=$newdependency_libs
-	    newdlfiles=
-
-	    for lib in $dlfiles; do
-	      case $lib in
-	      *.la)
-	        func_basename "$lib"
-		name=$func_basename_result
-		eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
-		test -z "$libdir" && \
-		  func_fatal_error "'$lib' is not a valid libtool archive"
-		func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
-		;;
-	      *) func_append newdlfiles " $lib" ;;
-	      esac
-	    done
-	    dlfiles=$newdlfiles
-	    newdlprefiles=
-	    for lib in $dlprefiles; do
-	      case $lib in
-	      *.la)
-		# Only pass preopened files to the pseudo-archive (for
-		# eventual linking with the app. that links it) if we
-		# didn't already link the preopened objects directly into
-		# the library:
-		func_basename "$lib"
-		name=$func_basename_result
-		eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
-		test -z "$libdir" && \
-		  func_fatal_error "'$lib' is not a valid libtool archive"
-		func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
-		;;
-	      esac
-	    done
-	    dlprefiles=$newdlprefiles
-	  else
-	    newdlfiles=
-	    for lib in $dlfiles; do
-	      case $lib in
-		[\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
-		*) abs=`pwd`"/$lib" ;;
-	      esac
-	      func_append newdlfiles " $abs"
-	    done
-	    dlfiles=$newdlfiles
-	    newdlprefiles=
-	    for lib in $dlprefiles; do
-	      case $lib in
-		[\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
-		*) abs=`pwd`"/$lib" ;;
-	      esac
-	      func_append newdlprefiles " $abs"
-	    done
-	    dlprefiles=$newdlprefiles
-	  fi
-	  $RM $output
-	  # place dlname in correct position for cygwin
-	  # In fact, it would be nice if we could use this code for all target
-	  # systems that can't hard-code library paths into their executables
-	  # and that have no shared library path variable independent of PATH,
-	  # but it turns out we can't easily determine that from inspecting
-	  # libtool variables, so we have to hard-code the OSs to which it
-	  # applies here; at the moment, that means platforms that use the PE
-	  # object format with DLL files.  See the long comment at the top of
-	  # tests/bindir.at for full details.
-	  tdlname=$dlname
-	  case $host,$output,$installed,$module,$dlname in
-	    *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
-	      # If a -bindir argument was supplied, place the dll there.
-	      if test -n "$bindir"; then
-		func_relative_path "$install_libdir" "$bindir"
-		tdlname=$func_relative_path_result/$dlname
-	      else
-		# Otherwise fall back on heuristic.
-		tdlname=../bin/$dlname
-	      fi
-	      ;;
-	  esac
-	  $ECHO > $output "\
-# $outputname - a libtool library file
-# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
-#
-# Please DO NOT delete this file!
-# It is necessary for linking the library.
-
-# The name that we can dlopen(3).
-dlname='$tdlname'
-
-# Names of this library.
-library_names='$library_names'
-
-# The name of the static archive.
-old_library='$old_library'
-
-# Linker flags that cannot go in dependency_libs.
-inherited_linker_flags='$new_inherited_linker_flags'
-
-# Libraries that this one depends upon.
-dependency_libs='$dependency_libs'
-
-# Names of additional weak libraries provided by this library
-weak_library_names='$weak_libs'
-
-# Version information for $libname.
-current=$current
-age=$age
-revision=$revision
-
-# Is this an already installed library?
-installed=$installed
-
-# Should we warn about portability when linking against -modules?
-shouldnotlink=$module
-
-# Files to dlopen/dlpreopen
-dlopen='$dlfiles'
-dlpreopen='$dlprefiles'
-
-# Directory that this library needs to be installed in:
-libdir='$install_libdir'"
-	  if test no,yes = "$installed,$need_relink"; then
-	    $ECHO >> $output "\
-relink_command=\"$relink_command\""
-	  fi
-	done
-      }
-
-      # Do a symbolic link so that the libtool archive can be found in
-      # LD_LIBRARY_PATH before the program is installed.
-      func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
-      ;;
-    esac
-    exit $EXIT_SUCCESS
-}
-
-if test link = "$opt_mode" || test relink = "$opt_mode"; then
-  func_mode_link ${1+"$@"}
-fi
-
-
-# func_mode_uninstall arg...
-func_mode_uninstall ()
-{
-    $debug_cmd
-
-    RM=$nonopt
-    files=
-    rmforce=false
-    exit_status=0
-
-    # This variable tells wrapper scripts just to set variables rather
-    # than running their programs.
-    libtool_install_magic=$magic
-
-    for arg
-    do
-      case $arg in
-      -f) func_append RM " $arg"; rmforce=: ;;
-      -*) func_append RM " $arg" ;;
-      *) func_append files " $arg" ;;
-      esac
-    done
-
-    test -z "$RM" && \
-      func_fatal_help "you must specify an RM program"
-
-    rmdirs=
-
-    for file in $files; do
-      func_dirname "$file" "" "."
-      dir=$func_dirname_result
-      if test . = "$dir"; then
-	odir=$objdir
-      else
-	odir=$dir/$objdir
-      fi
-      func_basename "$file"
-      name=$func_basename_result
-      test uninstall = "$opt_mode" && odir=$dir
-
-      # Remember odir for removal later, being careful to avoid duplicates
-      if test clean = "$opt_mode"; then
-	case " $rmdirs " in
-	  *" $odir "*) ;;
-	  *) func_append rmdirs " $odir" ;;
-	esac
-      fi
-
-      # Don't error if the file doesn't exist and rm -f was used.
-      if { test -L "$file"; } >/dev/null 2>&1 ||
-	 { test -h "$file"; } >/dev/null 2>&1 ||
-	 test -f "$file"; then
-	:
-      elif test -d "$file"; then
-	exit_status=1
-	continue
-      elif $rmforce; then
-	continue
-      fi
-
-      rmfiles=$file
-
-      case $name in
-      *.la)
-	# Possibly a libtool archive, so verify it.
-	if func_lalib_p "$file"; then
-	  func_source $dir/$name
-
-	  # Delete the libtool libraries and symlinks.
-	  for n in $library_names; do
-	    func_append rmfiles " $odir/$n"
-	  done
-	  test -n "$old_library" && func_append rmfiles " $odir/$old_library"
-
-	  case $opt_mode in
-	  clean)
-	    case " $library_names " in
-	    *" $dlname "*) ;;
-	    *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
-	    esac
-	    test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
-	    ;;
-	  uninstall)
-	    if test -n "$library_names"; then
-	      # Do each command in the postuninstall commands.
-	      func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1'
-	    fi
-
-	    if test -n "$old_library"; then
-	      # Do each command in the old_postuninstall commands.
-	      func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1'
-	    fi
-	    # FIXME: should reinstall the best remaining shared library.
-	    ;;
-	  esac
-	fi
-	;;
-
-      *.lo)
-	# Possibly a libtool object, so verify it.
-	if func_lalib_p "$file"; then
-
-	  # Read the .lo file
-	  func_source $dir/$name
-
-	  # Add PIC object to the list of files to remove.
-	  if test -n "$pic_object" && test none != "$pic_object"; then
-	    func_append rmfiles " $dir/$pic_object"
-	  fi
-
-	  # Add non-PIC object to the list of files to remove.
-	  if test -n "$non_pic_object" && test none != "$non_pic_object"; then
-	    func_append rmfiles " $dir/$non_pic_object"
-	  fi
-	fi
-	;;
-
-      *)
-	if test clean = "$opt_mode"; then
-	  noexename=$name
-	  case $file in
-	  *.exe)
-	    func_stripname '' '.exe' "$file"
-	    file=$func_stripname_result
-	    func_stripname '' '.exe' "$name"
-	    noexename=$func_stripname_result
-	    # $file with .exe has already been added to rmfiles,
-	    # add $file without .exe
-	    func_append rmfiles " $file"
-	    ;;
-	  esac
-	  # Do a test to see if this is a libtool program.
-	  if func_ltwrapper_p "$file"; then
-	    if func_ltwrapper_executable_p "$file"; then
-	      func_ltwrapper_scriptname "$file"
-	      relink_command=
-	      func_source $func_ltwrapper_scriptname_result
-	      func_append rmfiles " $func_ltwrapper_scriptname_result"
-	    else
-	      relink_command=
-	      func_source $dir/$noexename
-	    fi
-
-	    # note $name still contains .exe if it was in $file originally
-	    # as does the version of $file that was added into $rmfiles
-	    func_append rmfiles " $odir/$name $odir/${name}S.$objext"
-	    if test yes = "$fast_install" && test -n "$relink_command"; then
-	      func_append rmfiles " $odir/lt-$name"
-	    fi
-	    if test "X$noexename" != "X$name"; then
-	      func_append rmfiles " $odir/lt-$noexename.c"
-	    fi
-	  fi
-	fi
-	;;
-      esac
-      func_show_eval "$RM $rmfiles" 'exit_status=1'
-    done
-
-    # Try to remove the $objdir's in the directories where we deleted files
-    for dir in $rmdirs; do
-      if test -d "$dir"; then
-	func_show_eval "rmdir $dir >/dev/null 2>&1"
-      fi
-    done
-
-    exit $exit_status
-}
-
-if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then
-  func_mode_uninstall ${1+"$@"}
-fi
-
-test -z "$opt_mode" && {
-  help=$generic_help
-  func_fatal_help "you must specify a MODE"
-}
-
-test -z "$exec_cmd" && \
-  func_fatal_help "invalid operation mode '$opt_mode'"
-
-if test -n "$exec_cmd"; then
-  eval exec "$exec_cmd"
-  exit $EXIT_FAILURE
-fi
-
-exit $exit_status
-
-
-# The TAGs below are defined such that we never get into a situation
-# where we disable both kinds of libraries.  Given conflicting
-# choices, we go for a static library, that is the most portable,
-# since we can't tell whether shared libraries were disabled because
-# the user asked for that or because the platform doesn't support
-# them.  This is particularly important on AIX, because we don't
-# support having both static and shared libraries enabled at the same
-# time on that platform, so we default to a shared-only configuration.
-# If a disable-shared tag is given, we'll fallback to a static-only
-# configuration.  But we'll never go from static-only to shared-only.
-
-# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
-build_libtool_libs=no
-build_old_libs=yes
-# ### END LIBTOOL TAG CONFIG: disable-shared
-
-# ### BEGIN LIBTOOL TAG CONFIG: disable-static
-build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
-# ### END LIBTOOL TAG CONFIG: disable-static
-
-# Local Variables:
-# mode:shell-script
-# sh-indentation:2
-# End:
diff --git a/libs/libpng-src/mips/.dirstamp b/libs/libpng-src/mips/.dirstamp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/libs/libpng-src/png.h b/libs/libpng-src/png.h
index b12174ba359b1908d0fc3c59d34bc7b9c0ae9100..a01077bf0bd276504b5f90c52d1c7ed8d759d425 100644
--- a/libs/libpng-src/png.h
+++ b/libs/libpng-src/png.h
@@ -327,7 +327,7 @@
 /* If pnglibconf.h is missing, you can
  * copy scripts/pnglibconf.h.prebuilt to pnglibconf.h
  */
-#   include "pnglibconf.h"
+#   include "projects/pnglibconf.h"
 #endif
 
 #define PNG_APNG_SUPPORTED
@@ -336,7 +336,7 @@
 
 #ifndef PNG_VERSION_INFO_ONLY
 /* Machine specific configuration. */
-#  include "pngconf.h"
+#  include "projects/pngconf.h"
 #endif
 
 /*
diff --git a/libs/libpng-src/pngpriv.h b/libs/libpng-src/pngpriv.h
index 57e1bf989ba346acc742a93e5360846ff7aa71e0..601ff00d34e477d9f182e6831681d7f45038e2ab 100644
--- a/libs/libpng-src/pngpriv.h
+++ b/libs/libpng-src/pngpriv.h
@@ -69,7 +69,7 @@
  * before png.h is included, but load the configuration now if it is available.
  */
 #ifndef PNGLCONF_H
-#  include "pnglibconf.h"
+#  include "projects/pnglibconf.h"
 #endif
 
 /* Local renames may change non-exported API functions from png.h */
diff --git a/libs/libpng-src/powerpc/.dirstamp b/libs/libpng-src/powerpc/.dirstamp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/libs/libpng-src/projects/libpng32.a b/libs/libpng-src/projects/libpng32.a
index fdd5702b58a313c202a5873924022afc6c1cbbb8..1d965aa0bb67b6e6033045ca476fb6ac4f5af059 100644
Binary files a/libs/libpng-src/projects/libpng32.a and b/libs/libpng-src/projects/libpng32.a differ
diff --git a/libs/libpng-src/pngconf.h b/libs/libpng-src/projects/pngconf.h
similarity index 100%
rename from libs/libpng-src/pngconf.h
rename to libs/libpng-src/projects/pngconf.h
diff --git a/libs/libpng-src/pnglibconf.h b/libs/libpng-src/projects/pnglibconf.h
similarity index 100%
rename from libs/libpng-src/pnglibconf.h
rename to libs/libpng-src/projects/pnglibconf.h
diff --git a/libs/libpng-src/projects/visualc10/.gitignore b/libs/libpng-src/projects/visualc10/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e1bec81fcb4d7d25a7b176b6585f24a752bd0e56
--- /dev/null
+++ b/libs/libpng-src/projects/visualc10/.gitignore
@@ -0,0 +1,5 @@
+/Win32
+/x64
+/libpng.vcproj.*.*.user
+/ARM
+/ARM64
diff --git a/libs/libpng-src/projects/visualc10/libpng.vcxproj b/libs/libpng-src/projects/visualc10/libpng.vcxproj
new file mode 100644
index 0000000000000000000000000000000000000000..eaa3d4ffb9d169a4127d882bc69e053e9fd81349
--- /dev/null
+++ b/libs/libpng-src/projects/visualc10/libpng.vcxproj
@@ -0,0 +1,730 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|ARM">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|ARM64">
+      <Configuration>Debug</Configuration>
+      <Platform>ARM64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM">
+      <Configuration>Release</Configuration>
+      <Platform>ARM</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|ARM64">
+      <Configuration>Release</Configuration>
+      <Platform>ARM64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}</ProjectGuid>
+    <RootNamespace>libpng</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v141</PlatformToolset>
+    <WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v141</PlatformToolset>
+    <WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v141</PlatformToolset>
+    <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v140</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
+    <ConfigurationType>StaticLibrary</ConfigurationType>
+    <UseOfMfc>false</UseOfMfc>
+    <PlatformToolset>v141</PlatformToolset>
+    <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+    <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC60.props" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+    <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(ProjectDir)$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <StringPooling>true</StringPooling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <CompileAs>CompileAsC</CompileAs>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <StringPooling>true</StringPooling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <CompileAs>CompileAsC</CompileAs>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineARM</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Midl>
+      <TargetEnvironment>X64</TargetEnvironment>
+    </Midl>
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <StringPooling>true</StringPooling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <CompileAs>CompileAsC</CompileAs>
+      <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
+    <Midl />
+    <ClCompile>
+      <Optimization>MaxSpeed</Optimization>
+      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <StringPooling>true</StringPooling>
+      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <CompileAs>CompileAsC</CompileAs>
+      <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+      <AdditionalIncludeDirectories>..\..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineARM64</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+      <CompileAs>CompileAsC</CompileAs>
+      <MultiProcessorCompilation>false</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineX86</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <CompileAs>CompileAsC</CompileAs>
+      <MultiProcessorCompilation>false</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineARM</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Midl>
+      <TargetEnvironment>X64</TargetEnvironment>
+    </Midl>
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <MinimalRebuild>true</MinimalRebuild>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <CompileAs>CompileAsC</CompileAs>
+      <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>false</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineX64</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
+    <Midl />
+    <ClCompile>
+      <Optimization>Disabled</Optimization>
+      <AdditionalIncludeDirectories>..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+      <AssemblerListingLocation>$(ProjectDir)$(Platform)\$(Configuration)\</AssemblerListingLocation>
+      <ObjectFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ObjectFileName>
+      <ProgramDataBaseFileName>$(ProjectDir)$(Platform)\$(Configuration)\</ProgramDataBaseFileName>
+      <WarningLevel>Level3</WarningLevel>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <CompileAs>CompileAsC</CompileAs>
+      <DisableSpecificWarnings>4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>false</MultiProcessorCompilation>
+    </ClCompile>
+    <ResourceCompile>
+      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <Culture>0x0409</Culture>
+    </ResourceCompile>
+    <Lib>
+      <OutputFile>$(ProjectDir)$(Platform)\$(Configuration)\libpng.lib</OutputFile>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <TargetMachine>MachineARM64</TargetMachine>
+    </Lib>
+    <Bscmake>
+      <SuppressStartupBanner>true</SuppressStartupBanner>
+      <OutputFile>$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc</OutputFile>
+    </Bscmake>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\png.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngerror.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngget.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngmem.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngpread.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngread.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngrio.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngrtran.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngrutil.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngset.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngtrans.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngwio.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngwrite.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngwtran.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <ClCompile Include="..\..\pngwutil.c">
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <CustomBuildStep Include="..\..\scripts\pngw32.def">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
+    </CustomBuildStep>
+    <None Include="README.txt" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\png.h" />
+    <ClInclude Include="..\..\pngconf.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\..\scripts\pngw32.rc">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</ExcludedFromBuild>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\..\zlib\projects\visualc10\zlib.vcxproj">
+      <Project>{73a5729c-7323-41d4-ab48-8a03c9f81603}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/libs/libpng-src/stamp-h1 b/libs/libpng-src/stamp-h1
deleted file mode 100644
index 4547fe1b5efa99ebbf20e1fa55fdbd528abd3a97..0000000000000000000000000000000000000000
--- a/libs/libpng-src/stamp-h1
+++ /dev/null
@@ -1 +0,0 @@
-timestamp for config.h
diff --git a/libs/zlib/projects/visualc10/zlib.vcxproj b/libs/zlib/projects/visualc10/zlib.vcxproj
index a7055ddb8156e3bf2af4c67e9ea069b3135e65a4..de7b2aaab38b3dd9be4224e43cc2a695e3a1dc36 100644
--- a/libs/zlib/projects/visualc10/zlib.vcxproj
+++ b/libs/zlib/projects/visualc10/zlib.vcxproj
@@ -143,7 +143,7 @@
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
       <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <MinimalRebuild>true</MinimalRebuild>
       <BasicRuntimeChecks>Default</BasicRuntimeChecks>
       <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@@ -267,7 +267,7 @@
     <ClCompile>
       <Optimization>MaxSpeed</Optimization>
       <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
-      <PreprocessorDefinitions>WIN32;NDEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <StringPooling>true</StringPooling>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <FunctionLevelLinking>true</FunctionLevelLinking>
@@ -570,6 +570,8 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </MASM>
     <MASM Include="..\..\contrib\masmx86\match686.asm">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -578,6 +580,8 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </MASM>
     <MASM Include="..\..\contrib\masmx64\gvmat64.asm">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
diff --git a/libs/zlib/zconf.h b/libs/zlib/zconf.h
index 5e1d68a004e9744cb35f9d5a2fe94fd4dbcb7f76..1f7cfed46a716dc96813126ebca4f2342a0efa57 100644
--- a/libs/zlib/zconf.h
+++ b/libs/zlib/zconf.h
@@ -472,7 +472,9 @@ typedef uLong FAR uLongf;
 #endif
 #ifndef Z_SOLO
 #  if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
+#ifndef _MSC_VER
 #    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
+#endif
 #    ifdef VMS
 #      include <unixio.h>       /* for off_t */
 #    endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 97feae18bbda248b274a9e3c0b1797b97260b790..e8c9c31820c3ba1fcf46691cff5f4476df98220c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -518,10 +518,6 @@ add_definitions(-DCMAKECONFIG)
 
 add_subdirectory(sdl)
 
-if(${CMAKE_SYSTEM} MATCHES Windows)
-	add_subdirectory(win32)
-endif()
-
-if(NOT ${SRB2_SDL2_AVAILABLE} AND NOT ${SRB2_WIN32_AVAILABLE})
+if(NOT ${SRB2_SDL2_AVAILABLE})
 	message(FATAL_ERROR "There are no targets available to build an SRB2 executable. :(")
 endif()
diff --git a/src/Makefile b/src/Makefile
index 7c9ce8fd26614775c64d391591ca88f0c225cb8c..c30c236de33ce104c592d319c2b03f5d7daa3df7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,7 +2,7 @@
 #     GNU Make makefile for SRB2
 #############################################################################
 # Copyright (C) 1998-2000 by DooM Legacy Team.
-# Copyright (C) 2003-2018 by Sonic Team Junior.
+# Copyright (C) 2003-2019 by Sonic Team Junior.
 #
 # This program is free software distributed under the
 # terms of the GNU General Public License, version 2.
diff --git a/src/am_map.c b/src/am_map.c
index a184ea768365ee6f1ab701b01996c6f66c85bf3c..7dfbf4ba43d8e38109271cf35310d2eca054ba44 100644
--- a/src/am_map.c
+++ b/src/am_map.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/am_map.h b/src/am_map.h
index d59532819e9e1a2c14bb1455d36d4ef789ee1a6a..462bb3d6d7b0adf15140f55705fbb76642725089 100644
--- a/src/am_map.h
+++ b/src/am_map.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/android/i_system.c b/src/android/i_system.c
index 58fca7c1943448ff81ede9a67854f3791c93bc00..752e9f6c6bb4138caaf17004cbcc0fcaa6b66cee 100644
--- a/src/android/i_system.c
+++ b/src/android/i_system.c
@@ -245,6 +245,8 @@ void I_GetJoystick2Events(void){}
 
 void I_GetMouseEvents(void){}
 
+void I_UpdateMouseGrab(void){}
+
 char *I_GetEnv(const char *name)
 {
   LOGW("I_GetEnv() called?!");
diff --git a/src/android/i_video.c b/src/android/i_video.c
index 2d0151f5e27cd906e4856109fd8529772c704955..b8bb4fefbf408970af9c23ace95f5217548dc43e 100644
--- a/src/android/i_video.c
+++ b/src/android/i_video.c
@@ -19,6 +19,7 @@ boolean allow_fullscreen = false;
 consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 void I_StartupGraphics(void){}
+void I_StartupHardwareGraphics(void){}
 
 void I_ShutdownGraphics(void){}
 
@@ -51,6 +52,11 @@ INT32 VID_SetMode(INT32 modenum)
   return 0;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 const char *VID_GetModeName(INT32 modenum)
 {
   return "A320x240";
diff --git a/src/asm_defs.inc b/src/asm_defs.inc
index e494a676e7f62aac28f12f616b9dfed2fc4bdc06..82149f70e517f1d742be507ed4c88a0e5f92a127 100644
--- a/src/asm_defs.inc
+++ b/src/asm_defs.inc
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/b_bot.c b/src/b_bot.c
index 709a280b57a16a7cc4d7d40b434ed3f7caa2cd31..51dc4d2f21c9f4db04a4304c7604290b06e622a4 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2007-2016 by John "JTE" Muniz.
-// Copyright (C) 2011-2018 by Sonic Team Junior.
+// Copyright (C) 2011-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -43,7 +43,7 @@ static inline void B_ResetAI(void)
 	thinkfly = false;
 }
 
-static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
+static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 {
 	boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false;
 
@@ -56,7 +56,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 
 	fixed_t dist = P_AproxDistance(sonic->x - tails->x, sonic->y - tails->y);
 	fixed_t zdist = flip * (sonic->z - tails->z);
-	angle_t ang = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y);
+	angle_t ang = sonic->angle;
 	fixed_t pmom = P_AproxDistance(sonic->momx, sonic->momy);
 	fixed_t bmom = P_AproxDistance(tails->momx, tails->momy);
 	fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter "panic" state
@@ -65,6 +65,10 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 	fixed_t comfortheight = 96 * scale;
 	fixed_t touchdist = 24 * scale;
 	boolean stalled = (bmom < scale >> 1) && dist > followthres; // Helps to see if the AI is having trouble catching up
+	boolean samepos = (sonic->x == tails->x && sonic->y == tails->y);
+
+	if (!samepos)
+		ang = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y);
 
 	// We can't follow Sonic if he's not around!
 	if (!sonic || sonic->health <= 0)
@@ -135,7 +139,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 	// ********
 	// FLY MODE
 	// spinmode check
-	if (spinmode)
+	if (spinmode || player->exiting)
 		thinkfly = false;
 	else
 	{
@@ -154,7 +158,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 		// Check positioning
 		// Thinker for co-op flight
 		if (!(water || pmom || bmom)
-			&& (dist < touchdist)
+			&& (dist < touchdist && !samepos)
 			&& !(pcmd->forwardmove || pcmd->sidemove || player->dashspeed)
 			&& P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails)
 			&& !(player->pflags & PF_STASIS)
@@ -163,6 +167,12 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 		else
 			thinkfly = false;
 
+		// Set carried state
+		if (player->powers[pw_carry] == CR_PLAYER && sonic->tracer == tails)
+		{
+			flymode = 2;
+		}
+
 		// Ready for takeoff
 		if (flymode == 1)
 		{
@@ -171,16 +181,10 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 				spin = true;
 			else if (!jump_last)
 				jump = true;
-			
+
 			// Abort if the player moves away or spins
 			if (dist > followthres || player->dashspeed)
 				flymode = 0;
-			
-			// Set carried state
-			if (player->powers[pw_carry] == CR_PLAYER && sonic->tracer == tails)
-			{
-				flymode = 2;
-			}
 		}
 		// Read player inputs while carrying
 		else if (flymode == 2)
@@ -292,7 +296,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 			cmd->sidemove = 8 * pcmd->sidemove / 10;
 		}
 	}
-	
+
 	// ********
 	// JUMP
 	if (!(flymode || spinmode))
@@ -331,26 +335,6 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 	jump_last = jump;
 	spin_last = spin;
 
-	// ********
-	// Thinkfly overlay
-	if (thinkfly)
-	{
-		if (!tails->target)
-		{
-			P_SetTarget(&tails->target, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
-			if (tails->target)
-			{
-				P_SetTarget(&tails->target->target, tails);
-				P_SetMobjState(tails->target, S_FLIGHTINDICATOR);
-			}
-		}
-	}
-	else if (tails->target && tails->target->type == MT_OVERLAY && tails->target->state == states+S_FLIGHTINDICATOR)
-	{
-		P_RemoveMobj(tails->target);
-		P_SetTarget(&tails->target, NULL);
-	}
-
 	// Turn the virtual keypresses into ticcmd_t.
 	B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin);
 
@@ -444,7 +428,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
 			cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
 		if (straferight)
 			cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
-		
+
 		// cap inputs so the bot can't accelerate faster diagonally
 		angle = R_PointToAngle2(0, 0, cmd->sidemove << FRACBITS, cmd->forwardmove << FRACBITS);
 		{
@@ -560,3 +544,30 @@ void B_RespawnBot(INT32 playernum)
 	P_SetScale(tails, sonic->scale);
 	tails->destscale = sonic->destscale;
 }
+
+void B_HandleFlightIndicator(player_t *player)
+{
+	mobj_t *tails = player->mo;
+
+	if (!tails)
+		return;
+
+	if (thinkfly && player->bot == 1 && tails->health)
+	{
+		if (!tails->hnext)
+		{
+			P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
+			if (tails->hnext)
+			{
+				P_SetTarget(&tails->hnext->target, tails);
+				P_SetTarget(&tails->hnext->hprev, tails);
+				P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
+			}
+		}
+	}
+	else if (tails->hnext && tails->hnext->type == MT_OVERLAY && tails->hnext->state == states+S_FLIGHTINDICATOR)
+	{
+		P_RemoveMobj(tails->hnext);
+		P_SetTarget(&tails->hnext, NULL);
+	}
+}
diff --git a/src/b_bot.h b/src/b_bot.h
index 20b2803b69ccf4af24432f4f7f7058aaf7cd271c..54ef300a342e15dc50008dec5a1abaa7b21be4ef 100644
--- a/src/b_bot.h
+++ b/src/b_bot.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2007-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -15,3 +15,4 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
 boolean B_CheckRespawn(player_t *player);
 void B_MoveBlocked(player_t *player);
 void B_RespawnBot(INT32 playernum);
+void B_HandleFlightIndicator(player_t *player);
diff --git a/src/byteptr.h b/src/byteptr.h
index aa09d6be41f5251400cc902929c2ab87397f3802..57fe9cb75b3fd36637f35105a0a5bc02a32245e0 100644
--- a/src/byteptr.h
+++ b/src/byteptr.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/command.c b/src/command.c
index 0a0b62956f6e4b9c7f39fc0f01a8c7eda7fa6044..2893106dd7ca6690bcf0ac863165e2ea67b76f02 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -54,13 +54,15 @@ static void COM_Add_f(void);
 static void CV_EnforceExecVersion(void);
 static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
 static boolean CV_Command(void);
-static consvar_t *CV_FindVar(const char *name);
+consvar_t *CV_FindVar(const char *name);
 static const char *CV_StringValue(const char *var_name);
 static consvar_t *consvar_vars; // list of registered console variables
 
 static char com_token[1024];
 static char *COM_Parse(char *data);
 
+static char * COM_Purge (char *text, int *lenp);
+
 CV_PossibleValue_t CV_OnOff[] = {{0, "Off"}, {1, "On"}, {0, NULL}};
 CV_PossibleValue_t CV_YesNo[] = {{0, "No"}, {1, "Yes"}, {0, NULL}};
 CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}};
@@ -100,31 +102,61 @@ static cmdalias_t *com_alias; // aliases list
 
 static vsbuf_t com_text; // variable sized buffer
 
+/** Purges control characters out of some text.
+  *
+  * \param s The text.
+  * \param np Optionally a pointer to fill with the new string length.
+  * \return The text.
+  * \sa COM_ExecuteString
+  */
+static char *
+COM_Purge (char *s, int *np)
+{
+	char *t;
+	char *p;
+	int n;
+	n = strlen(s);
+	t = s + n + 1;
+	p = s;
+	while (( p = strchr(p, '\033') ))
+	{
+		memmove(p, &p[1], t - p - 1);
+		n--;
+	}
+	if (np)
+		(*np) = n;
+	return s;
+}
+
 /** Adds text into the command buffer for later execution.
   *
   * \param ptext The text to add.
-  * \sa COM_BufInsertText
+  * \sa COM_BufInsertTextEx
   */
-void COM_BufAddText(const char *ptext)
+void COM_BufAddTextEx(const char *ptext, int flags)
 {
-	size_t l;
+	int l;
+	char *text;
 
-	l = strlen(ptext);
+	text = COM_Purge(Z_StrDup(ptext), &l);
 
-	if (com_text.cursize + l >= com_text.maxsize)
+	if (com_text.cursize + 2 + l >= com_text.maxsize)
 	{
 		CONS_Alert(CONS_WARNING, M_GetText("Command buffer full!\n"));
 		return;
 	}
-	VS_Write(&com_text, ptext, l);
+
+	VS_WriteEx(&com_text, text, l, flags);
+
+	Z_Free(text);
 }
 
 /** Adds command text and executes it immediately.
   *
   * \param ptext The text to execute. A newline is automatically added.
-  * \sa COM_BufAddText
+  * \sa COM_BufAddTextEx
   */
-void COM_BufInsertText(const char *ptext)
+void COM_BufInsertTextEx(const char *ptext, int flags)
 {
 	char *temp = NULL;
 	size_t templen;
@@ -138,7 +170,7 @@ void COM_BufInsertText(const char *ptext)
 	}
 
 	// add the entire text of the file (or alias)
-	COM_BufAddText(ptext);
+	COM_BufAddTextEx(ptext, flags);
 	COM_BufExecute(); // do it right away
 
 	// add the copied off data
@@ -272,6 +304,7 @@ static size_t com_argc;
 static char *com_argv[MAX_ARGS];
 static const char *com_null_string = "";
 static char *com_args = NULL; // current command args or NULL
+static int com_flags;
 
 static void Got_NetVar(UINT8 **p, INT32 playernum);
 
@@ -394,12 +427,21 @@ static void COM_TokenizeString(char *ptext)
 
 	com_argc = 0;
 	com_args = NULL;
+	com_flags = 0;
 
 	while (com_argc < MAX_ARGS)
 	{
 		// Skip whitespace up to a newline.
 		while (*ptext != '\0' && *ptext <= ' ' && *ptext != '\n')
-			ptext++;
+		{
+			if (ptext[0] == '\033')
+			{
+				com_flags = (unsigned)ptext[1];
+				ptext += 2;
+			}
+			else
+				ptext++;
+		}
 
 		// A newline means end of command in buffer,
 		// thus end of this command's args too.
@@ -1016,6 +1058,15 @@ void VS_Write(vsbuf_t *buf, const void *data, size_t length)
 	M_Memcpy(VS_GetSpace(buf, length), data, length);
 }
 
+void VS_WriteEx(vsbuf_t *buf, const void *data, size_t length, int flags)
+{
+	char *p;
+	p = VS_GetSpace(buf, 2 + length);
+	p[0] = '\033';
+	p[1] = flags;
+	M_Memcpy(&p[2], data, length);
+}
+
 /** Prints text in a variable buffer. Like VS_Write() plus a
   * trailing NUL.
   *
@@ -1055,7 +1106,7 @@ static const char *cv_null_string = "";
   * \return Pointer to the variable if found, or NULL.
   * \sa CV_FindNetVar
   */
-static consvar_t *CV_FindVar(const char *name)
+consvar_t *CV_FindVar(const char *name)
 {
 	consvar_t *cvar;
 
@@ -1586,14 +1637,16 @@ void CV_StealthSet(consvar_t *var, const char *value)
   */
 static void CV_SetValueMaybeStealth(consvar_t *var, INT32 value, boolean stealth)
 {
-	char val[32];
+	char val[SKINNAMESIZE+1];
 
 	if (var == &cv_forceskin) // Special handling.
 	{
+		const char *tmpskin = NULL;
 		if ((value < 0) || (value >= numskins))
-			sprintf(val, "None");
+			tmpskin = "None";
 		else
-			sprintf(val, "%s", skins[value].name);
+			tmpskin = skins[value].name;
+		strncpy(val, tmpskin, SKINNAMESIZE);
 	}
 	else
 		sprintf(val, "%d", value);
@@ -2013,6 +2066,9 @@ static boolean CV_Command(void)
 	if (!v)
 		return false;
 
+	if (( com_flags & COM_SAFE ) && ( v->flags & CV_NOLUA ))
+		return false;
+
 	// perform a variable print or set
 	if (COM_Argc() == 1)
 	{
@@ -2114,8 +2170,13 @@ skipwhite:
 				com_token[len] = 0;
 				return data;
 			}
-			com_token[len] = c;
-			len++;
+			if (c == '\033')
+				data++;
+			else
+			{
+				com_token[len] = c;
+				len++;
+			}
 		}
 	}
 
@@ -2131,10 +2192,22 @@ skipwhite:
 	// parse a regular word
 	do
 	{
-		com_token[len] = c;
-		data++;
-		len++;
-		c = *data;
+		if (c == '\033')
+		{
+			do
+			{
+				data += 2;
+				c = *data;
+			}
+			while (c == '\033') ;
+		}
+		else
+		{
+			com_token[len] = c;
+			data++;
+			len++;
+			c = *data;
+		}
 		if (c == '{' || c == '}' || c == ')'|| c == '(' || c == '\'')
 			break;
 	} while (c > 32);
diff --git a/src/command.h b/src/command.h
index d324481f7b2467b64effa253026638e35898188d..42bd6eb84fccc3b99c903e54ae8ca20560d914e0 100644
--- a/src/command.h
+++ b/src/command.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -20,6 +20,11 @@
 // Command buffer & command execution
 //===================================
 
+enum
+{
+	COM_SAFE = 1,
+};
+
 typedef void (*com_func_t)(void);
 
 void COM_AddCommand(const char *name, com_func_t func);
@@ -36,10 +41,12 @@ size_t COM_FirstOption(void);
 const char *COM_CompleteCommand(const char *partial, INT32 skips);
 
 // insert at queu (at end of other command)
-void COM_BufAddText(const char *btext);
+#define COM_BufAddText(s) COM_BufAddTextEx(s, 0)
+void COM_BufAddTextEx(const char *btext, int flags);
 
 // insert in head (before other command)
-void COM_BufInsertText(const char *btext);
+#define COM_BufInsertText(s) COM_BufInsertTextEx(s, 0)
+void COM_BufInsertTextEx(const char *btext, int flags);
 
 // don't bother inserting, just do immediately
 void COM_ImmedExecute(const char *ptext);
@@ -71,6 +78,7 @@ void VS_Free(vsbuf_t *buf);
 void VS_Clear(vsbuf_t *buf);
 void *VS_GetSpace(vsbuf_t *buf, size_t length);
 void VS_Write(vsbuf_t *buf, const void *data, size_t length);
+void VS_WriteEx(vsbuf_t *buf, const void *data, size_t length, int flags);
 void VS_Print(vsbuf_t *buf, const char *data); // strcats onto the sizebuf
 
 //==================
@@ -100,7 +108,8 @@ typedef enum
 	CV_HIDEN = 1024, // variable is not part of the cvar list so cannot be accessed by the console
 	                 // can only be set when we have the pointer to it
                    // used on menus
-	CV_CHEAT = 2048 // Don't let this be used in multiplayer unless cheats are on.
+	CV_CHEAT = 2048, // Don't let this be used in multiplayer unless cheats are on.
+	CV_NOLUA = 4096,/* don't let this be called from Lua */
 } cvflags_t;
 
 typedef struct CV_PossibleValue_s
@@ -140,6 +149,9 @@ void CV_ToggleExecVersion(boolean enable);
 // register a variable for use at the console
 void CV_RegisterVar(consvar_t *variable);
 
+// returns a console variable by name
+consvar_t *CV_FindVar(const char *name);
+
 // sets changed to 0 for every console variable
 void CV_ClearChangedFlags(void);
 
diff --git a/src/config.h.in b/src/config.h.in
index 58d07e26d2d875d688109b6895a3ceecd534eb53..233cbdc53349cd4a97a9bf1f96607600138c5867 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -26,14 +26,14 @@
 #else
 
 /* Manually defined asset hashes for non-CMake builds
- * Last updated 2015 / 05 / 03 - v2.1.15 - main assets
- * Last updated 2018 / ?? / ?? - v2.2 - patch.pk3
+ * Last updated 2019 / 12 / 06 - v2.2.0 - main assets
+ * Last updated 20?? / ?? / ?? - v2.2.? - patch.pk3
  */
-#define ASSET_HASH_SRB2_PK3   "c1b9577687f8a795104aef4600720ea7"
-#define ASSET_HASH_ZONES_PK3  "303838c6c534d9540288360fa49cca60"
-#define ASSET_HASH_PLAYER_DTA "cfca0f1c73023cbbd8f844f45480f799"
+#define ASSET_HASH_SRB2_PK3   "51419a33b4982d840c6772c159ba7c0a"
+#define ASSET_HASH_ZONES_PK3  "df74843919fd51af26a0baa8e21e4c19"
+#define ASSET_HASH_PLAYER_DTA "56a247e074dd0dc794b6617efef1e918"
 #ifdef USE_PATCH_DTA
-#define ASSET_HASH_PATCH_PK3  "dbbf8bc6121618ee3be2d5b14650429b"
+#define ASSET_HASH_PATCH_PK3  "there is no patch.pk3, only zuul"
 #endif
 
 #endif
diff --git a/src/console.c b/src/console.c
index 11e236696093e3c754c26a861d8862206af9312a..01d1ddaa25f1b275b3e5e27ab3d3389736af98c9 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -20,6 +20,7 @@
 #include "g_input.h"
 #include "hu_stuff.h"
 #include "keys.h"
+#include "r_main.h"
 #include "r_defs.h"
 #include "sounds.h"
 #include "st_stuff.h"
@@ -591,6 +592,8 @@ void CON_ToggleOff(void)
 	CON_ClearHUD();
 	con_forcepic = 0;
 	con_clipviewtop = -1; // remove console clipping of view
+
+	I_UpdateMouseGrab();
 }
 
 boolean CON_Ready(void)
@@ -615,6 +618,7 @@ void CON_Ticker(void)
 		consoletoggle = false;
 		con_destlines = 0;
 		CON_ClearHUD();
+		I_UpdateMouseGrab();
 	}
 
 	// console key was pushed
@@ -627,6 +631,7 @@ void CON_Ticker(void)
 		{
 			con_destlines = 0;
 			CON_ClearHUD();
+			I_UpdateMouseGrab();
 		}
 		else
 			CON_ChangeHeight();
@@ -1285,10 +1290,10 @@ void CONS_Printf(const char *fmt, ...)
 	con_scrollup = 0;
 
 	// if not in display loop, force screen update
-	if (con_startup)
+	if (con_startup && (!setrenderneeded))
 	{
 #ifdef _WINDOWS
-		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_CACHE);
+		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_PATCH);
 
 		// Jimita: CON_DrawBackpic just called V_DrawScaledPatch
 		V_DrawScaledPatch(0, 0, 0, con_backpic);
@@ -1545,7 +1550,7 @@ static void CON_DrawConsole(void)
 	// draw console background
 	if (cons_backpic.value || con_forcepic)
 	{
-		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_CACHE);
+		patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_PATCH);
 
 		// Jimita: CON_DrawBackpic just called V_DrawScaledPatch
 		V_DrawScaledPatch(0, 0, 0, con_backpic);
@@ -1602,8 +1607,18 @@ void CON_Drawer(void)
 	if (!con_started || !graphics_started)
 		return;
 
+	if (needpatchrecache)
+	{
+		W_FlushCachedPatches();
+		HU_LoadGraphics();
+	}
+
 	if (con_recalc)
+	{
 		CON_RecalcSize();
+		if (con_curlines <= 0)
+			CON_ClearHUD();
+	}
 
 	if (con_curlines > 0)
 		CON_DrawConsole();
diff --git a/src/console.h b/src/console.h
index f628080330f80a6b855af539f3e7e73efb83150e..7a55c4b92da51b7c172c9a9749e2d25bf342df3b 100644
--- a/src/console.h
+++ b/src/console.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 2a89116a14e83785655ecdc1e49706b2b68be577..98ff3a6f8ed95785f76d0a53e6883d0797fe5167 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -70,7 +70,7 @@
 boolean server = true; // true or false but !server == client
 #define client (!server)
 boolean nodownload = false;
-static boolean serverrunning = false;
+boolean serverrunning = false;
 INT32 serverplayer = 0;
 char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
 
@@ -611,11 +611,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 
 	rsp->health = LONG(players[i].mo->health);
 	rsp->angle = (angle_t)LONG(players[i].mo->angle);
-#ifdef ROTSPRITE
 	rsp->rollangle = (angle_t)LONG(players[i].mo->rollangle);
-#else
-	rsp->rollangle = 0;
-#endif
 	rsp->x = LONG(players[i].mo->x);
 	rsp->y = LONG(players[i].mo->y);
 	rsp->z = LONG(players[i].mo->z);
@@ -766,9 +762,7 @@ static void resynch_read_player(resynch_pak *rsp)
 	//At this point, the player should have a body, whether they were respawned or not.
 	P_UnsetThingPosition(players[i].mo);
 	players[i].mo->angle = (angle_t)LONG(rsp->angle);
-#ifdef ROTSPRITE
 	players[i].mo->rollangle = (angle_t)LONG(rsp->rollangle);
-#endif
 	players[i].mo->eflags = (UINT16)SHORT(rsp->eflags);
 	players[i].mo->flags = LONG(rsp->flags);
 	players[i].mo->flags2 = LONG(rsp->flags2);
@@ -1025,7 +1019,7 @@ static void SV_SendResynch(INT32 node)
 		netbuffer->packettype = PT_RESYNCHEND;
 
 		netbuffer->u.resynchend.randomseed = P_GetRandSeed();
-		if (gametype == GT_CTF)
+		if (gametyperules & GTR_TEAMFLAGS)
 			resynch_write_ctf(&netbuffer->u.resynchend);
 		resynch_write_others(&netbuffer->u.resynchend);
 
@@ -1271,8 +1265,12 @@ static boolean CL_SendJoin(void)
 	if (splitscreen || botingame)
 		localplayers++;
 	netbuffer->u.clientcfg.localplayers = localplayers;
+	netbuffer->u.clientcfg._255 = 255;
+	netbuffer->u.clientcfg.packetversion = PACKETVERSION;
 	netbuffer->u.clientcfg.version = VERSION;
 	netbuffer->u.clientcfg.subversion = SUBVERSION;
+	strncpy(netbuffer->u.clientcfg.application, SRB2APPLICATION,
+			sizeof netbuffer->u.clientcfg.application);
 	strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME);
 	strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME);
 	return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
@@ -1283,15 +1281,20 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
 	UINT8 *p;
 
 	netbuffer->packettype = PT_SERVERINFO;
+	netbuffer->u.serverinfo._255 = 255;
+	netbuffer->u.serverinfo.packetversion = PACKETVERSION;
 	netbuffer->u.serverinfo.version = VERSION;
 	netbuffer->u.serverinfo.subversion = SUBVERSION;
+	strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION,
+			sizeof netbuffer->u.serverinfo.application);
 	// return back the time value so client can compute their ping
 	netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
 	netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
 
 	netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
 	netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
-	netbuffer->u.serverinfo.gametype = (UINT8)gametype;
+	strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
+			sizeof netbuffer->u.serverinfo.gametypename);
 	netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
 	netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
 	netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
@@ -1301,7 +1304,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
 
 	M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
 
-	if (*mapheaderinfo[gamemap-1]->lvlttl)
+	memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle);
+
+	if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl)
 	{
 		char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
 		while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
@@ -1319,14 +1324,13 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
 	else
 		strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32);
 
-	netbuffer->u.serverinfo.maptitle[32] = '\0';
-
-	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
+	if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
 		netbuffer->u.serverinfo.iszone = 1;
 	else
 		netbuffer->u.serverinfo.iszone = 0;
 
-	netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
+	if (mapheaderinfo[gamemap-1])
+		netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
 
 	p = PutFileNeeded();
 
@@ -1633,7 +1637,7 @@ static void CL_LoadReceivedSavegame(void)
 		{
 			CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
 			if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
-				CONS_Printf(M_GetText(" ZONE"));
+				CONS_Printf(M_GetText(" Zone"));
 			if (actnum > 0)
 				CONS_Printf(" %2d", actnum);
 		}
@@ -1717,12 +1721,21 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
 		if (serverlistcount >= MAXSERVERLIST)
 			return; // list full
 
+		if (info->_255 != 255)
+			return;/* old packet format */
+
+		if (info->packetversion != PACKETVERSION)
+			return;/* old new packet format */
+
 		if (info->version != VERSION)
 			return; // Not same version.
 
 		if (info->subversion != SUBVERSION)
 			return; // Close, but no cigar.
 
+		if (strcmp(info->application, SRB2APPLICATION))
+			return;/* that's a different mod */
+
 		i = serverlistcount++;
 	}
 
@@ -2110,13 +2123,10 @@ static void CL_ConnectToServer(boolean viams)
 
 	if (i != -1)
 	{
-		UINT8 num = serverlist[i].info.gametype;
-		const char *gametypestr = NULL;
+		char *gametypestr = serverlist[i].info.gametypename;
 		CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername);
-		if (num < NUMGAMETYPES)
-			gametypestr = Gametype_Names[num];
-		if (gametypestr)
-			CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
+		gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0';
+		CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
 		CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100,
 		 serverlist[i].info.version%100, serverlist[i].info.subversion);
 	}
@@ -2429,7 +2439,7 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
 		}
 	}
 
-	if (gametype == GT_CTF)
+	if (gametyperules & GTR_TEAMFLAGS)
 		P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you!
 
 	// If in a special stage, redistribute the player's spheres across
@@ -2485,6 +2495,17 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
 	(void)reason;
 #endif
 
+	// don't look through someone's view who isn't there
+	if (playernum == displayplayer)
+	{
+#ifdef HAVE_BLUA
+		// Call ViewpointSwitch hooks here.
+		// The viewpoint was forcibly changed.
+		LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], true);
+#endif
+		displayplayer = consoleplayer;
+	}
+
 	// Reset player data
 	CL_ClearPlayer(playernum);
 
@@ -2502,16 +2523,13 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
 		RemoveAdminPlayer(playernum); // don't stay admin after you're gone
 	}
 
-	if (playernum == displayplayer)
-		displayplayer = consoleplayer; // don't look through someone's view who isn't there
-
 #ifdef HAVE_BLUA
 	LUA_InvalidatePlayer(&players[playernum]);
 #endif
 
 	if (G_TagGametype()) //Check if you still have a game. Location flexible. =P
 		P_CheckSurvivors();
-	else if (gametype == GT_RACE || gametype == GT_COMPETITION)
+	else if (gametyperules & GTR_RACE)
 		P_CheckRacers();
 }
 
@@ -2984,8 +3002,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 		CL_RemovePlayer(pnum, kickreason);
 }
 
-consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL	};
-consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
+consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL	};
+consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
 static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
 consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}};
@@ -3460,7 +3478,7 @@ void SV_StartSinglePlayerServer(void)
 	server = true;
 	netgame = false;
 	multiplayer = false;
-	gametype = GT_COOP;
+	G_SetGametype(GT_COOP);
 
 	// no more tic the game with this settings!
 	SV_StopServer();
@@ -3506,6 +3524,12 @@ static void HandleConnect(SINT8 node)
 
 	if (bannednode && bannednode[node])
 		SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server"));
+	else if (netbuffer->u.clientcfg._255 != 255 ||
+			netbuffer->u.clientcfg.packetversion != PACKETVERSION)
+		SV_SendRefuse(node, "Incompatible packet formats.");
+	else if (strncmp(netbuffer->u.clientcfg.application, SRB2APPLICATION,
+				sizeof netbuffer->u.clientcfg.application))
+		SV_SendRefuse(node, "Different SRB2 modifications\nare not compatible.");
 	else if (netbuffer->u.clientcfg.version != VERSION
 		|| netbuffer->u.clientcfg.subversion != SUBVERSION)
 		SV_SendRefuse(node, va(M_GetText("Different SRB2 versions cannot\nplay a netgame!\n(server version %d.%d.%d)"), VERSION/100, VERSION%100, SUBVERSION));
@@ -3628,6 +3652,10 @@ static void HandleServerInfo(SINT8 node)
 	const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE;
 	netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff);
 	netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0;
+	netbuffer->u.serverinfo.application
+		[sizeof netbuffer->u.serverinfo.application - 1] = '\0';
+	netbuffer->u.serverinfo.gametypename
+		[sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0';
 
 	SL_InsertServer(&netbuffer->u.serverinfo, node);
 }
@@ -3739,7 +3767,7 @@ static void HandlePacketFromAwayNode(SINT8 node)
 			if (client)
 			{
 				maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
-				gametype = netbuffer->u.servercfg.gametype;
+				G_SetGametype(netbuffer->u.servercfg.gametype);
 				modifiedgame = netbuffer->u.servercfg.modifiedgame;
 				for (j = 0; j < MAXPLAYERS; j++)
 					adminplayers[j] = netbuffer->u.servercfg.adminplayers[j];
@@ -4123,7 +4151,7 @@ static void HandlePacketFromPlayer(SINT8 node)
 
 			P_SetRandSeed(netbuffer->u.resynchend.randomseed);
 
-			if (gametype == GT_CTF)
+			if (gametyperules & GTR_TEAMFLAGS)
 				resynch_read_ctf(&netbuffer->u.resynchend);
 			resynch_read_others(&netbuffer->u.resynchend);
 
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 7313c73554b6c557e40fda53072c1e10d1dcc85b..c797e5ca8b4cde824851312d7757f7ca23bb9770 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -19,6 +19,16 @@
 #include "tables.h"
 #include "d_player.h"
 
+/*
+The 'packet version' may be used with packets whose
+format is expected to change between versions.
+
+This version is independent of the mod name, and standard
+version and subversion. It should only account for the
+basic fields of the packet, and change infrequently.
+*/
+#define PACKETVERSION 1
+
 // Network play related stuff.
 // There is a data struct that stores network
 //  communication related stuff, and another
@@ -320,8 +330,13 @@ typedef struct {
 #pragma warning(default : 4200)
 #endif
 
+#define MAXAPPLICATION 16
+
 typedef struct
 {
+	UINT8 _255;/* see serverinfo_pak */
+	UINT8 packetversion;
+	char application[MAXAPPLICATION];
 	UINT8 version; // Different versions don't work
 	UINT8 subversion; // Contains build version
 	UINT8 localplayers;
@@ -334,11 +349,19 @@ typedef struct
 // This packet is too large
 typedef struct
 {
+	/*
+	In the old packet, 'version' is the first field. Now that field is set
+	to 255 always, so older versions won't be confused with the new
+	versions or vice-versa.
+	*/
+	UINT8 _255;
+	UINT8 packetversion;
+	char  application[MAXAPPLICATION];
 	UINT8 version;
 	UINT8 subversion;
 	UINT8 numberofplayer;
 	UINT8 maxplayer;
-	UINT8 gametype;
+	char gametypename[24];
 	UINT8 modifiedgame;
 	UINT8 cheatsenabled;
 	UINT8 isdedicated;
diff --git a/src/d_event.h b/src/d_event.h
index e9374efaf539b7970cab644f1ff4365bd488af49..c61b9460a37174b01419306c0f7235ecfc090e6e 100644
--- a/src/d_event.h
+++ b/src/d_event.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_main.c b/src/d_main.c
index 98e16a27722ce6784e60905cde4249ea1f619b3e..432dfd34f5043cc517cc295f2c14db9a195b8be9 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -108,6 +108,8 @@ boolean devparm = false; // started game with -devparm
 boolean singletics = false; // timedemo
 boolean lastdraw = false;
 
+static void D_CheckRendererState(void);
+
 postimg_t postimgtype = postimg_none;
 INT32 postimgparam;
 postimg_t postimgtype2 = postimg_none;
@@ -212,6 +214,7 @@ INT16 wipetypepost = -1;
 
 static void D_Display(void)
 {
+	INT32 setrenderstillneeded = 0;
 	boolean forcerefresh = false;
 	static boolean wipe = false;
 	INT32 wipedefindex = 0;
@@ -222,11 +225,38 @@ static void D_Display(void)
 	if (nodrawers)
 		return; // for comparative timing/profiling
 
-	// check for change of screen size (video mode)
-	if (setmodeneeded && !wipe)
+	// Lactozilla: Switching renderers works by checking
+	// if the game has to do it right when the frame
+	// needs to render. If so, five things will happen:
+	// 1. Interface functions will be called so
+	//    that switching to OpenGL creates a
+	//    GL context, and switching to Software
+	//    allocates screen buffers.
+	// 2. Software will set drawer functions,
+	//    and OpenGL will load textures and
+	//    create plane polygons, if necessary.
+	// 3. Functions related to switching video
+	//    modes (resolution) are called.
+	// 4. Patch data is freed from memory,
+	//    and recached if necessary.
+	// 5. The frame is ready to be drawn!
+
+	// stop movie if needs to change renderer
+	if (setrenderneeded && (moviemode == MM_APNG))
+		M_StopMovie();
+
+	// check for change of renderer or screen size (video mode)
+	if ((setrenderneeded || setmodeneeded) && !wipe)
+	{
+		if (setrenderneeded)
+		{
+			CONS_Debug(DBG_RENDER, "setrenderneeded set (%d)\n", setrenderneeded);
+			setrenderstillneeded = setrenderneeded;
+		}
 		SCR_SetMode(); // change video mode
+	}
 
-	if (vid.recalc)
+	if (vid.recalc || setrenderstillneeded)
 	{
 		SCR_Recalc(); // NOTE! setsizeneeded is set by SCR_Recalc()
 #ifdef HWRENDER
@@ -237,12 +267,15 @@ static void D_Display(void)
 	}
 
 	// change the view size if needed
-	if (setsizeneeded)
+	if (setsizeneeded || setrenderstillneeded)
 	{
 		R_ExecuteSetViewSize();
 		forcerefresh = true; // force background redraw
 	}
 
+	// Lactozilla: Renderer switching
+	D_CheckRendererState();
+
 	// draw buffered stuff to screen
 	// Used only by linux GGI version
 	I_UpdateNoBlit();
@@ -274,7 +307,10 @@ static void D_Display(void)
 			 && wipetypepre != UINT8_MAX)
 			{
 				F_WipeStartScreen();
-				F_WipeColorFill(31);
+				// Check for Mega Genesis fade
+				wipestyleflags = WSF_FADEOUT;
+				if (F_TryColormapFade(31))
+					wipetypepost = -1; // Don't run the fade below this one
 				F_WipeEndScreen();
 				F_RunWipe(wipetypepre, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN);
 			}
@@ -291,8 +327,11 @@ static void D_Display(void)
 	switch (gamestate)
 	{
 		case GS_TITLESCREEN:
-			F_TitleScreenDrawer();
-			break;
+			if (!titlemapinaction || !curbghide) {
+				F_TitleScreenDrawer();
+				break;
+			}
+			/* FALLTHRU */
 		case GS_LEVEL:
 			if (!gametic)
 				break;
@@ -363,11 +402,56 @@ static void D_Display(void)
 
 		// clean up border stuff
 		// see if the border needs to be initially drawn
-		if (gamestate == GS_LEVEL)
+		if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap)))
 		{
 			// draw the view directly
 
-			D_Render();
+			if (!automapactive && !dedicated && cv_renderview.value)
+			{
+				if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
+				{
+					topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
+					objectsdrawn = 0;
+	#ifdef HWRENDER
+					if (rendermode != render_soft)
+						HWR_RenderPlayerView(0, &players[displayplayer]);
+					else
+	#endif
+					if (rendermode != render_none)
+						R_RenderPlayerView(&players[displayplayer]);
+				}
+
+				// render the second screen
+				if (splitscreen && players[secondarydisplayplayer].mo)
+				{
+	#ifdef HWRENDER
+					if (rendermode != render_soft)
+						HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
+					else
+	#endif
+					if (rendermode != render_none)
+					{
+						viewwindowy = vid.height / 2;
+						M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
+
+						topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
+
+						R_RenderPlayerView(&players[secondarydisplayplayer]);
+
+						viewwindowy = 0;
+						M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
+					}
+				}
+
+				// Image postprocessing effect
+				if (rendermode == render_soft)
+				{
+					if (postimgtype)
+						V_DoPostProcessor(0, postimgtype, postimgparam);
+					if (postimgtype2)
+						V_DoPostProcessor(1, postimgtype2, postimgparam2);
+				}
+			}
 
 			if (lastdraw)
 			{
@@ -380,9 +464,14 @@ static void D_Display(void)
 				lastdraw = false;
 			}
 
-			ST_Drawer();
-			F_TextPromptDrawer();
-			HU_Drawer();
+			if (gamestate == GS_LEVEL)
+			{
+				ST_Drawer();
+				F_TextPromptDrawer();
+				HU_Drawer();
+			}
+			else
+				F_TitleScreenDrawer();
 		}
 	}
 
@@ -401,7 +490,7 @@ static void D_Display(void)
 			py = 4;
 		else
 			py = viewwindowy + 4;
-		patch = W_CachePatchName("M_PAUSE", PU_CACHE);
+		patch = W_CachePatchName("M_PAUSE", PU_PATCH);
 		V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch);
 #else
 		INT32 y = ((automapactive) ? (32) : (BASEVIDHEIGHT/2));
@@ -435,15 +524,24 @@ static void D_Display(void)
 		if (rendermode != render_none)
 		{
 			F_WipeEndScreen();
+
 			// Funny.
 			if (WipeStageTitle && st_overlay)
 			{
 				lt_ticker--;
 				lt_lasttic = lt_ticker;
-				ST_preLevelTitleCardDrawer(0, false);
+				ST_preLevelTitleCardDrawer();
 				V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
 				F_WipeStartScreen();
 			}
+
+			// Check for Mega Genesis fade
+			if (F_ShouldColormapFade())
+			{
+				wipestyleflags |= WSF_FADEIN;
+				wipestyleflags &= ~WSF_FADEOUT;
+			}
+
 			F_RunWipe(wipetypepost, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN);
 		}
 
@@ -492,56 +590,25 @@ static void D_Display(void)
 
 		I_FinishUpdate(); // page flip or blit buffer
 	}
+
+	needpatchflush = false;
+	needpatchrecache = false;
 }
 
-void D_Render(void)
+// Lactozilla: Check the renderer's state
+// after a possible renderer switch.
+void D_CheckRendererState(void)
 {
-	if (!automapactive && !dedicated && cv_renderview.value)
-	{
-		if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
-		{
-			topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
-			objectsdrawn = 0;
-#ifdef HWRENDER
-			if (rendermode != render_soft)
-				HWR_RenderPlayerView(0, &players[displayplayer]);
-			else
-#endif
-			if (rendermode != render_none)
-				R_RenderPlayerView(&players[displayplayer]);
-		}
-
-		// render the second screen
-		if (splitscreen && players[secondarydisplayplayer].mo)
-		{
-#ifdef HWRENDER
-			if (rendermode != render_soft)
-				HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
-			else
-#endif
-			if (rendermode != render_none)
-			{
-				viewwindowy = vid.height / 2;
-				M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
-
-				topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
-
-				R_RenderPlayerView(&players[secondarydisplayplayer]);
-
-				viewwindowy = 0;
-				M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
-			}
-		}
-
-		// Image postprocessing effect
-		if (rendermode == render_soft)
-		{
-			if (postimgtype)
-				V_DoPostProcessor(0, postimgtype, postimgparam);
-			if (postimgtype2)
-				V_DoPostProcessor(1, postimgtype2, postimgparam2);
-		}
-	}
+	// flush all patches from memory
+	// (also frees memory tagged with PU_CACHE)
+	// (which are not necessarily patches but I don't care)
+	if (needpatchflush)
+		Z_FlushCachedPatches();
+
+	// some patches have been freed,
+	// so cache them again
+	if (needpatchrecache)
+		R_ReloadHUDGraphics();
 }
 
 // =========================================================================
@@ -559,9 +626,6 @@ void D_SRB2Loop(void)
 
 	// Pushing of + parameters is now done back in D_SRB2Main, not here.
 
-	CONS_Printf("I_StartupKeyboard()...\n");
-	I_StartupKeyboard();
-
 #ifdef _WINDOWS
 	CONS_Printf("I_StartupMouse()...\n");
 	I_DoStartupMouse();
@@ -588,8 +652,7 @@ void D_SRB2Loop(void)
 	// hack to start on a nice clear console screen.
 	COM_ImmedExecute("cls;version");
 
-	if (rendermode == render_soft)
-		V_DrawScaledPatch(0, 0, 0, (patch_t *)W_CacheLumpNum(W_GetNumForName("CONSBACK"), PU_CACHE));
+	V_DrawScaledPatch(0, 0, 0, W_CachePatchNum(W_GetNumForName("CONSBACK"), PU_CACHE));
 	I_FinishUpdate(); // page flip or blit buffer
 
 	for (;;)
@@ -664,6 +727,7 @@ void D_SRB2Loop(void)
 
 		// consoleplayer -> displayplayer (hear sounds from viewpoint)
 		S_UpdateSounds(); // move positional sounds
+		S_UpdateClosedCaptions();
 
 		// check for media change, loop music..
 		I_UpdateCD();
@@ -702,7 +766,7 @@ void D_StartTitle(void)
 
 	if (netgame)
 	{
-		if (gametype == GT_COOP)
+		if (gametyperules & GTR_CAMPAIGN)
 		{
 			G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat
 
@@ -729,6 +793,8 @@ void D_StartTitle(void)
 	for (i = 0; i < MAXPLAYERS; i++)
 		CL_ClearPlayer(i);
 
+	players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
+
 	splitscreen = false;
 	SplitScreen_OnChange();
 	botingame = false;
@@ -747,7 +813,7 @@ void D_StartTitle(void)
 
 	gameaction = ga_nothing;
 	displayplayer = consoleplayer = 0;
-	gametype = GT_COOP;
+	G_SetGametype(GT_COOP);
 	paused = false;
 	advancedemo = false;
 	F_InitMenuPresValues();
@@ -963,7 +1029,7 @@ void D_SRB2Main(void)
 	// Print GPL notice for our console users (Linux)
 	CONS_Printf(
 	"\n\nSonic Robo Blast 2\n"
-	"Copyright (C) 1998-2018 by Sonic Team Junior\n\n"
+	"Copyright (C) 1998-2019 by Sonic Team Junior\n\n"
 	"This program comes with ABSOLUTELY NO WARRANTY.\n\n"
 	"This is free software, and you are welcome to redistribute it\n"
 	"and/or modify it under the terms of the GNU General Public License\n"
@@ -1103,26 +1169,6 @@ void D_SRB2Main(void)
 	if (M_CheckParm("-server") || dedicated)
 		netgame = server = true;
 
-	if (M_CheckParm("-warp") && M_IsNextParm())
-	{
-		const char *word = M_GetNextParm();
-		char ch; // use this with sscanf to catch non-digits with
-		if (fastncmp(word, "MAP", 3)) // MAPxx name
-			pstartmap = M_MapNumber(word[3], word[4]);
-		else if (sscanf(word, "%d%c", &pstartmap, &ch) != 1) // a plain number
-			I_Error("Cannot warp to map %s (invalid map name)\n", word);
-		// Don't check if lump exists just yet because the wads haven't been loaded!
-		// Just do a basic range check here.
-		if (pstartmap < 1 || pstartmap > NUMMAPS)
-			I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
-		else
-		{
-			if (!M_CheckParm("-server"))
-				G_SetGameModified(true);
-			autostart = true;
-		}
-	}
-
 	CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
 	Z_Init();
 
@@ -1149,31 +1195,10 @@ void D_SRB2Main(void)
 	// Have to be done here before files are loaded
 	M_InitCharacterTables();
 
-	mainwads = 0;
-
-#ifndef DEVELOP // md5s last updated 12/14/14
-
-	// Check MD5s of autoloaded files
-	W_VerifyFileMD5(mainwads++, ASSET_HASH_SRB2_PK3); // srb2.pk3
-	W_VerifyFileMD5(mainwads++, ASSET_HASH_ZONES_PK3); // zones.pk3
-	W_VerifyFileMD5(mainwads++, ASSET_HASH_PLAYER_DTA); // player.dta
+	mainwads = 3; // doesn't include music.dta
 #ifdef USE_PATCH_DTA
-	W_VerifyFileMD5(mainwads++, ASSET_HASH_PATCH_DTA); // patch.pk3
+	mainwads++;
 #endif
-	// don't check music.dta because people like to modify it, and it doesn't matter if they do
-	// ...except it does if they slip maps in there, and that's what W_VerifyNMUSlumps is for.
-	//mainwads++; // music.dta does not increment mainwads (see <= 2.1.21)
-	//mainwads++; // neither does music_new.dta
-#else
-
-	mainwads++;	// srb2.pk3
-	mainwads++; // zones.pk3
-	mainwads++; // player.dta
-#ifdef USE_PATCH_DTA
-	mainwads++; // patch.dta
-#endif
-	//mainwads++; // music.dta does not increment mainwads (see <= 2.1.21)
-	//mainwads++; // neither does music_new.dta
 
 	// load wad, including the main wad file
 	CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n");
@@ -1185,11 +1210,34 @@ void D_SRB2Main(void)
 #endif
 	D_CleanFile();
 
+#ifndef DEVELOP // md5s last updated 06/12/19 (ddmmyy)
+
+	// Check MD5s of autoloaded files
+	W_VerifyFileMD5(0, ASSET_HASH_SRB2_PK3); // srb2.pk3
+	W_VerifyFileMD5(1, ASSET_HASH_ZONES_PK3); // zones.pk3
+	W_VerifyFileMD5(2, ASSET_HASH_PLAYER_DTA); // player.dta
+#ifdef USE_PATCH_DTA
+	W_VerifyFileMD5(3, ASSET_HASH_PATCH_DTA); // patch.pk3
+#endif
+	// don't check music.dta because people like to modify it, and it doesn't matter if they do
+	// ...except it does if they slip maps in there, and that's what W_VerifyNMUSlumps is for.
 #endif //ifndef DEVELOP
 
-	mainwadstally = packetsizetally;
+	mainwadstally = packetsizetally; // technically not accurate atm, remember to port the two-stage -file process from kart in 2.2.x
 
-	mainwadstally = packetsizetally;
+	if (M_CheckParm("-warp") && M_IsNextParm())
+	{
+		const char *word = M_GetNextParm();
+		pstartmap = G_FindMapByNameOrCode(word, 0);
+		if (! pstartmap)
+			I_Error("Cannot find a map remotely named '%s'\n", word);
+		else
+		{
+			if (!M_CheckParm("-server"))
+				G_SetGameModified(true);
+			autostart = true;
+		}
+	}
 
 	cht_Init();
 
@@ -1199,6 +1247,13 @@ void D_SRB2Main(void)
 	CONS_Printf("I_StartupGraphics()...\n");
 	I_StartupGraphics();
 
+#ifdef HWRENDER
+	// Lactozilla: Add every hardware mode CVAR and CCMD.
+	// Has to be done before the configuration file loads,
+	// but after the OpenGL library loads.
+	HWR_AddCommands();
+#endif
+
 	//--------------------------------------------------------- CONSOLE
 	// setup loading screen
 	SCR_Startup();
@@ -1229,6 +1284,16 @@ void D_SRB2Main(void)
 	// set user default mode or mode set at cmdline
 	SCR_CheckDefaultMode();
 
+	// Lactozilla: Does the render mode need to change?
+	if ((setrenderneeded != 0) && (setrenderneeded != rendermode))
+	{
+		needpatchflush = true;
+		needpatchrecache = true;
+		VID_CheckRenderer();
+		SCR_ChangeRendererCVars(setrenderneeded);
+	}
+	D_CheckRendererState();
+
 	wipegamestate = gamestate;
 
 	savedata.lives = 0; // flag this as not-used
@@ -1287,9 +1352,10 @@ void D_SRB2Main(void)
 		I_StartupSound();
 		I_InitMusic();
 		S_InitSfxChannels(cv_soundvolume.value);
-		S_InitMusicDefs();
 	}
 
+	S_InitMusicDefs();
+
 	CONS_Printf("ST_Init(): Init status bar.\n");
 	ST_Init();
 
@@ -1408,14 +1474,14 @@ void D_SRB2Main(void)
 			if (newgametype == -1) // reached end of the list with no match
 			{
 				j = atoi(sgametype); // assume they gave us a gametype number, which is okay too
-				if (j >= 0 && j < NUMGAMETYPES)
+				if (j >= 0 && j < gametypecount)
 					newgametype = (INT16)j;
 			}
 
 			if (newgametype != -1)
 			{
 				j = gametype;
-				gametype = newgametype;
+				G_SetGametype(newgametype);
 				D_GameTypeChanged(j);
 			}
 		}
@@ -1451,7 +1517,7 @@ void D_SRB2Main(void)
 	{
 		levelstarttic = gametic;
 		G_SetGamestate(GS_LEVEL);
-		if (!P_SetupLevel(false))
+		if (!P_LoadLevel(false))
 			I_Quit(); // fail so reset game stuff
 	}
 }
diff --git a/src/d_main.h b/src/d_main.h
index 65c51802a7c644c871b862ef0ff28182204d0d6b..b0cd3cb8855b092d9a0fa55d52e0b67dfd9bf732 100644
--- a/src/d_main.h
+++ b/src/d_main.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -54,7 +54,4 @@ const char *D_Home(void);
 void D_AdvanceDemo(void);
 void D_StartTitle(void);
 
-/* Here for title maps */
-void D_Render(void);
-
 #endif //__D_MAIN__
diff --git a/src/d_net.c b/src/d_net.c
index cbfef7726ab8e5e96077c4c09b900193eb07582c..573c9cfe90aca8bdec2c71ef62b1db638a368e5b 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -497,9 +497,9 @@ void Net_AckTicker(void)
 		node_t *node = &nodes[nodei];
 		if (ackpak[i].acknum && ackpak[i].senttime + NODETIMEOUT < I_GetTime())
 		{
-			if (ackpak[i].resentnum > 10 && (node->flags & NF_CLOSE))
+			if (ackpak[i].resentnum > 20 && (node->flags & NF_CLOSE))
 			{
-				DEBFILE(va("ack %d sent 10 times so connection is supposed lost: node %d\n",
+				DEBFILE(va("ack %d sent 20 times so connection is supposed lost: node %d\n",
 					i, nodei));
 				Net_CloseConnection(nodei | FORCECLOSE);
 
diff --git a/src/d_net.h b/src/d_net.h
index 3d1058702101fef5d250d1c210b2756dd47ea408..c6fe4a27f759ef05e0ebfd2f07c0386c529af36f 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -40,6 +40,8 @@ extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if a
 extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
 extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
 
+extern boolean serverrunning;
+
 INT32 Net_GetFreeAcks(boolean urgent);
 void Net_AckTicker(void);
 
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 2b6055873e25a6b07ec48308e882260281ca7f27..19e1f1dad915e08d3559502f45b99f5851e48653 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -327,6 +327,10 @@ consvar_t cv_numlaps = {"numlaps", "4", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_con
 static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}};
 consvar_t cv_basenumlaps = {"basenumlaps", "Map default", CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
+// Point and time limits for every gametype
+INT32 pointlimits[NUMGAMETYPES];
+INT32 timelimits[NUMGAMETYPES];
+
 // log elemental hazards -- not a netvar, is local to current player
 consvar_t cv_hazardlog = {"hazardlog", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
 
@@ -355,7 +359,7 @@ static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NUL
 consvar_t cv_inttime = {"inttime", "10", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 static CV_PossibleValue_t coopstarposts_cons_t[] = {{0, "Per-player"}, {1, "Shared"}, {2, "Teamwork"}, {0, NULL}};
-consvar_t cv_coopstarposts = {"coopstarposts", "Teamwork", CV_NETVAR|CV_CALL|CV_CHEAT, coopstarposts_cons_t, CoopStarposts_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_coopstarposts = {"coopstarposts", "Per-player", CV_NETVAR|CV_CALL, coopstarposts_cons_t, CoopStarposts_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
 static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player"}, {2, "Avoid Game Over"}, {3, "Single pool"}, {0, NULL}};
 consvar_t cv_cooplives = {"cooplives", "Avoid Game Over", CV_NETVAR|CV_CALL|CV_CHEAT, cooplives_cons_t, CoopLives_OnChange, 0, NULL, NULL, 0, 0, NULL};
@@ -381,6 +385,9 @@ char timedemo_csv_id[256];
 boolean timedemo_quit;
 
 INT16 gametype = GT_COOP;
+UINT32 gametyperules = 0;
+INT16 gametypecount = (GT_CTF + 1);
+
 boolean splitscreen = false;
 boolean circuitmap = false;
 INT32 adminplayers[MAXPLAYERS];
@@ -823,6 +830,10 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_autobrake);
 	CV_RegisterVar(&cv_autobrake2);
 
+	// Ported from kart
+	CV_RegisterVar(&cv_deadzone);
+	CV_RegisterVar(&cv_deadzone2);
+
 	// s_sound.c
 	CV_RegisterVar(&cv_soundvolume);
 	CV_RegisterVar(&cv_closedcaptioning);
@@ -837,6 +848,10 @@ void D_RegisterClientCommands(void)
 	// screen.c
 	CV_RegisterVar(&cv_fullscreen);
 	CV_RegisterVar(&cv_renderview);
+	CV_RegisterVar(&cv_renderer);
+#ifdef HWRENDER
+	CV_RegisterVar(&cv_newrenderer);
+#endif
 	CV_RegisterVar(&cv_scr_depth);
 	CV_RegisterVar(&cv_scr_width);
 	CV_RegisterVar(&cv_scr_height);
@@ -1122,7 +1137,7 @@ UINT8 CanChangeSkin(INT32 playernum)
 			return true;
 
 		// Can change skin during initial countdown.
-		if ((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE)
+		if ((gametyperules & GTR_RACE) && leveltime < 4*TICRATE)
 			return true;
 
 		if (G_TagGametype())
@@ -1228,16 +1243,16 @@ static void SendNameAndColor(void)
 		}
 		else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
 		{
-			boolean notsame;
+			//boolean notsame;
 
 			cv_skin.value = foundskin;
 
-			notsame = (cv_skin.value != players[consoleplayer].skin);
+			//notsame = (cv_skin.value != players[consoleplayer].skin);
 
 			SetPlayerSkin(consoleplayer, cv_skin.string);
 			CV_StealthSet(&cv_skin, skins[cv_skin.value].name);
 
-			if (notsame)
+			/*if (notsame)
 			{
 				CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor);
 
@@ -1245,7 +1260,7 @@ static void SendNameAndColor(void)
 
 				if (players[consoleplayer].mo)
 					players[consoleplayer].mo->color = (UINT8)players[consoleplayer].skincolor;
-			}
+			}*/
 		}
 		else
 		{
@@ -1356,15 +1371,16 @@ static void SendNameAndColor2(void)
 		}
 		else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin))
 		{
-			boolean notsame;
+			//boolean notsame;
 
 			cv_skin2.value = foundskin;
 
-			notsame = (cv_skin2.value != players[secondplaya].skin);
+			//notsame = (cv_skin2.value != players[secondplaya].skin);
 
 			SetPlayerSkin(secondplaya, cv_skin2.string);
+			CV_StealthSet(&cv_skin2, skins[cv_skin2.value].name);
 
-			if (notsame)
+			/*if (notsame)
 			{
 				CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor);
 
@@ -1372,7 +1388,7 @@ static void SendNameAndColor2(void)
 
 				if (players[secondplaya].mo)
 					players[secondplaya].mo->color = players[secondplaya].skincolor;
-			}
+			}*/
 		}
 		else
 		{
@@ -1797,18 +1813,15 @@ static void Command_Map_f(void)
 	boolean newresetplayers;
 
 	boolean mustmodifygame;
-	boolean usemapcode = false;
 
 	INT32 newmapnum;
 
 	char   *    mapname;
-	size_t      mapnamelen;
 	char   *realmapname = NULL;
 
 	INT32 newgametype = gametype;
 
 	INT32 d;
-	char *p;
 
 	if (client && !IsPlayerAdmin(consoleplayer))
 	{
@@ -1868,56 +1881,16 @@ static void Command_Map_f(void)
 	}
 
 	mapname = ConcatCommandArgv(1, first_option);
-	mapnamelen = strlen(mapname);
 
-	if (mapnamelen == 2)/* maybe two digit code */
-	{
-		if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) ))
-			usemapcode = true;
-	}
-	else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0)
-	{
-		if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ) == 0)
-		{
-			CONS_Alert(CONS_ERROR, M_GetText("Invalid map code '%s'.\n"), mapname);
-			Z_Free(mapname);
-			return;
-		}
-		usemapcode = true;
-	}
-
-	if (!usemapcode)
-	{
-		/* Now detect map number in base 10, which no one asked for. */
-		newmapnum = strtol(mapname, &p, 10);
-		if (*p == '\0')/* we got it */
-		{
-			if (newmapnum < 1 || newmapnum > NUMMAPS)
-			{
-				CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
-				Z_Free(mapname);
-				return;
-			}
-			usemapcode = true;
-		}
-		else
-		{
-			newmapnum = G_FindMap(mapname, &realmapname, NULL, NULL);
-		}
-	}
+	newmapnum = G_FindMapByNameOrCode(mapname, &realmapname);
 
-	if (newmapnum == 0 || !mapheaderinfo[newmapnum-1])
+	if (newmapnum == 0)
 	{
 		CONS_Alert(CONS_ERROR, M_GetText("Could not find any map described as '%s'.\n"), mapname);
 		Z_Free(mapname);
 		return;
 	}
 
-	if (usemapcode)
-	{
-		realmapname = G_BuildMapTitle(newmapnum);
-	}
-
 	if (mustmodifygame && option_force)
 	{
 		G_SetGameModified(false);
@@ -1937,7 +1910,7 @@ static void Command_Map_f(void)
 			if (isdigit(gametypename[0]))
 			{
 				d = atoi(gametypename);
-				if (d >= 0 && d < NUMGAMETYPES)
+				if (d >= 0 && d < gametypecount)
 					newgametype = d;
 				else
 				{
@@ -1945,7 +1918,7 @@ static void Command_Map_f(void)
 							"Gametype number %d is out of range. Use a number between"
 							" 0 and %d inclusive. ...Or just use the name. :v\n",
 							d,
-							NUMGAMETYPES-1);
+							gametypecount-1);
 					Z_Free(realmapname);
 					Z_Free(mapname);
 					return;
@@ -2064,8 +2037,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
 
 	lastgametype = gametype;
 	gametype = READUINT8(*cp);
+	G_SetGametype(gametype); // I fear putting that macro as an argument
 
-	if (gametype < 0 || gametype >= NUMGAMETYPES)
+	if (gametype < 0 || gametype >= gametypecount)
 		gametype = lastgametype;
 	else if (gametype != lastgametype)
 		D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
@@ -2196,6 +2170,8 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
 		else
 			S_ResumeAudio();
 	}
+
+	I_UpdateMouseGrab();
 }
 
 // Command for stuck characters in netgames, griefing, etc.
@@ -2408,7 +2384,7 @@ static void Command_Teamchange_f(void)
 	}
 
 	//additional check for hide and seek. Don't allow change of status after hidetime ends.
-	if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
+	if ((gametyperules & GTR_HIDEFROZEN) && leveltime >= (hidetime * TICRATE))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
 		return;
@@ -2505,7 +2481,7 @@ static void Command_Teamchange2_f(void)
 	}
 
 	//additional check for hide and seek. Don't allow change of status after hidetime ends.
-	if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
+	if ((gametyperules & GTR_HIDEFROZEN) && leveltime >= (hidetime * TICRATE))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
 		return;
@@ -2634,7 +2610,7 @@ static void Command_ServerTeamChange_f(void)
 	}
 
 	//additional check for hide and seek. Don't allow change of status after hidetime ends.
-	if (gametype == GT_HIDEANDSEEK && leveltime >= (hidetime * TICRATE))
+	if ((gametyperules & GTR_HIDEFROZEN) && leveltime >= (hidetime * TICRATE))
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("Hiding time expired; no Hide and Seek status changes allowed!\n"));
 		return;
@@ -2723,6 +2699,16 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
 		return;
 	}
 
+#ifdef HAVE_BLUA
+	// Don't switch team, just go away, please, go awaayyyy, aaauuauugghhhghgh
+	if (!LUAh_TeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled))
+		return;
+#endif
+
+	//no status changes after hidetime
+	if ((gametyperules & GTR_HIDEFROZEN) && (leveltime >= (hidetime * TICRATE)))
+		error = true;
+
 	//Make sure that the right team number is sent. Keep in mind that normal clients cannot change to certain teams in certain gametypes.
 	switch (gametype)
 	{
@@ -2877,7 +2863,15 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
 
 	//reset view if you are changed, or viewing someone who was changed.
 	if (playernum == consoleplayer || displayplayer == playernum)
+	{
+#ifdef HAVE_BLUA
+		// Call ViewpointSwitch hooks here.
+		// The viewpoint was forcibly changed.
+		if (displayplayer != consoleplayer) // You're already viewing yourself. No big deal.
+			LUAh_ViewpointSwitch(&players[playernum], &players[displayplayer], true);
+#endif
 		displayplayer = consoleplayer;
+	}
 
 	if (G_GametypeHasTeams())
 	{
@@ -3613,7 +3607,7 @@ static void Command_ShowGametype_f(void)
 	}
 
 	// get name string for current gametype
-	if (gametype >= 0 && gametype < NUMGAMETYPES)
+	if (gametype >= 0 && gametype < gametypecount)
 		gametypestr = Gametype_Names[gametype];
 
 	if (gametypestr)
@@ -3675,7 +3669,7 @@ void ItemFinder_OnChange(void)
 static void PointLimit_OnChange(void)
 {
 	// Don't allow pointlimit in Single Player/Co-Op/Race!
-	if (server && Playing() && G_PlatformGametype())
+	if (server && Playing() && !(gametyperules & GTR_POINTLIMIT))
 	{
 		if (cv_pointlimit.value)
 			CV_StealthSetValue(&cv_pointlimit, 0);
@@ -3714,7 +3708,7 @@ static void CoopStarposts_OnChange(void)
 {
 	INT32 i;
 
-	if (!(netgame || multiplayer) || gametype != GT_COOP)
+	if (!(netgame || multiplayer) || !G_GametypeUsesCoopStarposts())
 		return;
 
 	switch (cv_coopstarposts.value)
@@ -3769,7 +3763,7 @@ static void CoopLives_OnChange(void)
 {
 	INT32 i;
 
-	if (!(netgame || multiplayer) || gametype != GT_COOP)
+	if (!(netgame || multiplayer) || !G_GametypeUsesCoopLives())
 		return;
 
 	switch (cv_cooplives.value)
@@ -3808,11 +3802,19 @@ static void CoopLives_OnChange(void)
 
 static void ExitMove_OnChange(void)
 {
+	UINT8 i;
+
 	if (!(netgame || multiplayer) || gametype != GT_COOP)
 		return;
 
 	if (cv_exitmove.value)
+	{
+		for (i = 0; i < MAXPLAYERS; ++i)
+			if (playeringame[i] && players[i].mo
+				&& players[i].mo->target && players[i].mo->target->type == MT_SIGN)
+				P_SetTarget(&players[i].mo->target, NULL);
 		CONS_Printf(M_GetText("Players can now move after completing the level.\n"));
+	}
 	else
 		CONS_Printf(M_GetText("Players can no longer move after completing the level.\n"));
 }
@@ -3830,7 +3832,7 @@ UINT32 timelimitintics = 0;
 static void TimeLimit_OnChange(void)
 {
 	// Don't allow timelimit in Single Player/Co-Op/Race!
-	if (server && Playing() && cv_timelimit.value != 0 && G_PlatformGametype())
+	if (server && Playing() && cv_timelimit.value != 0 && !(gametyperules & GTR_TIMELIMIT))
 	{
 		CV_SetValue(&cv_timelimit, 0);
 		return;
@@ -3866,9 +3868,9 @@ void D_GameTypeChanged(INT32 lastgametype)
 	{
 		const char *oldgt = NULL, *newgt = NULL;
 
-		if (lastgametype >= 0 && lastgametype < NUMGAMETYPES)
+		if (lastgametype >= 0 && lastgametype < gametypecount)
 			oldgt = Gametype_Names[lastgametype];
-		if (gametype >= 0 && lastgametype < NUMGAMETYPES)
+		if (gametype >= 0 && lastgametype < gametypecount)
 			newgt = Gametype_Names[gametype];
 
 		if (oldgt && newgt)
@@ -3922,11 +3924,20 @@ void D_GameTypeChanged(INT32 lastgametype)
 				if (!cv_itemrespawntime.changed)
 					CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
 				break;
+			default:
+				if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
+				{
+					CV_SetValue(&cv_timelimit, timelimits[gametype]);
+					CV_SetValue(&cv_pointlimit, pointlimits[gametype]);
+				}
+				if (!cv_itemrespawntime.changed)
+					CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
+				break;
 		}
 	}
 	else if (!multiplayer && !netgame)
 	{
-		gametype = GT_COOP;
+		G_SetGametype(GT_COOP);
 		// These shouldn't matter anymore
 		//CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue);
 		//CV_SetValue(&cv_itemrespawn, 0);
@@ -3935,7 +3946,7 @@ void D_GameTypeChanged(INT32 lastgametype)
 	// reset timelimit and pointlimit in race/coop, prevent stupid cheats
 	if (server)
 	{
-		if (G_PlatformGametype())
+		if (!(gametyperules & GTR_POINTLIMIT))
 		{
 			if (cv_timelimit.value)
 				CV_SetValue(&cv_timelimit, 0);
@@ -3953,6 +3964,7 @@ void D_GameTypeChanged(INT32 lastgametype)
 
 	// When swapping to a gametype that supports spectators,
 	// make everyone a spectator initially.
+	// Averted with GTR_NOSPECTATORSPAWN.
 	if (!splitscreen && (G_GametypeHasSpectators()))
 	{
 		INT32 i;
@@ -3960,7 +3972,7 @@ void D_GameTypeChanged(INT32 lastgametype)
 			if (playeringame[i])
 			{
 				players[i].ctfteam = 0;
-				players[i].spectator = true;
+				players[i].spectator = (gametyperules & GTR_NOSPECTATORSPAWN) ? false : true;
 			}
 	}
 
@@ -4289,6 +4301,8 @@ void Command_ExitGame_f(void)
 	for (i = 0; i < MAXPLAYERS; i++)
 		CL_ClearPlayer(i);
 
+	players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
+
 	splitscreen = false;
 	SplitScreen_OnChange();
 	botingame = false;
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index c2e6f980dc53381c525ec235cf3cde8c9721a1cf..8f857c6dbc5a309119b2fac1d4a334959fbabd24 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_netfil.c b/src/d_netfil.c
index bd7089609f8ed2ff9ac23064173019bc28671416..93b4b199098ed83298fbb31ab58cb8fff3a8f386 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_netfil.h b/src/d_netfil.h
index 3d7c2ed599dafb0b5eb4c772403b42a771d8b236..17aeb8b7ea0206288596dcc937b6f49bf4f568d9 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_player.h b/src/d_player.h
index 6d0c5f5d140de5079d67fbb18032b1022d63eb5c..62f38193f7d9224b8e9da40466a8467bdb97edbf 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_think.h b/src/d_think.h
index b907c17fd77d6f231c1813df056867322bcf56e4..c56551c21073cb1bc5482b5297e4546658b4aab1 100644
--- a/src/d_think.h
+++ b/src/d_think.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 1ea015439c0fbbae529cc3a98ec21975b27aefb5..337be1ff0541fd149ea00fb5a0d9cce1a8b2f425 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/dehacked.c b/src/dehacked.c
index 5fa4902cb708699a2d49a6de4ef9cb61e9d585a5..d295afd54b7c447e1509baa7b34328a0c59d4d1f 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -22,6 +22,7 @@
 #include "m_menu.h"
 #include "m_misc.h"
 #include "f_finale.h"
+#include "y_inter.h"
 #include "dehacked.h"
 #include "st_stuff.h"
 #include "i_system.h"
@@ -76,6 +77,7 @@ static UINT16 get_mus(const char *word, UINT8 dehacked_mode);
 static hudnum_t get_huditem(const char *word);
 static menutype_t get_menutype(const char *word);
 #ifndef HAVE_BLUA
+static INT16 get_gametype(const char *word);
 static powertype_t get_power(const char *word);
 #endif
 
@@ -242,6 +244,7 @@ INT32 flags;         Bits = 3232              MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|M
 INT32 raisestate;    Respawn frame = 32       S_NULL          // raisestate
                                          }, */
 
+#ifdef HWRENDER
 static INT32 searchvalue(const char *s)
 {
 	while (s[0] != '=' && s[0])
@@ -255,7 +258,6 @@ static INT32 searchvalue(const char *s)
 	}
 }
 
-#ifdef HWRENDER
 static float searchfvalue(const char *s)
 {
 	while (s[0] != '=' && s[0])
@@ -286,7 +288,7 @@ static void clear_levels(void)
 	// we may as well try to save some memory, right?
 	for (i = 0; i < NUMMAPS; ++i)
 	{
-		if (!mapheaderinfo[i])
+		if (!mapheaderinfo[i] || i == (tutorialmap-1))
 			continue;
 
 		// Custom map header info
@@ -590,6 +592,16 @@ static void readfreeslots(MYFILE *f)
 				} else
 					CONS_Alert(CONS_WARNING, "Ran out of free SPR2 slots!\n");
 			}
+			else if (fastcmp(type, "TOL"))
+			{
+				if (lastcustomtol > 31)
+					CONS_Alert(CONS_WARNING, "Ran out of free typeoflevel slots!\n");
+				else
+				{
+					G_AddTOL((1<<lastcustomtol), word);
+					lastcustomtol++;
+				}
+			}
 			else
 				deh_warning("Freeslots: unknown enum class '%s' for '%s_%s'", type, type, word);
 		}
@@ -863,14 +875,12 @@ static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame)
 			strupr(word);
 			value = atoi(word2); // used for numerical settings
 
-#ifdef ROTSPRITE
 			if (fastcmp(word, "XPIVOT"))
 				sprinfo->pivot[frame].x = value;
 			else if (fastcmp(word, "YPIVOT"))
 				sprinfo->pivot[frame].y = value;
 			else if (fastcmp(word, "ROTAXIS"))
 				sprinfo->pivot[frame].rotaxis = value;
-#endif
 			else
 			{
 				f->curpos = lastline;
@@ -886,7 +896,9 @@ static void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2)
 	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
 	char *word, *word2;
 	char *tmp;
+#ifdef HWRENDER
 	INT32 value;
+#endif
 	char *lastline;
 	INT32 skinnumbers[MAXSKINS];
 	INT32 foundskins = 0;
@@ -947,9 +959,9 @@ static void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2)
 					break;
 			}
 			strupr(word);
+#ifdef HWRENDER
 			value = atoi(word2); // used for numerical settings
 
-#ifdef HWRENDER
 			if (fastcmp(word, "LIGHTTYPE"))
 			{
 				if (sprite2)
@@ -1093,10 +1105,10 @@ static void readsprite2(MYFILE *f, INT32 num)
 	Z_Free(s);
 }
 
-static const struct {
-	const char *name;
-	const UINT16 flag;
-} TYPEOFLEVEL[] = {
+INT32 numtolinfo = NUMBASETOL;
+UINT32 lastcustomtol = 13;
+
+tolinfo_t TYPEOFLEVEL[NUMMAXTOL] = {
 	{"SOLO",TOL_SP},
 	{"SP",TOL_SP},
 	{"SINGLEPLAYER",TOL_SP},
@@ -1112,8 +1124,6 @@ static const struct {
 	{"TAG",TOL_TAG},
 	{"CTF",TOL_CTF},
 
-	{"CUSTOM",TOL_CUSTOM},
-
 	{"2D",TOL_2D},
 	{"MARIO",TOL_MARIO},
 	{"NIGHTS",TOL_NIGHTS},
@@ -1126,6 +1136,216 @@ static const struct {
 	{NULL, 0}
 };
 
+// copypasted from readPlayer :sleep:
+static const char *const GAMETYPERULE_LIST[];
+static void readgametype(MYFILE *f, char *gtname)
+{
+	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
+	char *word;
+	char *word2, *word2lwr = NULL;
+	char *tmp;
+	INT32 i, j;
+
+	INT16 newgtidx = 0;
+	UINT32 newgtrules = 0;
+	UINT32 newgttol = 0;
+	INT32 newgtpointlimit = 0;
+	INT32 newgttimelimit = 0;
+	UINT8 newgtleftcolor = 0;
+	UINT8 newgtrightcolor = 0;
+	INT16 newgtrankingstype = -1;
+	int newgtinttype = 0;
+	char gtdescription[441];
+	char gtconst[MAXLINELEN];
+
+	// Empty strings.
+	gtdescription[0] = '\0';
+	gtconst[0] = '\0';
+
+	do
+	{
+		if (myfgets(s, MAXLINELEN, f))
+		{
+			if (s[0] == '\n')
+				break;
+
+			word = strtok(s, " ");
+			if (word)
+				strupr(word);
+			else
+				break;
+
+			if (fastcmp(word, "DESCRIPTION"))
+			{
+				char *descr = NULL;
+
+				for (i = 0; i < MAXLINELEN-3; i++)
+				{
+					if (s[i] == '=')
+					{
+						descr = &s[i+2];
+						break;
+					}
+				}
+				if (descr)
+				{
+					strcpy(gtdescription, descr);
+					strcat(gtdescription, myhashfgets(descr, sizeof (gtdescription), f));
+				}
+				else
+					strcpy(gtdescription, "");
+
+				// For some reason, cutting the string did not work above. Most likely due to strcpy or strcat...
+				// It works down here, though.
+				{
+					INT32 numline = 0;
+					for (i = 0; i < MAXLINELEN-1; i++)
+					{
+						if (numline < 20 && gtdescription[i] == '\n')
+							numline++;
+
+						if (numline >= 20 || gtdescription[i] == '\0' || gtdescription[i] == '#')
+							break;
+					}
+				}
+				gtdescription[strlen(gtdescription)-1] = '\0';
+				gtdescription[i] = '\0';
+				continue;
+			}
+
+			word2 = strtok(NULL, " = ");
+			if (word2)
+			{
+				if (!word2lwr)
+					word2lwr = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
+				strcpy(word2lwr, word2);
+				strupr(word2);
+			}
+			else
+				break;
+
+			if (word2[strlen(word2)-1] == '\n')
+				word2[strlen(word2)-1] = '\0';
+			i = atoi(word2);
+
+			// Game type rules
+			if (fastcmp(word, "RULES"))
+			{
+				// GTR_
+				newgtrules = (UINT32)get_number(word2);
+			}
+			// Identifier
+			else if (fastcmp(word, "IDENTIFIER"))
+			{
+				// GT_
+				strncpy(gtconst, word2, MAXLINELEN);
+			}
+			// Point and time limits
+			else if (fastcmp(word, "DEFAULTPOINTLIMIT"))
+				newgtpointlimit = (INT32)i;
+			else if (fastcmp(word, "DEFAULTTIMELIMIT"))
+				newgttimelimit = (INT32)i;
+			// Level platter
+			else if (fastcmp(word, "HEADERCOLOR") || fastcmp(word, "HEADERCOLOUR"))
+				newgtleftcolor = newgtrightcolor = (UINT8)get_number(word2);
+			else if (fastcmp(word, "HEADERLEFTCOLOR") || fastcmp(word, "HEADERLEFTCOLOUR"))
+				newgtleftcolor = (UINT8)get_number(word2);
+			else if (fastcmp(word, "HEADERRIGHTCOLOR") || fastcmp(word, "HEADERRIGHTCOLOUR"))
+				newgtrightcolor = (UINT8)get_number(word2);
+			// Rankings type
+			else if (fastcmp(word, "RANKINGTYPE"))
+			{
+				// Case insensitive
+				newgtrankingstype = (int)get_number(word2);
+			}
+			// Intermission type
+			else if (fastcmp(word, "INTERMISSIONTYPE"))
+			{
+				// Case sensitive
+				newgtinttype = (int)get_number(word2lwr);
+			}
+			// Type of level
+			else if (fastcmp(word, "TYPEOFLEVEL"))
+			{
+				if (i) // it's just a number
+					newgttol = (UINT32)i;
+				else
+				{
+					UINT16 tol = 0;
+					tmp = strtok(word2,",");
+					do {
+						for (i = 0; TYPEOFLEVEL[i].name; i++)
+							if (fasticmp(tmp, TYPEOFLEVEL[i].name))
+								break;
+						if (!TYPEOFLEVEL[i].name)
+							deh_warning("readgametype %s: unknown typeoflevel flag %s\n", gtname, tmp);
+						tol |= TYPEOFLEVEL[i].flag;
+					} while((tmp = strtok(NULL,",")) != NULL);
+					newgttol = tol;
+				}
+			}
+			// The SOC probably provided gametype rules as words,
+			// instead of using the RULES keyword.
+			// Like for example "NOSPECTATORSPAWN = TRUE".
+			// This is completely valid, and looks better anyway.
+			else
+			{
+				UINT32 wordgt = 0;
+				for (j = 0; GAMETYPERULE_LIST[j]; j++)
+					if (fastcmp(word, GAMETYPERULE_LIST[j])) {
+						if (!j) // GTR_CAMPAIGN
+							wordgt |= 1;
+						else
+							wordgt |= (1<<j);
+						if (i || word2[0] == 'T' || word2[0] == 'Y')
+							newgtrules |= wordgt;
+						break;
+					}
+				if (!wordgt)
+					deh_warning("readgametype %s: unknown word '%s'", gtname, word);
+			}
+		}
+	} while (!myfeof(f)); // finish when the line is empty
+
+	// Free strings.
+	Z_Free(s);
+	if (word2lwr)
+		Z_Free(word2lwr);
+
+	// Ran out of gametype slots
+	if (gametypecount == NUMGAMETYPEFREESLOTS)
+	{
+		CONS_Alert(CONS_WARNING, "Ran out of free gametype slots!\n");
+		return;
+	}
+
+	// Add the new gametype
+	newgtidx = G_AddGametype(newgtrules);
+	G_AddGametypeTOL(newgtidx, newgttol);
+	G_SetGametypeDescription(newgtidx, gtdescription, newgtleftcolor, newgtrightcolor);
+
+	// Not covered by G_AddGametype alone.
+	if (newgtrankingstype == -1)
+		newgtrankingstype = newgtidx;
+	gametyperankings[newgtidx] = newgtrankingstype;
+	intermissiontypes[newgtidx] = newgtinttype;
+	pointlimits[newgtidx] = newgtpointlimit;
+	timelimits[newgtidx] = newgttimelimit;
+
+	// Write the new gametype name.
+	Gametype_Names[newgtidx] = Z_StrDup((const char *)gtname);
+
+	// Write the constant name.
+	if (gtconst[0] == '\0')
+		strncpy(gtconst, gtname, MAXLINELEN);
+	G_AddGametypeConstant(newgtidx, (const char *)gtconst);
+
+	// Update gametype_cons_t accordingly.
+	G_UpdateGametypeSelections();
+
+	CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]);
+}
+
 static const struct {
 	const char *name;
 	const mobjtype_t type;
@@ -1393,7 +1613,7 @@ static void readlevelheader(MYFILE *f, INT32 num)
 			else if (fastcmp(word, "TYPEOFLEVEL"))
 			{
 				if (i) // it's just a number
-					mapheaderinfo[num-1]->typeoflevel = (UINT16)i;
+					mapheaderinfo[num-1]->typeoflevel = (UINT32)i;
 				else
 				{
 					UINT16 tol = 0;
@@ -1518,10 +1738,27 @@ static void readlevelheader(MYFILE *f, INT32 num)
 					deh_warning("Level header %d: invalid bonus type number %d", num, i);
 			}
 
+			// Title card
+			else if (fastcmp(word, "TITLECARDZIGZAG"))
+			{
+				deh_strlcpy(mapheaderinfo[num-1]->ltzzpatch, word2,
+					sizeof(mapheaderinfo[num-1]->ltzzpatch), va("Level header %d: title card zigzag patch name", num));
+			}
+			else if (fastcmp(word, "TITLECARDZIGZAGTEXT"))
+			{
+				deh_strlcpy(mapheaderinfo[num-1]->ltzztext, word2,
+					sizeof(mapheaderinfo[num-1]->ltzztext), va("Level header %d: title card zigzag text patch name", num));
+			}
+			else if (fastcmp(word, "TITLECARDACTDIAMOND"))
+			{
+				deh_strlcpy(mapheaderinfo[num-1]->ltactdiamond, word2,
+					sizeof(mapheaderinfo[num-1]->ltactdiamond), va("Level header %d: title card act diamond patch name", num));
+			}
+
 			else if (fastcmp(word, "MAXBONUSLIVES"))
 				mapheaderinfo[num-1]->maxbonuslives = (SINT8)i;
 			else if (fastcmp(word, "LEVELFLAGS"))
-				mapheaderinfo[num-1]->levelflags = (UINT8)i;
+				mapheaderinfo[num-1]->levelflags = (UINT16)i;
 			else if (fastcmp(word, "MENUFLAGS"))
 				mapheaderinfo[num-1]->menuflags = (UINT8)i;
 
@@ -2671,11 +2908,9 @@ static actionpointer_t actionpointers[] =
 	{{A_SpawnObjectRelative},    "A_SPAWNOBJECTRELATIVE"},
 	{{A_ChangeAngleRelative},    "A_CHANGEANGLERELATIVE"},
 	{{A_ChangeAngleAbsolute},    "A_CHANGEANGLEABSOLUTE"},
-#ifdef ROTSPRITE
 	{{A_RollAngle},              "A_ROLLANGLE"},
 	{{A_ChangeRollAngleRelative},"A_CHANGEROLLANGLERELATIVE"},
 	{{A_ChangeRollAngleAbsolute},"A_CHANGEROLLANGLEABSOLUTE"},
-#endif
 	{{A_PlaySound},              "A_PLAYSOUND"},
 	{{A_FindTarget},             "A_FINDTARGET"},
 	{{A_FindTracer},             "A_FINDTRACER"},
@@ -4157,6 +4392,7 @@ static void ignorelines(MYFILE *f)
 static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 {
 	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
+	char textline[MAXLINELEN];
 	char *word;
 	char *word2;
 	INT32 i;
@@ -4177,6 +4413,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 		char *traverse;
 
 		myfgets(s, MAXLINELEN, f);
+		memcpy(textline, s, MAXLINELEN);
 		if (s[0] == '\n' || s[0] == '#')
 			continue;
 
@@ -4365,6 +4602,36 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 						ignorelines(f);
 					}
 				}
+				else if (fastcmp(word, "GAMETYPE"))
+				{
+					// Get the gametype name from textline
+					// instead of word2, so that gametype names
+					// aren't allcaps
+					INT32 c;
+					for (c = 0; c < MAXLINELEN; c++)
+					{
+						if (textline[c] == '\0')
+							break;
+						if (textline[c] == ' ')
+						{
+							char *gtname = (textline+c+1);
+							if (gtname)
+							{
+								// remove funny characters
+								INT32 j;
+								for (j = 0; j < (MAXLINELEN - c); j++)
+								{
+									if (gtname[j] == '\0')
+										break;
+									if (gtname[j] < 32)
+										gtname[j] = '\0';
+								}
+								readgametype(f, gtname);
+							}
+							break;
+						}
+					}
+				}
 				else if (fastcmp(word, "CUTSCENE"))
 				{
 					if (i > 0 && i < 129)
@@ -4464,20 +4731,34 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 						ignorelines(f);
 					}
 				}
-				// Last I heard this crashes the game if you try to use it
-				// so this is disabled for now
-				// -- Monster Iestyn
-/*				else if (fastcmp(word, "SRB2"))
+				else if (fastcmp(word, "SRB2"))
 				{
-					INT32 ver = searchvalue(strtok(NULL, "\n"));
-					if (ver != PATCHVERSION)
-						deh_warning("Patch is for SRB2 version %d,\nonly version %d is supported", ver, PATCHVERSION);
+					if (isdigit(word2[0]))
+					{
+						i = atoi(word2);
+						if (i != PATCHVERSION)
+						{
+							deh_warning(
+									"Patch is for SRB2 version %d, "
+									"only version %d is supported",
+									i,
+									PATCHVERSION
+							);
+						}
+					}
+					else
+					{
+						deh_warning(
+								"SRB2 version definition has incorrect format, "
+								"use \"SRB2 %d\"",
+								PATCHVERSION
+						);
+					}
 				}
 				// Clear all data in certain locations (mostly for unlocks)
 				// Unless you REALLY want to piss people off,
 				// define a custom gamedata /before/ doing this!!
 				// (then again, modifiedgame will prevent game data saving anyway)
-*/
 				else if (fastcmp(word, "CLEAR"))
 				{
 					boolean clearall = (fastcmp(word2, "ALL"));
@@ -4527,11 +4808,13 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 		if (introchanged)
 		{
 			menuactive = false;
+			I_UpdateMouseGrab();
 			COM_BufAddText("playintro");
 		}
 		else if (titlechanged)
 		{
 			menuactive = false;
+			I_UpdateMouseGrab();
 			COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed
 		}
 	}
@@ -4979,14 +5262,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_MINUS_BURST4",
 	"S_MINUS_BURST5",
 	"S_MINUS_POPUP",
-	"S_MINUS_UPWARD1",
-	"S_MINUS_UPWARD2",
-	"S_MINUS_UPWARD3",
-	"S_MINUS_UPWARD4",
-	"S_MINUS_DOWNWARD1",
-	"S_MINUS_DOWNWARD2",
-	"S_MINUS_DOWNWARD3",
-	"S_MINUS_DOWNWARD4",
+	"S_MINUS_AERIAL1",
+	"S_MINUS_AERIAL2",
+	"S_MINUS_AERIAL3",
+	"S_MINUS_AERIAL4",
 
 	// Minus dirt
 	"S_MINUSDIRT1",
@@ -8551,9 +8830,11 @@ static const char *const MOBJEFLAG_LIST[] = {
 	"JUSTSTEPPEDDOWN", // used for ramp sectors
 	"VERTICALFLIP", // Vertically flip sprite/allow upside-down physics
 	"GOOWATER", // Goo water
+	"TOUCHLAVA", // The mobj is touching a lava block
 	"PUSHED", // Mobj was already pushed this tic
 	"SPRUNG", // Mobj was already sprung this tic
 	"APPLYPMOMZ", // Platform movement
+	"TRACERANGLE", // Compute and trigger on mobj angle relative to tracer
 	NULL
 };
 
@@ -8625,6 +8906,42 @@ static const char *const PLAYERFLAG_LIST[] = {
 	NULL // stop loop here.
 };
 
+static const char *const GAMETYPERULE_LIST[] = {
+	"CAMPAIGN",
+	"RINGSLINGER",
+	"SPECTATORS",
+	"LIVES",
+	"TEAMS",
+	"FIRSTPERSON",
+	"POWERSTONES",
+	"TEAMFLAGS",
+	"FRIENDLY",
+	"SPECIALSTAGES",
+	"EMERALDTOKENS",
+	"EMERALDHUNT",
+	"RACE",
+	"TAG",
+	"POINTLIMIT",
+	"TIMELIMIT",
+	"OVERTIME",
+	"HURTMESSAGES",
+	"FRIENDLYFIRE",
+	"STARTCOUNTDOWN",
+	"HIDEFROZEN",
+	"BLINDFOLDED",
+	"RESPAWNDELAY",
+	"PITYSHIELD",
+	"DEATHPENALTY",
+	"NOSPECTATORSPAWN",
+	"DEATHMATCHSTARTS",
+	"SPAWNINVUL",
+	"SPAWNENEMIES",
+	"ALLOWEXIT",
+	"NOTITLECARD",
+	"CUTSCENES",
+	NULL
+};
+
 #ifdef HAVE_BLUA
 // Linedef flags
 static const char *const ML_LIST[16] = {
@@ -9040,21 +9357,6 @@ struct {
 	{"tr_trans90",tr_trans90},
 	{"NUMTRANSMAPS",NUMTRANSMAPS},
 
-	// Type of levels
-	{"TOL_SP",TOL_SP},
-	{"TOL_COOP",TOL_COOP},
-	{"TOL_COMPETITION",TOL_COMPETITION},
-	{"TOL_RACE",TOL_RACE},
-	{"TOL_MATCH",TOL_MATCH},
-	{"TOL_TAG",TOL_TAG},
-	{"TOL_CTF",TOL_CTF},
-	{"TOL_CUSTOM",TOL_CUSTOM},
-	{"TOL_2D",TOL_2D},
-	{"TOL_MARIO",TOL_MARIO},
-	{"TOL_NIGHTS",TOL_NIGHTS},
-	{"TOL_ERZ3",TOL_ERZ3},
-	{"TOL_XMAS",TOL_XMAS},
-
 	// Level flags
 	{"LF_SCRIPTISFILE",LF_SCRIPTISFILE},
 	{"LF_SPEEDMUSIC",LF_SPEEDMUSIC},
@@ -9163,6 +9465,10 @@ struct {
 	{"SF_MULTIABILITY",SF_MULTIABILITY},
 	{"SF_NONIGHTSROTATION",SF_NONIGHTSROTATION},
 
+	// Dashmode constants
+	{"DASHMODE_THRESHOLD",DASHMODE_THRESHOLD},
+	{"DASHMODE_MAX",DASHMODE_MAX},
+
 	// Character abilities!
 	// Primary
 	{"CA_NONE",CA_NONE}, // now slot 0!
@@ -9237,15 +9543,16 @@ struct {
 	{"DMG_CANHURTSELF",DMG_CANHURTSELF},
 	{"DMG_DEATHMASK",DMG_DEATHMASK},
 
-	// Gametypes, for use with global var "gametype"
-	{"GT_COOP",GT_COOP},
-	{"GT_COMPETITION",GT_COMPETITION},
-	{"GT_RACE",GT_RACE},
-	{"GT_MATCH",GT_MATCH},
-	{"GT_TEAMMATCH",GT_TEAMMATCH},
-	{"GT_TAG",GT_TAG},
-	{"GT_HIDEANDSEEK",GT_HIDEANDSEEK},
-	{"GT_CTF",GT_CTF},
+	// Intermission types
+	{"int_none",int_none},
+	{"int_coop",int_coop},
+	{"int_match",int_match},
+	{"int_teammatch",int_teammatch},
+	//{"int_tag",int_tag},
+	{"int_ctf",int_ctf},
+	{"int_spec",int_spec},
+	{"int_race",int_race},
+	{"int_comp",int_comp},
 
 	// Jingles (jingletype_t)
 	{"JT_NONE",JT_NONE},
@@ -9418,12 +9725,10 @@ struct {
 	{"DI_SOUTHEAST",DI_SOUTHEAST},
 	{"NUMDIRS",NUMDIRS},
 
-#ifdef ROTSPRITE
 	// Sprite rotation axis (rotaxis_t)
 	{"ROTAXIS_X",ROTAXIS_X},
 	{"ROTAXIS_Y",ROTAXIS_Y},
 	{"ROTAXIS_Z",ROTAXIS_Z},
-#endif
 
 	// Buttons (ticcmd_t)
 	{"BT_WEAPONMASK",BT_WEAPONMASK}, //our first four bits.
@@ -9454,6 +9759,7 @@ struct {
 	{"CV_HIDEN",CV_HIDEN},
 	{"CV_HIDDEN",CV_HIDEN},
 	{"CV_CHEAT",CV_CHEAT},
+	{"CV_NOLUA",CV_NOLUA},
 
 	// v_video flags
 	{"V_NOSCALEPATCH",V_NOSCALEPATCH},
@@ -9538,7 +9844,7 @@ struct {
 };
 
 static mobjtype_t get_mobjtype(const char *word)
-{ // Returns the vlaue of MT_ enumerations
+{ // Returns the value of MT_ enumerations
 	mobjtype_t i;
 	if (*word >= '0' && *word <= '9')
 		return atoi(word);
@@ -9690,8 +9996,22 @@ static menutype_t get_menutype(const char *word)
 }
 
 #ifndef HAVE_BLUA
+static INT16 get_gametype(const char *word)
+{ // Returns the value of GT_ enumerations
+	INT16 i;
+	if (*word >= '0' && *word <= '9')
+		return atoi(word);
+	if (fastncmp("GT_",word,3))
+		word += 3; // take off the GT_
+	for (i = 0; i < NUMGAMETYPES; i++)
+		if (fastcmp(word, Gametype_ConstantNames[i]+3))
+			return i;
+	deh_warning("Couldn't find gametype named 'GT_%s'",word);
+	return GT_COOP;
+}
+
 static powertype_t get_power(const char *word)
-{ // Returns the vlaue of pw_ enumerations
+{ // Returns the value of pw_ enumerations
 	powertype_t i;
 	if (*word >= '0' && *word <= '9')
 		return atoi(word);
@@ -9885,11 +10205,42 @@ static fixed_t find_const(const char **rword)
 		free(word);
 		return r;
 	}
-	else if (fastncmp("MN_",word,4)) {
+	else if (fastncmp("MN_",word,3)) {
 		r = get_menutype(word);
 		free(word);
 		return r;
 	}
+	else if (fastncmp("GT_",word,4)) {
+		r = get_gametype(word);
+		free(word);
+		return r;
+	}
+	else if (fastncmp("GTR_", word, 4)) {
+		char *p = word+4;
+		for (i = 0; GAMETYPERULE_LIST[i]; i++)
+			if (fastcmp(p, GAMETYPERULE_LIST[i])) {
+				free(word);
+				return (1<<i);
+			}
+
+		// Not found error
+		const_warning("game type rule",word);
+		free(word);
+		return 0;
+	}
+	else if (fastncmp("TOL_", word, 4)) {
+		char *p = word+4;
+		for (i = 0; TYPEOFLEVEL[i].name; i++)
+			if (fastcmp(p, TYPEOFLEVEL[i].name)) {
+				free(word);
+				return TYPEOFLEVEL[i].flag;
+			}
+
+		// Not found error
+		const_warning("typeoflevel",word);
+		free(word);
+		return 0;
+	}
 	else if (fastncmp("HUD_",word,4)) {
 		r = get_huditem(word);
 		free(word);
@@ -10099,6 +10450,20 @@ static inline int lib_freeslot(lua_State *L)
 			}
 			r++;
 		}
+		else if (fastcmp(type, "TOL"))
+		{
+			if (lastcustomtol > 31)
+				CONS_Alert(CONS_WARNING, "Ran out of free typeoflevel slots!\n");
+			else
+			{
+				UINT32 newtol = (1<<lastcustomtol);
+				CONS_Printf("TypeOfLevel TOL_%s allocated.\n",word);
+				G_AddTOL(newtol, word);
+				lua_pushinteger(L, newtol);
+				lastcustomtol++;
+				r++;
+			}
+		}
 		Z_Free(s);
 		lua_remove(L, 1);
 		continue;
@@ -10203,6 +10568,36 @@ static inline int lib_getenum(lua_State *L)
 		if (mathlib) return luaL_error(L, "playerflag '%s' could not be found.\n", word);
 		return 0;
 	}
+	else if (fastncmp("GT_", word, 3)) {
+		p = word;
+		for (i = 0; Gametype_ConstantNames[i]; i++)
+			if (fastcmp(p, Gametype_ConstantNames[i])) {
+				lua_pushinteger(L, i);
+				return 1;
+			}
+		if (mathlib) return luaL_error(L, "gametype '%s' could not be found.\n", word);
+		return 0;
+	}
+	else if (fastncmp("GTR_", word, 4)) {
+		p = word+4;
+		for (i = 0; GAMETYPERULE_LIST[i]; i++)
+			if (fastcmp(p, GAMETYPERULE_LIST[i])) {
+				lua_pushinteger(L, ((lua_Integer)1<<i));
+				return 1;
+			}
+		if (mathlib) return luaL_error(L, "game type rule '%s' could not be found.\n", word);
+		return 0;
+	}
+	else if (fastncmp("TOL_", word, 4)) {
+		p = word+4;
+		for (i = 0; TYPEOFLEVEL[i].name; i++)
+			if (fastcmp(p, TYPEOFLEVEL[i].name)) {
+				lua_pushinteger(L, TYPEOFLEVEL[i].flag);
+				return 1;
+			}
+		if (mathlib) return luaL_error(L, "typeoflevel '%s' could not be found.\n", word);
+		return 0;
+	}
 	else if (fastncmp("ML_", word, 3)) {
 		p = word+3;
 		for (i = 0; i < 16; i++)
@@ -10440,126 +10835,7 @@ static inline int lib_getenum(lua_State *L)
 	// DYNAMIC variables too!!
 	// Try not to add anything that would break netgames or timeattack replays here.
 	// You know, like consoleplayer, displayplayer, secondarydisplayplayer, or gametime.
-	if (fastcmp(word,"gamemap")) {
-		lua_pushinteger(L, gamemap);
-		return 1;
-	} else if (fastcmp(word,"maptol")) {
-		lua_pushinteger(L, maptol);
-		return 1;
-	} else if (fastcmp(word,"ultimatemode")) {
-		lua_pushboolean(L, ultimatemode != 0);
-		return 1;
-	} else if (fastcmp(word,"mariomode")) {
-		lua_pushboolean(L, mariomode != 0);
-		return 1;
-	} else if (fastcmp(word,"twodlevel")) {
-		lua_pushboolean(L, twodlevel != 0);
-		return 1;
-	} else if (fastcmp(word,"circuitmap")) {
-		lua_pushboolean(L, circuitmap);
-		return 1;
-	} else if (fastcmp(word,"netgame")) {
-		lua_pushboolean(L, netgame);
-		return 1;
-	} else if (fastcmp(word,"multiplayer")) {
-		lua_pushboolean(L, multiplayer);
-		return 1;
-	} else if (fastcmp(word,"modeattacking")) {
-		lua_pushboolean(L, modeattacking);
-		return 1;
-	} else if (fastcmp(word,"splitscreen")) {
-		lua_pushboolean(L, splitscreen);
-		return 1;
-	} else if (fastcmp(word,"gamecomplete")) {
-		lua_pushboolean(L, gamecomplete);
-		return 1;
-	} else if (fastcmp(word,"devparm")) {
-		lua_pushboolean(L, devparm);
-		return 1;
-	} else if (fastcmp(word,"modifiedgame")) {
-		lua_pushboolean(L, modifiedgame && !savemoddata);
-		return 1;
-	} else if (fastcmp(word,"menuactive")) {
-		lua_pushboolean(L, menuactive);
-		return 1;
-	} else if (fastcmp(word,"paused")) {
-		lua_pushboolean(L, paused);
-		return 1;
-	} else if (fastcmp(word,"titlemap")) {
-		lua_pushinteger(L, titlemap);
-		return 1;
-	} else if (fastcmp(word,"titlemapinaction")) {
-		lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF));
-		return 1;
-	} else if (fastcmp(word,"gametype")) {
-		lua_pushinteger(L, gametype);
-		return 1;
-	} else if (fastcmp(word,"leveltime")) {
-		lua_pushinteger(L, leveltime);
-		return 1;
-	} else if (fastcmp(word,"curWeather")) {
-		lua_pushinteger(L, curWeather);
-		return 1;
-	} else if (fastcmp(word,"globalweather")) {
-		lua_pushinteger(L, globalweather);
-		return 1;
-	} else if (fastcmp(word,"levelskynum")) {
-		lua_pushinteger(L, levelskynum);
-		return 1;
-	} else if (fastcmp(word,"globallevelskynum")) {
-		lua_pushinteger(L, globallevelskynum);
-		return 1;
-	} else if (fastcmp(word,"mapmusname")) {
-		lua_pushstring(L, mapmusname);
-		return 1;
-	} else if (fastcmp(word,"mapmusflags")) {
-		lua_pushinteger(L, mapmusflags);
-		return 1;
-	} else if (fastcmp(word,"mapmusposition")) {
-		lua_pushinteger(L, mapmusposition);
-		return 1;
-	// local player variables, by popular request
-	} else if (fastcmp(word,"consoleplayer")) { // player controlling console (aka local player 1)
-		if (consoleplayer < 0 || !playeringame[consoleplayer])
-			return 0;
-		LUA_PushUserdata(L, &players[consoleplayer], META_PLAYER);
-		return 1;
-	} else if (fastcmp(word,"displayplayer")) { // player visible on screen (aka display player 1)
-		if (displayplayer < 0 || !playeringame[displayplayer])
-			return 0;
-		LUA_PushUserdata(L, &players[displayplayer], META_PLAYER);
-		return 1;
-	} else if (fastcmp(word,"secondarydisplayplayer")) { // local/display player 2, for splitscreen
-		if (!splitscreen || secondarydisplayplayer < 0 || !playeringame[secondarydisplayplayer])
-			return 0;
-		LUA_PushUserdata(L, &players[secondarydisplayplayer], META_PLAYER);
-		return 1;
-	// end local player variables
-	} else if (fastcmp(word,"server")) {
-		if ((!multiplayer || !netgame) && !playeringame[serverplayer])
-			return 0;
-		LUA_PushUserdata(L, &players[serverplayer], META_PLAYER);
-		return 1;
-	} else if (fastcmp(word,"admin")) { // BACKWARDS COMPATIBILITY HACK: This was replaced with IsPlayerAdmin(), but some 2.1 Lua scripts still use the admin variable. It now points to the first admin player in the array.
-		LUA_Deprecated(L, "admin", "IsPlayerAdmin(player)");
-		if (!playeringame[adminplayers[0]] || IsPlayerAdmin(serverplayer))
-			return 0;
-		LUA_PushUserdata(L, &players[adminplayers[0]], META_PLAYER);
-		return 1;
-	} else if (fastcmp(word,"emeralds")) {
-		lua_pushinteger(L, emeralds);
-		return 1;
-	} else if (fastcmp(word,"gravity")) {
-		lua_pushinteger(L, gravity);
-		return 1;
-	} else if (fastcmp(word,"VERSIONSTRING")) {
-		lua_pushstring(L, VERSIONSTRING);
-		return 1;
-	} else if (fastcmp(word, "token")) {
-		lua_pushinteger(L, token);
-		return 1;
-	}
-	return 0;
+	return LUA_PushGlobals(L, word);
 }
 
 int LUA_EnumLib(lua_State *L)
@@ -10626,6 +10902,8 @@ static int lib_getActionName(lua_State *L)
 	return luaL_typerror(L, 1, "action userdata or Lua function");
 }
 
+
+
 int LUA_SOCLib(lua_State *L)
 {
 	lua_register(L,"freeslot",lib_freeslot);
diff --git a/src/dehacked.h b/src/dehacked.h
index 31f7f220dceb37654af225bf8113928922ac78f7..2b34377fd5e97e884f4c2a78f9062e0e34f76088 100644
--- a/src/dehacked.h
+++ b/src/dehacked.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -47,7 +47,7 @@ extern const char *superactions[MAXRECURSION];
 extern UINT8 superstack;
 
 // If the dehacked patch does not match this version, we throw a warning
-#define PATCHVERSION 210
+#define PATCHVERSION 220
 
 #define MAXLINELEN 1024
 
diff --git a/src/djgppdos/i_video.c b/src/djgppdos/i_video.c
index 56570b7bf8daf871fca8db3a03909823c33ad605..02c7a842bc2c0aa77fb7d8d7180a04d9029878b5 100644
--- a/src/djgppdos/i_video.c
+++ b/src/djgppdos/i_video.c
@@ -338,3 +338,8 @@ void I_StartupGraphics(void)
 	graphics_started = true;
 
 }
+
+void I_StartupHardwareGraphics(void)
+{
+	// oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo y
+}
diff --git a/src/djgppdos/rdb-s.h b/src/djgppdos/rdb-s.h
index 7a6d8be3c65595609ebe14f9e9e58fa60fcad1d3..b7a994fc9ccdf5dac08a8288e620a446efde56fc 100644
--- a/src/djgppdos/rdb-s.h
+++ b/src/djgppdos/rdb-s.h
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*-
 //-----------------------------------------------------------------------------
 //
-// Copyright (C) 2005-2018 by Sonic Team Jr.
+// Copyright (C) 2005-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
diff --git a/src/djgppdos/vid_vesa.c b/src/djgppdos/vid_vesa.c
index ec7b8b886343d0d8fe9c8d9e66eff3c1aeaf1793..c8ce7dae52e645372455b76cbad40ffd03c53a0e 100644
--- a/src/djgppdos/vid_vesa.c
+++ b/src/djgppdos/vid_vesa.c
@@ -378,6 +378,11 @@ INT32 VID_SetMode (INT32 modenum)  //, UINT8 *palette)
 	return 1;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 
 
 // converts a segm:offs 32bit pair to a 32bit flat ptr
diff --git a/src/doomdata.h b/src/doomdata.h
index b4142463c7673bfb9d1d5bcf7d53c9d502f2c9e4..f6e7cb58403b6d79059050730b738e136f6b0790 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/doomdef.h b/src/doomdef.h
index 511dad85820fe04eb16a7d3deaa3b42d617e964c..0a98c874aa5eb63ace8d9e04021df642d7790f80 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -127,9 +127,13 @@
 
 #ifdef LOGMESSAGES
 extern FILE *logstream;
+extern char logfilename[1024];
 #endif
 
-#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3
+/* A mod name to further distinguish versions. */
+#define SRB2APPLICATION "SRB2"
+
+//#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3
 #ifdef DEVELOP
 #define VERSION    0 // Game version
 #define SUBVERSION 0 // more precise version number
@@ -140,8 +144,8 @@ extern FILE *logstream;
 #else
 #define VERSION    202 // Game version
 #define SUBVERSION 0  // more precise version number
-#define VERSIONSTRING "v2.2"
-#define VERSIONSTRINGW L"v2.2"
+#define VERSIONSTRING "v2.2.0"
+#define VERSIONSTRINGW L"v2.2.0"
 // Hey! If you change this, add 1 to the MODVERSION below!
 // Otherwise we can't force updates!
 #endif
@@ -199,14 +203,14 @@ extern FILE *logstream;
 
 // The Modification ID; must be obtained from Rob ( https://mb.srb2.org/private.php?do=newpm&u=546 ).
 // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server.
-// "12" is the default mod ID for version 2.1
-#define MODID 12
+// "18" is the default mod ID for version 2.2
+#define MODID 18
 
 // The Modification Version, starting from 1. Do not follow your version string for this,
 // it's only for detection of the version the player is using so the MS can alert them of an update.
 // Only set it higher, not lower, obviously.
-// Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
-#define MODVERSION 30
+// Note that we use this to help keep internal testing in check; this is why v2.2.0 is not version "1".
+#define MODVERSION 40
 
 // To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically.
 // Increment MINOREXECVERSION whenever a config change is needed that does not correspond
@@ -460,6 +464,8 @@ extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL;
 char *va(const char *format, ...) FUNCPRINTF;
 char *M_GetToken(const char *inputString);
 void M_UnGetToken(void);
+UINT32 M_GetTokenPos(void);
+void M_SetTokenPos(UINT32 newPos);
 char *sizeu1(size_t num);
 char *sizeu2(size_t num);
 char *sizeu3(size_t num);
@@ -623,13 +629,20 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 ///      	SRB2CB itself ported this from PrBoom+
 #define NEWCLIP
 
+/// Cache patches in Lua in a way that renderer switching will work flawlessly.
+//#define LUA_PATCH_SAFETY
+
 /// Sprite rotation
 #define ROTSPRITE
-#define ROTANGLES 24	// Needs to be a divisor of 360 (45, 60, 90, 120...)
+#define ROTANGLES 72 // Needs to be a divisor of 360 (45, 60, 90, 120...)
 #define ROTANGDIFF (360 / ROTANGLES)
 
+/// PNG support
 #ifndef HAVE_PNG
 #define NO_PNG_LUMPS
 #endif
 
+/// Render flats on walls
+#define WALLFLATS
+
 #endif // __DOOMDEF__
diff --git a/src/doomstat.h b/src/doomstat.h
index 877f5b50b72fda0b81838200562a88bea305c83e..6d1d6eb3665b95eceb0c58683086722d0f98b92f 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -39,7 +39,7 @@ extern UINT32 mapmusposition;
 #define MUSIC_FORCERESET  0x4000 // -*--------------
 // Use other bits if necessary.
 
-extern INT16 maptol;
+extern UINT32 maptol;
 extern UINT8 globalweather;
 extern INT32 curWeather;
 extern INT32 cursaveslot;
@@ -84,6 +84,9 @@ extern boolean addedtogame; // true after the server has added you
 extern boolean multiplayer;
 
 extern INT16 gametype;
+extern UINT32 gametyperules;
+extern INT16 gametypecount;
+
 extern boolean splitscreen;
 extern boolean circuitmap; // Does this level have 'circuit mode'?
 extern boolean fromlevelselect;
@@ -284,7 +287,7 @@ typedef struct
 	char lvlttl[22];       ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway)
 	char subttl[33];       ///< Subtitle for level
 	UINT8 actnum;          ///< Act number or 0 for none.
-	UINT16 typeoflevel;    ///< Combination of typeoflevel flags.
+	UINT32 typeoflevel;    ///< Combination of typeoflevel flags.
 	INT16 nextlevel;       ///< Map number of next level, or 1100-1102 to end.
 	char musname[7];       ///< Music track to play. "" for no music.
 	UINT16 mustrack;       ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
@@ -310,12 +313,17 @@ typedef struct
 	SINT8 bonustype;      ///< What type of bonus does this level have? (-1 for null.)
 	SINT8 maxbonuslives;  ///< How many bonus lives to award at Intermission? (-1 for unlimited.)
 
-	UINT8 levelflags;     ///< LF_flags:  merged booleans into one UINT8 for space, see below
+	UINT16 levelflags;     ///< LF_flags:  merged booleans into one UINT16 for space, see below
 	UINT8 menuflags;      ///< LF2_flags: options that affect record attack / nights mode menus
 
 	char selectheading[22]; ///< Level select heading. Allows for controllable grouping.
 	UINT16 startrings;      ///< Number of rings players start with.
 
+	// Title card.
+	char ltzzpatch[8];      ///< Zig zag patch.
+	char ltzztext[8];       ///< Zig zag text.
+	char ltactdiamond[8];   ///< Act diamond.
+
 	// Freed animals stuff.
 	UINT8 numFlickies;     ///< Internal. For freed flicky support.
 	mobjtype_t *flickies;  ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful.
@@ -361,6 +369,73 @@ typedef struct
 
 extern mapheader_t* mapheaderinfo[NUMMAPS];
 
+// Gametypes
+#define NUMGAMETYPEFREESLOTS 128
+enum GameType
+{
+	GT_COOP = 0, // also used in single player
+	GT_COMPETITION, // Classic "Race"
+	GT_RACE,
+
+	GT_MATCH,
+	GT_TEAMMATCH,
+
+	GT_TAG,
+	GT_HIDEANDSEEK,
+
+	GT_CTF, // capture the flag
+
+	GT_FIRSTFREESLOT,
+	GT_LASTFREESLOT = GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1,
+	NUMGAMETYPES
+};
+// If you alter this list, update dehacked.c, MISC_ChangeGameTypeMenu in m_menu.c, and Gametype_Names in g_game.c
+
+// Gametype rules
+enum GameTypeRules
+{
+	GTR_CAMPAIGN         = 1,     // Linear Co-op map progression, don't allow random maps
+	GTR_RINGSLINGER      = 1<<1,  // Outside of Co-op, Competition, and Race (overriden by cv_ringslinger)
+	GTR_SPECTATORS       = 1<<2,  // Outside of Co-op, Competition, and Race
+	GTR_LIVES            = 1<<3,  // Co-op and Competition
+	GTR_TEAMS            = 1<<4,  // Team Match, CTF
+	GTR_FIRSTPERSON      = 1<<5,  // First person camera
+	GTR_POWERSTONES      = 1<<6,  // Power stones (Match and CTF)
+	GTR_TEAMFLAGS        = 1<<7,  // Gametype has team flags (CTF)
+	GTR_FRIENDLY         = 1<<8,  // Co-op
+	GTR_SPECIALSTAGES    = 1<<9,  // Allow special stages
+	GTR_EMERALDTOKENS    = 1<<10, // Spawn emerald tokens
+	GTR_EMERALDHUNT      = 1<<11, // Emerald Hunt
+	GTR_RACE             = 1<<12, // Race and Competition
+	GTR_TAG              = 1<<13, // Tag and Hide and Seek
+	GTR_POINTLIMIT       = 1<<14, // Ringslinger point limit
+	GTR_TIMELIMIT        = 1<<15, // Ringslinger time limit
+	GTR_OVERTIME         = 1<<16, // Allow overtime
+	GTR_HURTMESSAGES     = 1<<17, // Hit and death messages
+	GTR_FRIENDLYFIRE     = 1<<18, // Always allow friendly fire
+	GTR_STARTCOUNTDOWN   = 1<<19, // Hide time countdown (Tag and Hide and Seek)
+	GTR_HIDEFROZEN       = 1<<20, // Frozen after hide time (Hide and Seek, but not Tag)
+	GTR_BLINDFOLDED      = 1<<21, // Blindfolded view (Tag and Hide and Seek)
+	GTR_RESPAWNDELAY     = 1<<22, // Respawn delay
+	GTR_PITYSHIELD       = 1<<23, // Award pity shield
+	GTR_DEATHPENALTY     = 1<<24, // Death score penalty
+	GTR_NOSPECTATORSPAWN = 1<<25, // Use with GTR_SPECTATORS, spawn in the map instead of with the spectators
+	GTR_DEATHMATCHSTARTS = 1<<26, // Use deathmatch starts
+	GTR_SPAWNINVUL       = 1<<27, // Babysitting deterrent
+	GTR_SPAWNENEMIES     = 1<<28, // Spawn enemies
+	GTR_ALLOWEXIT        = 1<<29, // Allow exit sectors
+	GTR_NOTITLECARD      = 1<<30, // Don't show the title card
+	GTR_CUTSCENES        = 1<<31, // Play cutscenes, ending, credits, and evaluation
+};
+
+// String names for gametypes
+extern const char *Gametype_Names[NUMGAMETYPES];
+extern const char *Gametype_ConstantNames[NUMGAMETYPES];
+
+// Point and time limits for every gametype
+extern INT32 pointlimits[NUMGAMETYPES];
+extern INT32 timelimits[NUMGAMETYPES];
+
 enum TypeOfLevel
 {
 	TOL_SP          = 0x01, ///< Single Player
@@ -376,36 +451,26 @@ enum TypeOfLevel
 	TOL_CTF         = 0x40, ///< Capture the Flag
 // CTF default = 64
 
-	TOL_CUSTOM      = 0x80, ///< Custom (Lua-scripted, etc.)
+	// 0x80 was here
 
 	TOL_2D     = 0x0100, ///< 2D
 	TOL_MARIO  = 0x0200, ///< Mario
 	TOL_NIGHTS = 0x0400, ///< NiGHTS
 	TOL_ERZ3   = 0x0800, ///< ERZ3
-	TOL_XMAS   = 0x1000  ///< Christmas NiGHTS
+	TOL_XMAS   = 0x1000, ///< Christmas NiGHTS
 };
 
-// Gametypes
-enum GameType
-{
-	GT_COOP = 0, // also used in single player
-	GT_COMPETITION, // Classic "Race"
-	GT_RACE,
-
-	GT_MATCH,
-	GT_TEAMMATCH,
+#define NUMBASETOL 18
+#define NUMMAXTOL (18 + NUMGAMETYPEFREESLOTS)
 
-	GT_TAG,
-	GT_HIDEANDSEEK,
-
-	GT_CTF, // capture the flag
-
-	NUMGAMETYPES
-};
-// If you alter this list, update dehacked.c, MISC_ChangeGameTypeMenu in m_menu.c, and Gametype_Names in g_game.c
-
-// String names for gametypes
-extern const char *Gametype_Names[NUMGAMETYPES];
+typedef struct
+{
+	const char *name;
+	UINT32 flag;
+} tolinfo_t;
+extern tolinfo_t TYPEOFLEVEL[NUMMAXTOL];
+extern INT32 numtolinfo;
+extern UINT32 lastcustomtol;
 
 extern tic_t totalplaytime;
 
@@ -466,7 +531,8 @@ extern recorddata_t *mainrecords[NUMMAPS];
 #define MV_ALLEMERALDS  4
 #define MV_ULTIMATE     8
 #define MV_PERFECT     16
-#define MV_MAX         31 // used in gamedata check
+#define MV_PERFECTRA   32
+#define MV_MAX         63 // used in gamedata check, update whenever MV's are added
 #define MV_MP         128
 extern UINT8 mapvisited[NUMMAPS];
 
diff --git a/src/doomtype.h b/src/doomtype.h
index 746463cd296e624602165ef1006f7b451e010bbc..6365f851eaf1891191c9959339956c621dab8795 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index a3fe3077c2a46f6956a5723eb1de40127da7e798..5c0f7eb99ed6ab0d41fc93bcfd87fc89ab8d044b 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -150,6 +150,8 @@ void I_GetJoystick2Events(void){}
 
 void I_GetMouseEvents(void){}
 
+void I_UpdateMouseGrab(void){}
+
 char *I_GetEnv(const char *name)
 {
 	(void)name;
diff --git a/src/dummy/i_video.c b/src/dummy/i_video.c
index e167e833fb152d6fcc5daa7a490cede554944d3b..fafeee0001400ef868b6c8d91042870c6c2e3773 100644
--- a/src/dummy/i_video.c
+++ b/src/dummy/i_video.c
@@ -11,6 +11,7 @@ boolean allow_fullscreen = false;
 consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 void I_StartupGraphics(void){}
+void I_StartupHardwareGraphics(void){}
 
 void I_ShutdownGraphics(void){}
 
@@ -39,6 +40,11 @@ INT32 VID_SetMode(INT32 modenum)
 	return 0;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 const char *VID_GetModeName(INT32 modenum)
 {
 	(void)modenum;
diff --git a/src/endian.h b/src/endian.h
index d3c1cb18b4e6923cf101a64059266ed78326175a..76f3ef6e82bfd745585cd2fa3a620979f9285636 100644
--- a/src/endian.h
+++ b/src/endian.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2014-2018 by Sonic Team Junior.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/f_finale.c b/src/f_finale.c
index 2d20eb7f41face5c65cdff0edeae7e3955c5ea9b..317a57f3d385efdaa1795788bbf96390588b9ddf 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -305,7 +305,7 @@ static void F_NewCutscene(const char *basetext)
 // =============
 //  INTRO SCENE
 // =============
-#define NUMINTROSCENES 16
+#define NUMINTROSCENES 17
 INT32 intro_scenenum = 0;
 INT32 intro_curtime = 0;
 
@@ -325,7 +325,8 @@ static tic_t introscenetime[NUMINTROSCENES] =
 	16*TICRATE,					// Meanwhile, Sonic was tearing across the zones...
 	16*TICRATE + (TICRATE/2),	// Sonic knew he was getting closer to the city...
 	17*TICRATE,					// Greenflower City was gone...
-	16*TICRATE + (TICRATE/2),	// You're not quite as dead as we thought, huh?...
+	 7*TICRATE,					// You're not quite as dead as we thought, huh?...
+	 8*TICRATE,					// We'll see... let's give you a quick warm up...
 	18*TICRATE + (TICRATE/2),	// Eggman took this as his cue and blasted off...
 	16*TICRATE,					// Easy! We go find Eggman and stop his...
 	25*TICRATE,					// I'm just finding what mission obje...
@@ -436,14 +437,22 @@ void F_StartIntro(void)
 	"had been obliterated.\n#");
 
 	introtext[12] = M_GetText(
-	"\xA7\"You're not quite as gone as we thought,\n"
-	"huh?\xBF Are you going to tell us your plan as\n"
-	"usual or will I \xA8\xB4'have to work it out'\xA7 or\n"
-	"something?\"\xD2\xD2\n"
+	"\xA7\"You're not\n"
+	"quite as gone\n"
+	"as we thought,\n"
+	"huh?\xBF Are you\n"
+	"going to tell\n"
+	"us your plan as\n"
+	"usual or will I\n"
+	"\xA8\xB4'have to work\n"
+	"it out'\xA7 or\n"
+	"something?\"\xD2\xD2\n#");
+
+	introtext[13] = M_GetText(
 	"\"We'll see\xAA...\xA7\xBF let's give you a quick warm\n"
 	"up, Sonic!\xA6\xC4 JETTYSYNS!\xA7\xBD Open fire!\"\n#");
 
-	introtext[13] = M_GetText(
+	introtext[14] = M_GetText(
 	"Eggman took this\n"
 	"as his cue and\n"
 	"blasted off,\n"
@@ -457,7 +466,7 @@ void F_StartIntro(void)
 	"\xA7\"Now\xB6 what do we\n"
 	"do?\",\xA9 he asked.\n#");
 
-	introtext[14] = M_GetText(
+	introtext[15] = M_GetText(
 	"\xA7\"Easy!\xBF We go\n"
 	"find Eggman\n"
 	"and stop his\n"
@@ -471,7 +480,7 @@ void F_StartIntro(void)
 	"\xAA*ARE*\xA9 you\n"
 	"doing?\"\n#");
 
-	introtext[15] = M_GetText(
+	introtext[16] = M_GetText(
 	"\xA8\"I'm just finding what mission obje\xAC\xB1...\xBF\n"
 	"\xA6""a-\xB8""ha!\xBF Here it is!\xA8\xBF This will only give us\n"
 	"the robot's primary objective.\xBF It says\xAC\xB1...\"\n"
@@ -509,91 +518,93 @@ void F_StartIntro(void)
 //
 static void F_IntroDrawScene(void)
 {
-	boolean highres = false;
+	boolean highres = true;
 	INT32 cx = 8, cy = 128;
 	patch_t *background = NULL;
 	INT32 bgxoffs = 0;
 	void *patch;
 
 	// DRAW A FULL PIC INSTEAD OF FLAT!
-	if (intro_scenenum == 0);
-	else if (intro_scenenum == 1)
-		background = W_CachePatchName("INTRO1", PU_CACHE);
-	else if (intro_scenenum == 2)
-	{
-		background = W_CachePatchName("INTRO2", PU_CACHE);
-		highres = true;
-	}
-	else if (intro_scenenum == 3)
-		background = W_CachePatchName("INTRO3", PU_CACHE);
-	else if (intro_scenenum == 4)
-		background = W_CachePatchName("INTRO4", PU_CACHE);
-	else if (intro_scenenum == 5)
+	switch (intro_scenenum)
 	{
-		if (intro_curtime >= 5*TICRATE)
-			background = W_CachePatchName("RADAR", PU_CACHE);
-		else
+		case 0:
+			break;
+		case 1:
+			background = W_CachePatchName("INTRO1", PU_PATCH);
+			break;
+		case 2:
+			background = W_CachePatchName("INTRO2", PU_PATCH);
+			break;
+		case 3:
+			background = W_CachePatchName("INTRO3", PU_PATCH);
+			break;
+		case 4:
+			background = W_CachePatchName("INTRO4", PU_PATCH);
+			break;
+		case 5:
+			if (intro_curtime >= 5*TICRATE)
+				background = W_CachePatchName("RADAR", PU_PATCH);
+			else
+				background = W_CachePatchName("DRAT", PU_PATCH);
+			break;
+		case 6:
+			background = W_CachePatchName("INTRO6", PU_PATCH);
+			cx = 180;
+			cy = 8;
+			break;
+		case 7:
 		{
-			background = W_CachePatchName("DRAT", PU_CACHE);
-			highres = true;
+			if (intro_curtime >= 7*TICRATE + ((TICRATE/7)*2))
+				background = W_CachePatchName("SGRASS5", PU_PATCH);
+			else if (intro_curtime >= 7*TICRATE + (TICRATE/7))
+				background = W_CachePatchName("SGRASS4", PU_PATCH);
+			else if (intro_curtime >= 7*TICRATE)
+				background = W_CachePatchName("SGRASS3", PU_PATCH);
+			else if (intro_curtime >= 6*TICRATE)
+				background = W_CachePatchName("SGRASS2", PU_PATCH);
+			else
+				background = W_CachePatchName("SGRASS1", PU_PATCH);
+			break;
 		}
-	}
-	else if (intro_scenenum == 6)
-	{
-		background = W_CachePatchName("INTRO6", PU_CACHE);
-		cx = 180;
-		cy = 8;
-	}
-	else if (intro_scenenum == 7)
-	{
-		if (intro_curtime >= 6*TICRATE)
-			background = W_CachePatchName("SGRASS5", PU_CACHE);
-		else
-			background = W_CachePatchName("SGRASS1", PU_CACHE);
-	}
-	else if (intro_scenenum == 8)
-	{
-		background = W_CachePatchName("WATCHING", PU_CACHE);
-		highres = true;
-	}
-	else if (intro_scenenum == 9)
-	{
-		background = W_CachePatchName("ZOOMING", PU_CACHE);
-		highres = true;
-	}
-	else if (intro_scenenum == 10);
-	else if (intro_scenenum == 11)
-		background = W_CachePatchName("INTRO5", PU_CACHE);
-	else if (intro_scenenum == 12)
-	{
-		if (intro_curtime >= 7*TICRATE)
-			background = W_CachePatchName("CONFRONT", PU_CACHE);
-		else
-			background = W_CachePatchName("REVENGE", PU_CACHE);
-		highres = true;
-	}
-	else if (intro_scenenum == 13)
-	{
-		background = W_CachePatchName("TAILSSAD", PU_CACHE);
-		highres = true;
-		bgxoffs = 144;
-		cx = 8;
-		cy = 8;
-	}
-	else if (intro_scenenum == 14)
-	{
-		if (intro_curtime >= 7*TICRATE)
-			background = W_CachePatchName("SONICDO2", PU_CACHE);
-		else
-			background = W_CachePatchName("SONICDO1", PU_CACHE);
-		highres = true;
-		cx = 224;
-		cy = 8;
-	}
-	else if (intro_scenenum == 15)
-	{
-		background = W_CachePatchName("INTRO7", PU_CACHE);
-		highres = true;
+		case 8:
+			background = W_CachePatchName("WATCHING", PU_PATCH);
+			break;
+		case 9:
+			background = W_CachePatchName("ZOOMING", PU_PATCH);
+			break;
+		case 10:
+			break;
+		case 11:
+			background = W_CachePatchName("INTRO5", PU_PATCH);
+			break;
+		case 12:
+			background = W_CachePatchName("REVENGE", PU_PATCH);
+			cx = 208;
+			cy = 8;
+			break;
+		case 13:
+			background = W_CachePatchName("CONFRONT", PU_PATCH);
+			cy += 48;
+			break;
+		case 14:
+			background = W_CachePatchName("TAILSSAD", PU_PATCH);
+			bgxoffs = 144;
+			cx = 8;
+			cy = 8;
+			break;
+		case 15:
+			if (intro_curtime >= 7*TICRATE)
+				background = W_CachePatchName("SONICDO2", PU_PATCH);
+			else
+				background = W_CachePatchName("SONICDO1", PU_PATCH);
+			cx = 224;
+			cy = 8;
+			break;
+		case 16:
+			background = W_CachePatchName("INTRO7", PU_PATCH);
+			break;
+		default:
+			break;
 	}
 
 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
@@ -620,30 +631,30 @@ static void F_IntroDrawScene(void)
 					S_ChangeMusicInternal("_stjr", false);
 				x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(334<<FRACBITS, aspect)/2;
 				y = (BASEVIDHEIGHT<<FRACBITS)/2 - FixedMul(358<<FRACBITS, aspect)/2;
-				V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH1", PU_CACHE)), aspect);
+				V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH1", PU_PATCH)), aspect);
 				W_UnlockCachedPatch(patch);
 				if (finalecount > 6) {
-					V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH2", PU_CACHE)), aspect);
+					V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH2", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 				}
 				if (finalecount > 10) {
-					V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH3", PU_CACHE)), aspect);
+					V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH3", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 				}
 				if (finalecount > 14) {
-					V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH4", PU_CACHE)), aspect);
+					V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH4", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 				}
 			}
 			else if (finalecount-30 < 20) { // Big eggy
-				background = W_CachePatchName("FEEDIN", PU_CACHE);
+				background = W_CachePatchName("FEEDIN", PU_PATCH);
 				x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(560<<FRACBITS, aspect)/2;
 				y = (BASEVIDHEIGHT<<FRACBITS) - FixedMul(477<<FRACBITS, aspect);
 				V_DrawSciencePatch(x, y, V_SNAPTOBOTTOM, background, aspect);
 			}
 			else if (finalecount-50 < 30) { // Zoom out
 				fixed_t scale = FixedDiv(aspect, FixedDiv((finalecount-50)<<FRACBITS, (15<<FRACBITS))+FRACUNIT);
-				background = W_CachePatchName("FEEDIN", PU_CACHE);
+				background = W_CachePatchName("FEEDIN", PU_PATCH);
 				x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(560<<FRACBITS, aspect)/2 + (FixedMul(560<<FRACBITS, aspect) - FixedMul(560<<FRACBITS, scale));
 				y = (BASEVIDHEIGHT<<FRACBITS) - FixedMul(477<<FRACBITS, scale);
 				V_DrawSciencePatch(x, y, V_SNAPTOBOTTOM, background, scale);
@@ -653,7 +664,7 @@ static void F_IntroDrawScene(void)
 				{
 					// Draw tiny eggy
 					fixed_t scale = FixedMul(FRACUNIT/3, aspect);
-					background = W_CachePatchName("FEEDIN", PU_CACHE);
+					background = W_CachePatchName("FEEDIN", PU_PATCH);
 					x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(560<<FRACBITS, aspect)/2 + (FixedMul(560<<FRACBITS, aspect) - FixedMul(560<<FRACBITS, scale));
 					y = (BASEVIDHEIGHT<<FRACBITS) - FixedMul(477<<FRACBITS, scale);
 					V_DrawSciencePatch(x, y, V_SNAPTOBOTTOM, background, scale);
@@ -664,34 +675,34 @@ static void F_IntroDrawScene(void)
 					x = (-189*FRACUNIT) + (FixedMul((6<<FRACBITS)+FRACUNIT/3, ftime<<FRACBITS) - FixedMul((6<<FRACBITS)+FRACUNIT/3, FixedDiv(FixedMul(ftime<<FRACBITS, ftime<<FRACBITS), 120<<FRACBITS)));
 					y = (BASEVIDHEIGHT<<FRACBITS) - FixedMul(417<<FRACBITS, aspect);
 					// Draw the body
-					V_DrawSciencePatch(x, y, V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT1", PU_CACHE)), aspect);
+					V_DrawSciencePatch(x, y, V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT1", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 					// Draw the door
-					V_DrawSciencePatch(x+FixedMul(344<<FRACBITS, aspect), y+FixedMul(292<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT2", PU_CACHE)), aspect);
+					V_DrawSciencePatch(x+FixedMul(344<<FRACBITS, aspect), y+FixedMul(292<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT2", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 					// Draw the wheel
-					V_DrawSciencePatch(x+FixedMul(178<<FRACBITS, aspect), y+FixedMul(344<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName(va("TYRE%02u",(abs(finalecount-144)/3)%16), PU_CACHE)), aspect);
+					V_DrawSciencePatch(x+FixedMul(178<<FRACBITS, aspect), y+FixedMul(344<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName(va("TYRE%02u",(abs(finalecount-144)/3)%16), PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 					// Draw the wheel cover
-					V_DrawSciencePatch(x+FixedMul(88<<FRACBITS, aspect), y+FixedMul(238<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT3", PU_CACHE)), aspect);
+					V_DrawSciencePatch(x+FixedMul(88<<FRACBITS, aspect), y+FixedMul(238<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT3", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 				} else { // Pure Fat has stopped!
 					y = (BASEVIDHEIGHT<<FRACBITS) - FixedMul(417<<FRACBITS, aspect);
 					// Draw the body
-					V_DrawSciencePatch(0, y, V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT1", PU_CACHE)), aspect);
+					V_DrawSciencePatch(0, y, V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT1", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 					// Draw the wheel
-					V_DrawSciencePatch(FixedMul(178<<FRACBITS, aspect), y+FixedMul(344<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("TYRE00", PU_CACHE)), aspect);
+					V_DrawSciencePatch(FixedMul(178<<FRACBITS, aspect), y+FixedMul(344<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("TYRE00", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 					// Draw the wheel cover
-					V_DrawSciencePatch(FixedMul(88<<FRACBITS, aspect), y+FixedMul(238<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT3", PU_CACHE)), aspect);
+					V_DrawSciencePatch(FixedMul(88<<FRACBITS, aspect), y+FixedMul(238<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT3", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 					// Draw the door
 					if (finalecount-TICRATE/2 > 4*TICRATE) { // Door is being raised!
 						int ftime = (finalecount-TICRATE/2-4*TICRATE);
 						y -= FixedDiv((ftime*ftime)<<FRACBITS, 23<<FRACBITS);
 					}
-					V_DrawSciencePatch(FixedMul(344<<FRACBITS, aspect), y+FixedMul(292<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT2", PU_CACHE)), aspect);
+					V_DrawSciencePatch(FixedMul(344<<FRACBITS, aspect), y+FixedMul(292<<FRACBITS, aspect), V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT2", PU_PATCH)), aspect);
 					W_UnlockCachedPatch(patch);
 				}
 			}
@@ -705,78 +716,137 @@ static void F_IntroDrawScene(void)
 		if (timetonext > 5*TICRATE && timetonext < 6*TICRATE)
 		{
 			if (!(finalecount & 3))
-				background = W_CachePatchName("BRITEGG1", PU_CACHE);
+				background = W_CachePatchName("BRITEGG1", PU_PATCH);
 			else
-				background = W_CachePatchName("DARKEGG1", PU_CACHE);
+				background = W_CachePatchName("DARKEGG1", PU_PATCH);
 
-			V_DrawScaledPatch(0, 0, 0, background);
+			V_DrawSmallScaledPatch(0, 0, 0, background);
 		}
 		else if (timetonext > 3*TICRATE && timetonext < 4*TICRATE)
 		{
 			if (!(finalecount & 3))
-				background = W_CachePatchName("BRITEGG2", PU_CACHE);
+				background = W_CachePatchName("BRITEGG2", PU_PATCH);
 			else
-				background = W_CachePatchName("DARKEGG2", PU_CACHE);
+				background = W_CachePatchName("DARKEGG2", PU_PATCH);
 
-			V_DrawScaledPatch(0, 0, 0, background);
+			V_DrawSmallScaledPatch(0, 0, 0, background);
 		}
 		else if (timetonext > 1*TICRATE && timetonext < 2*TICRATE)
 		{
 			if (!(finalecount & 3))
-				background = W_CachePatchName("BRITEGG3", PU_CACHE);
+				background = W_CachePatchName("BRITEGG3", PU_PATCH);
 			else
-				background = W_CachePatchName("DARKEGG3", PU_CACHE);
+				background = W_CachePatchName("DARKEGG3", PU_PATCH);
 
-			V_DrawScaledPatch(0, 0, 0, background);
+			V_DrawSmallScaledPatch(0, 0, 0, background);
 		}
 		else
 		{
-			menuanimtimer = animtimer; // Reusing this variable for the intro to fix the scrolling sky, better than changing the function around.
-			F_SkyScroll(80*4, 0, "TITLESKY");
-			if (timetonext == 6)
-			{
-				stoptimer = finalecount;
-				animtimer = finalecount % 16;
-			}
-			else if (timetonext >= 0 && timetonext < 6)
+			tic_t sonicdelay = max(0, timetonext - 16*TICRATE);
+			tic_t tailsdelay = max(0, timetonext - (9*TICRATE >> 1));
+			tic_t knucklesdelay = max(0, timetonext - (5*TICRATE >> 1));
+			INT32 sonicx = (timetonext >> 2) + min(sonicdelay, TICRATE >> 1) * sonicdelay;
+			INT32 tailsx = 32 + min(tailsdelay, TICRATE >> 1) * tailsdelay;
+			INT32 knucklesx = 96 + min(knucklesdelay, TICRATE >> 1) * knucklesdelay;
+			INT32 tailsy = 12 + P_ReturnThrustX(NULL, finalecount * ANGLE_22h, 2);
+			INT32 knucklesy = 48 - (timetonext >> 3);
+			INT32 skyx, grassx;
+
+			if (timetonext >= 0 && timetonext < 18)
 			{
-				animtimer = stoptimer;
-				deplete -= 32;
+				deplete -= 16;
 			}
 			else
 			{
-				animtimer = finalecount % 16;
-				deplete = 160;
+				stoptimer = finalecount;
+				deplete = 96;
 			}
+			skyx = 2 * stoptimer % 320;
+			grassx = 16 * stoptimer % 320;
+			sonicx += deplete;
+			tailsx += sonicx;
+			knucklesx += sonicx;
+			sonicx += P_ReturnThrustX(NULL, finalecount * ANG10, 3);
+
+			V_DrawSmallScaledPatch(skyx, 0, 0, (patch = W_CachePatchName("INTROSKY", PU_PATCH)));
+			V_DrawSmallScaledPatch(skyx - 320, 0, 0, patch);
+			W_UnlockCachedPatch(patch);
+			V_DrawSmallScaledPatch(grassx, 0, 0, (patch = W_CachePatchName("INTROGRS", PU_PATCH)));
+			V_DrawSmallScaledPatch(grassx - 320, 0, 0, patch);
+			W_UnlockCachedPatch(patch);
 
 			if (finalecount & 1)
 			{
-				V_DrawScaledPatch(deplete, 8, 0, (patch = W_CachePatchName("RUN2", PU_CACHE)));
+				// Sonic
+				V_DrawSmallScaledPatch(sonicx, 54, 0, (patch = W_CachePatchName("RUN2", PU_PATCH)));
 				W_UnlockCachedPatch(patch);
-				V_DrawScaledPatch(deplete, 72, 0, (patch = W_CachePatchName("PEELOUT2", PU_CACHE)));
+
+				// Appendages
+				if (finalecount & 2)
+				{
+					// Sonic's feet
+					V_DrawSmallScaledPatch(sonicx - 8, 92, 0, (patch = W_CachePatchName("PEELOUT4", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
+					// Tails' tails
+					V_DrawSmallScaledPatch(tailsx, tailsy, 0, (patch = W_CachePatchName("HELICOP2", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
+				}
+				else
+				{
+					// Sonic's feet
+					V_DrawSmallScaledPatch(sonicx - 8, 92, 0, (patch = W_CachePatchName("PEELOUT2", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
+					// Tails' tails
+					V_DrawSmallScaledPatch(tailsx, tailsy, 0, (patch = W_CachePatchName("HELICOP1", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
+				}
+
+				// Tails
+				V_DrawSmallScaledPatch(tailsx, tailsy, 0, (patch = W_CachePatchName("FLY2", PU_PATCH)));
+				W_UnlockCachedPatch(patch);
+
+				// Knuckles
+				V_DrawSmallScaledPatch(knucklesx, knucklesy, 0, (patch = W_CachePatchName("GLIDE2", PU_PATCH)));
 				W_UnlockCachedPatch(patch);
 			}
 			else
 			{
-				V_DrawScaledPatch(deplete, 8, 0, (patch = W_CachePatchName("RUN1", PU_CACHE)));
-				W_UnlockCachedPatch(patch);
-				V_DrawScaledPatch(deplete, 72, 0, (patch = W_CachePatchName("PEELOUT1", PU_CACHE)));
+				// Sonic
+				V_DrawSmallScaledPatch(sonicx, 54, 0, (patch = W_CachePatchName("RUN1", PU_PATCH)));
 				W_UnlockCachedPatch(patch);
-			}
 
-			{ // Fixing up the black box rendering to look right in resolutions <16:10 -Red
-				INT32 y = 112;
-				INT32 h = BASEVIDHEIGHT - 112;
-				if (vid.height != BASEVIDHEIGHT * vid.dupy)
+				// Appendages
+				if (finalecount & 2)
 				{
-					INT32 adjust = (vid.height/vid.dupy)-200;
-					adjust /= 2;
-					y += adjust;
-					h += adjust;
-					V_DrawFill(0, 0, BASEVIDWIDTH, adjust, 31); // Render a black bar on top so it keeps the "cinematic" windowboxing... I just prefer it this way. -Red
+					// Sonic's feet
+					V_DrawSmallScaledPatch(sonicx - 8, 92, 0, (patch = W_CachePatchName("PEELOUT3", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
+					// Tails' tails
+					V_DrawSmallScaledPatch(tailsx, tailsy, 0, (patch = W_CachePatchName("HELICOP2", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
 				}
-				V_DrawFill(0, y, BASEVIDWIDTH, h, 31);
+				else
+				{
+					// Sonic's feet
+					V_DrawSmallScaledPatch(sonicx - 8, 92, 0, (patch = W_CachePatchName("PEELOUT1", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
+					// Tails' tails
+					V_DrawSmallScaledPatch(tailsx, tailsy, 0, (patch = W_CachePatchName("HELICOP1", PU_PATCH)));
+					W_UnlockCachedPatch(patch);
+				}
+
+				// Tails
+				V_DrawSmallScaledPatch(tailsx, tailsy, 0, (patch = W_CachePatchName("FLY1", PU_PATCH)));
+				W_UnlockCachedPatch(patch);
+
+				// Knuckles
+				V_DrawSmallScaledPatch(knucklesx, knucklesy, 0, (patch = W_CachePatchName("GLIDE1", PU_PATCH)));
+				W_UnlockCachedPatch(patch);
 			}
+
+			// Black bars to hide the sky on widescreen
+			V_DrawFill(-80, 0, 80, 256, 31);
+			V_DrawFill(BASEVIDWIDTH, 0, 80, 256, 31);
 		}
 	}
 
@@ -803,8 +873,8 @@ static void F_IntroDrawScene(void)
 				y += (30*(FRACUNIT-scale));
 			}
 
-			rockpat = W_CachePatchName(va("ROID00%.2d", 34 - (worktics % 35)), PU_LEVEL);
-			glow = W_CachePatchName(va("ENDGLOW%.1d", 2+(worktics & 1)), PU_LEVEL);
+			rockpat = W_CachePatchName(va("ROID00%.2d", 34 - (worktics % 35)), PU_PATCH);
+			glow = W_CachePatchName(va("ENDGLOW%.1d", 2+(worktics & 1)), PU_PATCH);
 
 			if (worktics >= 5)
 				trans = (worktics-5)>>1;
@@ -822,25 +892,17 @@ static void F_IntroDrawScene(void)
 				V_DrawFixedPatch(x, y, scale, trans<<V_ALPHASHIFT, rockpat, R_GetTranslationColormap(TC_BLINK, SKINCOLOR_AQUA, GTC_CACHE));
 		}
 	}
+	else if (intro_scenenum == 1 && intro_curtime < 5*TICRATE)
+	{
+		INT32 trans = intro_curtime + 10 - (5*TICRATE);
+		if (trans < 0)
+			trans = 0;
+		V_DrawRightAlignedString(BASEVIDWIDTH-4, BASEVIDHEIGHT-12, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), "\x86""Press ""\x82""ENTER""\x86"" to skip...");
+	}
 
 	if (animtimer)
 		animtimer--;
 
-	if (intro_scenenum == 7 && intro_curtime > 7*TICRATE)
-	{
-		patch_t *sgrass;
-
-		if (intro_curtime >= 7*TICRATE + ((TICRATE/7)*2))
-			sgrass = W_CachePatchName("SGRASS4", PU_CACHE);
-		else if (intro_curtime >= 7*TICRATE + (TICRATE/7))
-			sgrass = W_CachePatchName("SGRASS3", PU_CACHE);
-		else
-			sgrass = W_CachePatchName("SGRASS2", PU_CACHE);
-		V_DrawScaledPatch(123, 4, 0, sgrass);
-
-		W_UnlockCachedPatch(sgrass);
-	}
-
 	V_DrawString(cx, cy, V_ALLOWLOWERCASE, cutscene_disptext);
 }
 
@@ -855,8 +917,9 @@ void F_IntroDrawer(void)
 		{
 			if (rendermode != render_none)
 			{
+				wipestyleflags = WSF_FADEOUT;
 				F_WipeStartScreen();
-				F_WipeColorFill(31);
+				F_TryColormapFade(31);
 				F_WipeEndScreen();
 				F_RunWipe(99,true);
 			}
@@ -865,22 +928,22 @@ void F_IntroDrawer(void)
 		}
 		else if (intro_scenenum == 10)
 		{
-			// The only fade to white in the entire damn game.
-			// (not true)
 			if (rendermode != render_none)
 			{
+				wipestyleflags = (WSF_FADEOUT|WSF_TOWHITE);
 				F_WipeStartScreen();
-				F_WipeColorFill(0);
+				F_TryColormapFade(0);
 				F_WipeEndScreen();
 				F_RunWipe(99,true);
 			}
 		}
-		else if (intro_scenenum == 15)
+		else if (intro_scenenum == 16)
 		{
 			if (rendermode != render_none)
 			{
+				wipestyleflags = WSF_FADEOUT;
 				F_WipeStartScreen();
-				F_WipeColorFill(31);
+				F_TryColormapFade(31);
 				F_WipeEndScreen();
 				F_RunWipe(99,true);
 			}
@@ -915,6 +978,7 @@ void F_IntroDrawer(void)
 
 		F_WipeStartScreen();
 		wipegamestate = -1;
+		wipestyleflags = WSF_CROSSFADE;
 		animtimer = stoptimer = 0;
 	}
 
@@ -924,11 +988,11 @@ void F_IntroDrawer(void)
 	{
 		if (intro_scenenum == 5 && intro_curtime == 5*TICRATE)
 		{
-			patch_t *radar = W_CachePatchName("RADAR", PU_CACHE);
+			patch_t *radar = W_CachePatchName("RADAR", PU_PATCH);
 
 			F_WipeStartScreen();
 			F_WipeColorFill(31);
-			V_DrawScaledPatch(0, 0, 0, radar);
+			V_DrawSmallScaledPatch(0, 0, 0, radar);
 			W_UnlockCachedPatch(radar);
 			V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext);
 
@@ -937,20 +1001,20 @@ void F_IntroDrawer(void)
 		}
 		else if (intro_scenenum == 7 && intro_curtime == 6*TICRATE) // Force a wipe here
 		{
-			patch_t *grass = W_CachePatchName("SGRASS5", PU_CACHE);
+			patch_t *grass = W_CachePatchName("SGRASS2", PU_PATCH);
 
 			F_WipeStartScreen();
 			F_WipeColorFill(31);
-			V_DrawScaledPatch(0, 0, 0, grass);
+			V_DrawSmallScaledPatch(0, 0, 0, grass);
 			W_UnlockCachedPatch(grass);
 			V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext);
 
 			F_WipeEndScreen();
 			F_RunWipe(99,true);
 		}
-		else if (intro_scenenum == 12 && intro_curtime == 7*TICRATE)
+		/*else if (intro_scenenum == 12 && intro_curtime == 7*TICRATE)
 		{
-			patch_t *confront = W_CachePatchName("CONFRONT", PU_CACHE);
+			patch_t *confront = W_CachePatchName("CONFRONT", PU_PATCH);
 
 			F_WipeStartScreen();
 			F_WipeColorFill(31);
@@ -960,10 +1024,10 @@ void F_IntroDrawer(void)
 
 			F_WipeEndScreen();
 			F_RunWipe(99,true);
-		}
-		if (intro_scenenum == 14 && intro_curtime == 7*TICRATE)
+		}*/
+		if (intro_scenenum == 15 && intro_curtime == 7*TICRATE)
 		{
-			patch_t *sdo = W_CachePatchName("SONICDO2", PU_CACHE);
+			patch_t *sdo = W_CachePatchName("SONICDO2", PU_PATCH);
 
 			F_WipeStartScreen();
 			F_WipeColorFill(31);
@@ -1066,16 +1130,22 @@ static const char *credits[] = {
 	"Logan \"GBA\" Arias",
 	"Callum Dickinson",
 	"Scott \"Graue\" Feeney",
+	"Victor \"SteelT\" Fuentes",
 	"Nathan \"Jazz\" Giroux",
 	"Vivian \"toaster\" Grannell",
 	"Kepa \"Nev3r\" Iceta",
 	"Thomas \"Shadow Hog\" Igoe",
+	"\"james\"",
 	"Iestyn \"Monster Iestyn\" Jealous",
+	"\"Jimita\"",
 	"Ronald \"Furyhunter\" Kinard", // The SDL2 port
+	"Louis-Antoine \"LJ Sonic\" de Moulins", // de Rochefort doesn't quite fit on the screen sorry lol
 	"John \"JTE\" Muniz",
 	"Ehab \"Wolfy\" Saeed",
+	"Jonas \"MascaraSnake\" Sauer",
 	"\"Kaito Sinclaire\"",
 	"\"SSNTails\"",
+	"Lachlan \"Lach\" Wright",
 	"Marco \"mazmazz\" Zafra",
 	"",
 	"\1Programming",
@@ -1084,10 +1154,7 @@ static const char *credits[] = {
 	"Andrew \"orospakr\" Clunis",
 	"Sally \"TehRealSalt\" Cochenour",
 	"Gregor \"Oogaland\" Dick",
-	"Louis-Antoine \"LJSonic\" de Moulins", // for fixing 2.1's netcode (de Rochefort doesn't quite fit on the screen sorry lol)
-	"Victor \"Steel Titanium\" Fuentes",
 	"Julio \"Chaos Zero 64\" Guir",
-	"\"Jimita\"",
 	"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
 	"\"Lat'\"", // SRB2-CHAT, the chat window from Kart
 	"Matthew \"Shuffle\" Marsalko",
@@ -1096,39 +1163,53 @@ static const char *credits[] = {
 	"Colin \"Sonict\" Pfaff",
 	"Sean \"Sryder13\" Ryder",
 	"Tasos \"tatokis\" Sahanidis", // Corrected C FixedMul, making 64-bit builds netplay compatible
+	"Wessel \"sphere\" Smit",
 	"Ben \"Cue\" Woodford",
 	// Git contributors with 5+ approved merges of substantive quality,
 	// or contributors with at least one groundbreaking merge, may be named.
 	// Everyone else is acknowledged under "Special Thanks > SRB2 Community Contributors".
 	"",
-	"\1Sprite Artists",
-	"\"Iceman404\"",
+	"\1Art",
 	"Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D:
+	"Ryan \"Blaze Hedgehog\" Bloom",
+	"Paul \"Boinciel\" Clempson",
 	"Sally \"TehRealSalt\" Cochenour",
-	"Jim \"MotorRoach\" DeMello",
+	"\"Dave Lite\"",
 	"Desmond \"Blade\" DesJardins",
 	"Sherman \"CoatRack\" DesJardins",
+	"\"DirkTheHusky\"",
+	"Buddy \"KinkaJoy\" Fischer",
 	"Vivian \"toaster\" Grannell",
+	"James \"SwitchKaze\" Hale",
+	"James \"SeventhSentinel\" Hall",
+	"Kepa \"Nev3r\" Iceta",
+	"Iestyn \"Monster Iestyn\" Jealous",
+	"William \"GuyWithThePie\" Kloppenberg",
+	"Alice \"Alacroix\" de Lemos",
+	"Alexander \"DrTapeworm\" Moench-Ford",
 	"Andrew \"Senku Niola\" Moran",
+	"\"MotorRoach\"",
+	"Phillip \"TelosTurntable\" Robinson",
+	"Wessel \"sphere\" Smit",
 	"David \"Instant Sonic\" Spencer Jr.",
 	"\"SSNTails\"",
-	"",
-	"\1Texture Artists",
-	"Ryan \"Blaze Hedgehog\" Bloom",
-	"Buddy \"KinkaJoy\" Fischer",
-	"Vivian \"toaster\" Grannell",
-	"Kepa \"Nev3r\" Iceta",
+	"Daniel \"Inazuma\" Trinh",
+	"\"VelocitOni\"",
 	"Jarrett \"JEV3\" Voight",
 	"",
 	"\1Music and Sound",
 	"\1Production",
+	"Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo
 	"Malcolm \"RedXVI\" Brown",
 	"Dave \"DemonTomatoDave\" Bulmer",
 	"Paul \"Boinciel\" Clempson",
+	"Shane \"CobaltBW\" Ellis",
+	"James \"SeventhSentinel\" Hall",
 	"Cyan Helkaraxe",
 	"Kepa \"Nev3r\" Iceta",
 	"Iestyn \"Monster Iestyn\" Jealous",
 	"Jarel \"Arrow\" Jones",
+	"Alexander \"DrTapeworm\" Moench-Ford",
 	"Stefan \"Stuf\" Rimalia",
 	"Shane Mychal Sexton",
 	"\"Spazzo\"",
@@ -1137,16 +1218,20 @@ static const char *credits[] = {
 	"\"SSNTails\"",
 	"",
 	"\1Level Design",
+	"Hank \"FuriousFox\" Brannock",
 	"Matthew \"Fawfulfan\" Chapman",
 	"Paul \"Boinciel\" Clempson",
+	"Sally \"TehRealSalt\" Cochenour",
 	"Desmond \"Blade\" DesJardins",
 	"Sherman \"CoatRack\" DesJardins",
 	"Ben \"Mystic\" Geyer",
 	"Nathan \"Jazz\" Giroux",
 	"Vivian \"toaster\" Grannell",
 	"Dan \"Blitzzo\" Hagerstrand",
+	"James \"SeventhSentinel\" Hall",
 	"Kepa \"Nev3r\" Iceta",
 	"Thomas \"Shadow Hog\" Igoe",
+	"Alexander \"DrTapeworm\" Moench-Ford",
 	"\"Kaito Sinclaire\"",
 	"Wessel \"sphere\" Smit",
 	"\"Spazzo\"",
@@ -1180,6 +1265,7 @@ static const char *credits[] = {
 	"iD Software",
 	"Doom Legacy Project",
 	"FreeDoom Project", // Used some of the mancubus and rocket launcher sprites for Brak
+	"Kart Krew",
 	"Alex \"MistaED\" Fuller",
 	"Pascal \"CodeImp\" vd Heiden", // Doom Builder developer
 	"Randi Heit (<!>)", // For their MSPaint <!> sprite that we nicked
@@ -1190,6 +1276,7 @@ static const char *credits[] = {
 	// Creators of small quantities of sprite/texture assets
 	// - Arietty - New Green Hill-styled textures
 	// - Scizor300 - the only other contributor to the 2.0 SRB2 Asset Pack
+	// - Revan/Icefox - the new Nimbus Ruins skybox
 	"SRB2 Community Contributors",
 	"",
 	"\1Produced By",
@@ -1198,30 +1285,41 @@ static const char *credits[] = {
 	"\1Published By",
 	"A 28K dialup modem",
 	"",
-	"\1Thank you",
-	"\1for playing!",
+	"\1Thank you       ",
+	"\1for playing!       ",
 	NULL
 };
 
+#define CREDITS_LEFT 8
+#define CREDITS_RIGHT ((BASEVIDWIDTH) - 8)
+
 static struct {
-	UINT32 x, y;
+	UINT32 x;
 	const char *patch;
 } credits_pics[] = {
-	{  8, 80+200* 1, "CREDIT01"},
-	{  4, 80+200* 2, "CREDIT13"},
-	{250, 80+200* 3, "CREDIT12"},
-	{  8, 80+200* 4, "CREDIT03"},
-	{248, 80+200* 5, "CREDIT11"},
-	{  8, 80+200* 6, "CREDIT04"},
-	{112, 80+200* 7, "CREDIT10"},
-	{240, 80+200* 8, "CREDIT05"},
-	{120, 80+200* 9, "CREDIT06"},
-	{  8, 80+200*10, "CREDIT07"},
-	{  8, 80+200*11, "CREDIT08"},
-	{112, 80+200*12, "CREDIT09"},
-	{0, 0, NULL}
+	{CREDITS_LEFT,                     "CREDIT01"},
+	{CREDITS_RIGHT - (271 >> 1),       "CREDIT02"},
+	{CREDITS_LEFT,                     "CREDIT03"},
+	{CREDITS_RIGHT - (316 >> 1),       "CREDIT04"},
+	{CREDITS_LEFT,                     "CREDIT05"},
+	{CREDITS_RIGHT - (399 >> 1),       "CREDIT06"},
+	{CREDITS_LEFT,                     "CREDIT07"},
+	{CREDITS_RIGHT - (302 >> 1),       "CREDIT08"},
+	{CREDITS_LEFT,                     "CREDIT09"},
+	{CREDITS_RIGHT - (250 >> 1),       "CREDIT10"},
+	{CREDITS_LEFT,                     "CREDIT11"},
+	{CREDITS_RIGHT - (279 >> 1),       "CREDIT12"},
+	//{(BASEVIDWIDTH - (279 >> 1)) >> 1, "CREDIT12"},
+	// CREDIT13 is extra art and is not shown by default
+	{0, NULL}
 };
 
+#undef CREDITS_LEFT
+#undef CREDITS_RIGHT
+
+static UINT32 credits_height = 0;
+static const UINT8 credits_numpics = sizeof(credits_pics)/sizeof(credits_pics[0]) - 1;
+
 void F_StartCredits(void)
 {
 	G_SetGamestate(GS_CREDITS);
@@ -1245,7 +1343,7 @@ void F_StartCredits(void)
 	S_StopMusic();
 	S_StopSounds();
 
-	S_ChangeMusicInternal("_creds", false);
+	S_ChangeMusicInternal("_creds", true);
 
 	finalecount = 0;
 	animtimer = 0;
@@ -1255,13 +1353,20 @@ void F_StartCredits(void)
 void F_CreditDrawer(void)
 {
 	UINT16 i;
-	fixed_t y = (80<<FRACBITS) - 5*(animtimer<<FRACBITS)/8;
+	INT16 zagpos = (timetonext - finalecount - animtimer) % 32;
+	fixed_t y = (80<<FRACBITS) - (animtimer<<FRACBITS>>1);
 
 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
+	// Zig Zagz
+	V_DrawScaledPatch(-16,               zagpos,       V_SNAPTOLEFT,         W_CachePatchName("LTZIGZAG", PU_PATCH));
+	V_DrawScaledPatch(-16,               zagpos - 320, V_SNAPTOLEFT,         W_CachePatchName("LTZIGZAG", PU_PATCH));
+	V_DrawScaledPatch(BASEVIDWIDTH + 16, zagpos,       V_SNAPTORIGHT|V_FLIP, W_CachePatchName("LTZIGZAG", PU_PATCH));
+	V_DrawScaledPatch(BASEVIDWIDTH + 16, zagpos - 320, V_SNAPTORIGHT|V_FLIP, W_CachePatchName("LTZIGZAG", PU_PATCH));
+
 	// Draw background pictures first
 	for (i = 0; credits_pics[i].patch; i++)
-		V_DrawSciencePatch(credits_pics[i].x<<FRACBITS, (credits_pics[i].y<<FRACBITS) - 4*(animtimer<<FRACBITS)/5, 0, W_CachePatchName(credits_pics[i].patch, PU_CACHE), FRACUNIT>>1);
+		V_DrawSciencePatch(credits_pics[i].x<<FRACBITS, (280<<FRACBITS) + (((i*credits_height)<<FRACBITS)/(credits_numpics)) - 4*(animtimer<<FRACBITS)/5, 0, W_CachePatchName(credits_pics[i].patch, PU_PATCH), FRACUNIT>>1);
 
 	// Dim the background
 	V_DrawFadeScreen(0xFF00, 16);
@@ -1279,6 +1384,11 @@ void F_CreditDrawer(void)
 				V_DrawCreditString((160 - (V_CreditStringWidth(&credits[i][1])>>1))<<FRACBITS, y, 0, &credits[i][1]);
 			y += 30<<FRACBITS;
 			break;
+		case 2:
+			if (y>>FRACBITS > -10)
+				V_DrawStringAtFixed((BASEVIDWIDTH-V_StringWidth(&credits[i][1], V_ALLOWLOWERCASE|V_YELLOWMAP))<<FRACBITS>>1, y, V_ALLOWLOWERCASE|V_YELLOWMAP, &credits[i][1]);
+			y += 12<<FRACBITS;
+			break;
 		default:
 			if (y>>FRACBITS > -10)
 				V_DrawStringAtFixed(32<<FRACBITS, y, V_ALLOWLOWERCASE, credits[i]);
@@ -1294,7 +1404,22 @@ void F_CreditTicker(void)
 {
 	// "Simulate" the drawing of the credits so that dedicated mode doesn't get stuck
 	UINT16 i;
-	fixed_t y = (80<<FRACBITS) - 5*(animtimer<<FRACBITS)/8;
+	fixed_t y = (80<<FRACBITS) - (animtimer<<FRACBITS>>1);
+
+	// Calculate credits height to display art properly
+	if (credits_height == 0)
+	{
+		for (i = 0; credits[i]; i++)
+		{
+			switch(credits[i][0])
+			{
+				case 0: credits_height += 80; break;
+				case 1: credits_height += 30; break;
+				default: credits_height += 12; break;
+			}
+		}
+		credits_height = 131*credits_height/80; // account for scroll speeds. This is a guess now, so you may need to update this if you change the credits length.
+	}
 
 	// Draw credits text on top
 	for (i = 0; credits[i]; i++)
@@ -1449,14 +1574,14 @@ void F_GameEvaluationDrawer(void)
 
 		if (goodending)
 		{
-			rockpat = W_CachePatchName(va("ROID00%.2d", 34 - (finalecount % 35)), PU_LEVEL);
-			glow = W_CachePatchName(va("ENDGLOW%.1d", 2+(finalecount & 1)), PU_LEVEL);
+			rockpat = W_CachePatchName(va("ROID00%.2d", 34 - (finalecount % 35)), PU_PATCH);
+			glow = W_CachePatchName(va("ENDGLOW%.1d", 2+(finalecount & 1)), PU_PATCH);
 			x -= FRACUNIT;
 		}
 		else
 		{
 			rockpat = W_CachePatchName("ROID0000", PU_LEVEL);
-			glow = W_CachePatchName(va("ENDGLOW%.1d", (finalecount & 1)), PU_LEVEL);
+			glow = W_CachePatchName(va("ENDGLOW%.1d", (finalecount & 1)), PU_PATCH);
 		}
 
 		if (finalecount >= 5)
@@ -1488,20 +1613,20 @@ void F_GameEvaluationDrawer(void)
 					// if j == 0 - alternate between 0 and 1
 					//         1 -                   1 and 2
 					//         2 -                   2 and not rendered
-					V_DrawFixedPatch(x+sparkloffs[j-1][0], y+sparkloffs[j-1][1], FRACUNIT, 0, W_CachePatchName(va("ENDSPKL%.1d", (j - ((sparklloop & 1) ? 0 : 1))), PU_LEVEL), R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_AQUA, GTC_CACHE));
+					V_DrawFixedPatch(x+sparkloffs[j-1][0], y+sparkloffs[j-1][1], FRACUNIT, 0, W_CachePatchName(va("ENDSPKL%.1d", (j - ((sparklloop & 1) ? 0 : 1))), PU_PATCH), R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_AQUA, GTC_CACHE));
 				}
 				j--;
 			}
 		}
 		else
 		{
-			patch_t *eggrock = W_CachePatchName("ENDEGRK5", PU_LEVEL);
+			patch_t *eggrock = W_CachePatchName("ENDEGRK5", PU_PATCH);
 			V_DrawFixedPatch(x, y, scale, 0, eggrock, colormap[0]);
 			if (trans < 10)
 				V_DrawFixedPatch(x, y, scale, trans<<V_ALPHASHIFT, eggrock, colormap[1]);
 			else if (sparklloop)
 				V_DrawFixedPatch(x, y, scale, (10-sparklloop)<<V_ALPHASHIFT,
-					W_CachePatchName("ENDEGRK0", PU_LEVEL), colormap[1]);
+					W_CachePatchName("ENDEGRK0", PU_PATCH), colormap[1]);
 		}
 	}
 
@@ -1515,7 +1640,7 @@ void F_GameEvaluationDrawer(void)
 		eemeralds_cur += (360<<FRACBITS)/7;
 
 		patchname[4] = 'A'+(char)i;
-		V_DrawFixedPatch(x, y, FRACUNIT, ((emeralds & (1<<i)) ? 0 : V_80TRANS), W_CachePatchName(patchname, PU_LEVEL), NULL);
+		V_DrawFixedPatch(x, y, FRACUNIT, ((emeralds & (1<<i)) ? 0 : V_80TRANS), W_CachePatchName(patchname, PU_PATCH), NULL);
 	}
 
 	V_DrawCreditString((BASEVIDWIDTH - V_CreditStringWidth(endingtext))<<(FRACBITS-1), (BASEVIDHEIGHT-100)<<(FRACBITS-1), 0, endingtext);
@@ -1625,57 +1750,37 @@ void F_GameEvaluationTicker(void)
 // ==========
 
 #define INFLECTIONPOINT (6*TICRATE)
+#define STOPPINGPOINT (14*TICRATE)
 #define SPARKLLOOPTIME 15 // must be odd
 
-void F_StartEnding(void)
+static void F_CacheEnding(void)
 {
-	G_SetGamestate(GS_ENDING);
-	wipetypepost = INT16_MAX;
-
-	// Just in case they're open ... somehow
-	M_ClearMenus(true);
-
-	// Save before the credits sequence.
-	if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
-		G_SaveGame((UINT32)cursaveslot);
-
-	gameaction = ga_nothing;
-	paused = false;
-	CON_ToggleOff();
-	S_StopMusic();
-	S_StopSounds();
+	endbrdr[1] = W_CachePatchName("ENDBRDR1", PU_PATCH);
 
-	finalecount = -10; // what? this totally isn't a hack. why are you asking?
-
-	memset(sparkloffs, 0, sizeof(INT32)*3*2);
-	sparklloop = 0;
+	endegrk[0] = W_CachePatchName("ENDEGRK0", PU_PATCH);
+	endegrk[1] = W_CachePatchName("ENDEGRK1", PU_PATCH);
 
-	endbrdr[1] = W_CachePatchName("ENDBRDR1", PU_LEVEL);
+	endglow[0] = W_CachePatchName("ENDGLOW0", PU_PATCH);
+	endglow[1] = W_CachePatchName("ENDGLOW1", PU_PATCH);
 
-	endegrk[0] = W_CachePatchName("ENDEGRK0", PU_LEVEL);
-	endegrk[1] = W_CachePatchName("ENDEGRK1", PU_LEVEL);
+	endbgsp[0] = W_CachePatchName("ENDBGSP0", PU_PATCH);
+	endbgsp[1] = W_CachePatchName("ENDBGSP1", PU_PATCH);
+	endbgsp[2] = W_CachePatchName("ENDBGSP2", PU_PATCH);
 
-	endglow[0] = W_CachePatchName("ENDGLOW0", PU_LEVEL);
-	endglow[1] = W_CachePatchName("ENDGLOW1", PU_LEVEL);
+	endspkl[0] = W_CachePatchName("ENDSPKL0", PU_PATCH);
+	endspkl[1] = W_CachePatchName("ENDSPKL1", PU_PATCH);
+	endspkl[2] = W_CachePatchName("ENDSPKL2", PU_PATCH);
 
-	endbgsp[0] = W_CachePatchName("ENDBGSP0", PU_LEVEL);
-	endbgsp[1] = W_CachePatchName("ENDBGSP1", PU_LEVEL);
-	endbgsp[2] = W_CachePatchName("ENDBGSP2", PU_LEVEL);
+	endxpld[0] = W_CachePatchName("ENDXPLD0", PU_PATCH);
+	endxpld[1] = W_CachePatchName("ENDXPLD1", PU_PATCH);
+	endxpld[2] = W_CachePatchName("ENDXPLD2", PU_PATCH);
+	endxpld[3] = W_CachePatchName("ENDXPLD3", PU_PATCH);
 
-	endspkl[0] = W_CachePatchName("ENDSPKL0", PU_LEVEL);
-	endspkl[1] = W_CachePatchName("ENDSPKL1", PU_LEVEL);
-	endspkl[2] = W_CachePatchName("ENDSPKL2", PU_LEVEL);
-
-	endxpld[0] = W_CachePatchName("ENDXPLD0", PU_LEVEL);
-	endxpld[1] = W_CachePatchName("ENDXPLD1", PU_LEVEL);
-	endxpld[2] = W_CachePatchName("ENDXPLD2", PU_LEVEL);
-	endxpld[3] = W_CachePatchName("ENDXPLD3", PU_LEVEL);
-
-	endescp[0] = W_CachePatchName("ENDESCP0", PU_LEVEL);
-	endescp[1] = W_CachePatchName("ENDESCP1", PU_LEVEL);
-	endescp[2] = W_CachePatchName("ENDESCP2", PU_LEVEL);
-	endescp[3] = W_CachePatchName("ENDESCP3", PU_LEVEL);
-	endescp[4] = W_CachePatchName("ENDESCP4", PU_LEVEL);
+	endescp[0] = W_CachePatchName("ENDESCP0", PU_PATCH);
+	endescp[1] = W_CachePatchName("ENDESCP1", PU_PATCH);
+	endescp[2] = W_CachePatchName("ENDESCP2", PU_PATCH);
+	endescp[3] = W_CachePatchName("ENDESCP3", PU_PATCH);
+	endescp[4] = W_CachePatchName("ENDESCP4", PU_PATCH);
 
 	// so we only need to check once
 	if ((goodending = ALL7EMERALDS(emeralds)))
@@ -1683,40 +1788,77 @@ void F_StartEnding(void)
 		UINT8 skinnum = players[consoleplayer].skin;
 		spritedef_t *sprdef;
 		spriteframe_t *sprframe;
-		if (skins[skinnum].sprites[SPR2_XTRA].numframes >= (XTRA_ENDING+2)+1)
+		if (skins[skinnum].sprites[SPR2_XTRA].numframes > (XTRA_ENDING+2))
 		{
 			sprdef = &skins[skinnum].sprites[SPR2_XTRA];
 			// character head, skin specific
 			sprframe = &sprdef->spriteframes[XTRA_ENDING];
-			endfwrk[0] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL);
+			endfwrk[0] = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 			sprframe = &sprdef->spriteframes[XTRA_ENDING+1];
-			endfwrk[1] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL);
+			endfwrk[1] = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 			sprframe = &sprdef->spriteframes[XTRA_ENDING+2];
-			endfwrk[2] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL);
+			endfwrk[2] = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 		}
 		else // Show a star if your character doesn't have an ending firework display. (Basically the MISSINGs for this)
 		{
-			endfwrk[0] = W_CachePatchName("ENDFWRK3", PU_LEVEL);
-			endfwrk[1] = W_CachePatchName("ENDFWRK4", PU_LEVEL);
-			endfwrk[2] = W_CachePatchName("ENDFWRK5", PU_LEVEL);
+			endfwrk[0] = W_CachePatchName("ENDFWRK3", PU_PATCH);
+			endfwrk[1] = W_CachePatchName("ENDFWRK4", PU_PATCH);
+			endfwrk[2] = W_CachePatchName("ENDFWRK5", PU_PATCH);
 		}
 
-		endbrdr[0] = W_CachePatchName("ENDBRDR2", PU_LEVEL);
+		endbrdr[0] = W_CachePatchName("ENDBRDR2", PU_PATCH);
 	}
 	else
 	{
 		// eggman, skin nonspecific
-		endfwrk[0] = W_CachePatchName("ENDFWRK0", PU_LEVEL);
-		endfwrk[1] = W_CachePatchName("ENDFWRK1", PU_LEVEL);
-		endfwrk[2] = W_CachePatchName("ENDFWRK2", PU_LEVEL);
+		endfwrk[0] = W_CachePatchName("ENDFWRK0", PU_PATCH);
+		endfwrk[1] = W_CachePatchName("ENDFWRK1", PU_PATCH);
+		endfwrk[2] = W_CachePatchName("ENDFWRK2", PU_PATCH);
 
 		endbrdr[0] = W_CachePatchName("ENDBRDR0", PU_LEVEL);
 	}
 }
 
+static void F_CacheGoodEnding(void)
+{
+	endegrk[0] = W_CachePatchName("ENDEGRK2", PU_PATCH);
+	endegrk[1] = W_CachePatchName("ENDEGRK3", PU_PATCH);
+
+	endglow[0] = W_CachePatchName("ENDGLOW2", PU_PATCH);
+	endglow[1] = W_CachePatchName("ENDGLOW3", PU_PATCH);
+
+	endxpld[0] = W_CachePatchName("ENDEGRK4", PU_PATCH);
+}
+
+void F_StartEnding(void)
+{
+	G_SetGamestate(GS_ENDING);
+	wipetypepost = INT16_MAX;
+
+	// Just in case they're open ... somehow
+	M_ClearMenus(true);
+
+	// Save before the credits sequence.
+	if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
+		G_SaveGame((UINT32)cursaveslot);
+
+	gameaction = ga_nothing;
+	paused = false;
+	CON_ToggleOff();
+	S_StopMusic(); // todo: placeholder
+	S_StopSounds();
+
+	finalecount = -10; // what? this totally isn't a hack. why are you asking?
+
+	memset(sparkloffs, 0, sizeof(INT32)*3*2);
+	sparklloop = 0;
+
+	F_CacheEnding();
+}
+
 void F_EndingTicker(void)
 {
-	if (++finalecount > INFLECTIONPOINT*2)
+	if (++finalecount > STOPPINGPOINT)
 	{
 		F_StartCredits();
 		wipetypepre = INT16_MAX;
@@ -1727,15 +1869,7 @@ void F_EndingTicker(void)
 		S_ChangeMusicInternal((goodending ? "_endg" : "_endb"), false);
 
 	if (goodending && finalecount == INFLECTIONPOINT) // time to swap some assets
-	{
-		endegrk[0] = W_CachePatchName("ENDEGRK2", PU_LEVEL);
-		endegrk[1] = W_CachePatchName("ENDEGRK3", PU_LEVEL);
-
-		endglow[0] = W_CachePatchName("ENDGLOW2", PU_LEVEL);
-		endglow[1] = W_CachePatchName("ENDGLOW3", PU_LEVEL);
-
-		endxpld[0] = W_CachePatchName("ENDEGRK4", PU_LEVEL);
-	}
+		F_CacheGoodEnding();
 
 	if (++sparklloop == SPARKLLOOPTIME) // time to roll the randomisation again
 	{
@@ -1754,10 +1888,17 @@ void F_EndingDrawer(void)
 	INT32 x, y, i, j, parallaxticker;
 	patch_t *rockpat;
 
+	if (needpatchrecache)
+	{
+		F_CacheEnding();
+		if (goodending && finalecount >= INFLECTIONPOINT) // time to swap some assets
+			F_CacheGoodEnding();
+	}
+
 	if (!goodending || finalecount < INFLECTIONPOINT)
-		rockpat = W_CachePatchName("ROID0000", PU_LEVEL);
+		rockpat = W_CachePatchName("ROID0000", PU_PATCH);
 	else
-		rockpat = W_CachePatchName(va("ROID00%.2d", 34 - ((finalecount - INFLECTIONPOINT) % 35)), PU_LEVEL);
+		rockpat = W_CachePatchName(va("ROID00%.2d", 34 - ((finalecount - INFLECTIONPOINT) % 35)), PU_PATCH);
 
 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
@@ -2112,26 +2253,26 @@ void F_EndingDrawer(void)
 
 		if (finalecount < 10)
 			trans = (10-finalecount)/2;
-		else if (finalecount > (2*INFLECTIONPOINT) - 20)
+		else if (finalecount > STOPPINGPOINT - 20)
 		{
-			trans = 10 + (finalecount/2) - INFLECTIONPOINT;
+			trans = 10 + (finalecount - STOPPINGPOINT)/2;
 			donttouch = true;
 		}
 
-		if (trans != 10)
+		if (trans < 10)
 		{
 			//colset(linkmap,  164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret
 			V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str);
 			V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false);
-			V_DrawString(40, ((finalecount == (2*INFLECTIONPOINT)-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= (2*INFLECTIONPOINT)-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
+			V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
 		}
 
-		if (finalecount > (2*INFLECTIONPOINT)-(20+(2*TICRATE)))
+		if (finalecount > STOPPINGPOINT-(20+(2*TICRATE)))
 		{
 			INT32 trans2 = abs((5*FINECOSINE((FixedAngle((finalecount*5)<<FRACBITS)>>ANGLETOFINESHIFT & FINEMASK)))>>FRACBITS)+2;
 			if (!donttouch)
 			{
-				trans = 10 + ((2*INFLECTIONPOINT)-(20+(2*TICRATE))) - finalecount;
+				trans = 10 + (STOPPINGPOINT-(20+(2*TICRATE))) - finalecount;
 				if (trans > trans2)
 					trans2 = trans;
 			}
@@ -2241,11 +2382,11 @@ void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname)
 
 	if (!scrollxspeed && !scrollyspeed)
 	{
-		V_DrawPatchFill(W_CachePatchName(patchname, PU_CACHE));
+		V_DrawPatchFill(W_CachePatchName(patchname, PU_PATCH));
 		return;
 	}
 
-	pat = W_CachePatchName(patchname, PU_CACHE);
+	pat = W_CachePatchName(patchname, PU_PATCH);
 
 	patwidth = SHORT(pat->width);
 	patheight = SHORT(pat->height);
@@ -2306,6 +2447,45 @@ else if (strlen(name) <= 6) \
 else \
 	arr[0] = 0;
 
+static void F_CacheTitleScreen(void)
+{
+	switch(curttmode)
+	{
+		case TTMODE_OLD:
+		case TTMODE_NONE:
+			ttbanner = W_CachePatchName("TTBANNER", PU_LEVEL);
+			ttwing = W_CachePatchName("TTWING", PU_LEVEL);
+			ttsonic = W_CachePatchName("TTSONIC", PU_LEVEL);
+			ttswave1 = W_CachePatchName("TTSWAVE1", PU_LEVEL);
+			ttswave2 = W_CachePatchName("TTSWAVE2", PU_LEVEL);
+			ttswip1 = W_CachePatchName("TTSWIP1", PU_LEVEL);
+			ttsprep1 = W_CachePatchName("TTSPREP1", PU_LEVEL);
+			ttsprep2 = W_CachePatchName("TTSPREP2", PU_LEVEL);
+			ttspop1 = W_CachePatchName("TTSPOP1", PU_LEVEL);
+			ttspop2 = W_CachePatchName("TTSPOP2", PU_LEVEL);
+			ttspop3 = W_CachePatchName("TTSPOP3", PU_LEVEL);
+			ttspop4 = W_CachePatchName("TTSPOP4", PU_LEVEL);
+			ttspop5 = W_CachePatchName("TTSPOP5", PU_LEVEL);
+			ttspop6 = W_CachePatchName("TTSPOP6", PU_LEVEL);
+			ttspop7 = W_CachePatchName("TTSPOP7", PU_LEVEL);
+			break;
+
+		// don't load alacroix gfx yet; we do that upon first draw.
+		case TTMODE_ALACROIX:
+			break;
+
+		case TTMODE_USER:
+		{
+			UINT16 i;
+			lumpnum_t lumpnum;
+			char lumpname[9];
+
+			LOADTTGFX(ttuser, curttname, TTMAX_USER)
+			break;
+		}
+	}
+}
+
 void F_StartTitleScreen(void)
 {
 	if (menupres[MN_MAIN].musname[0])
@@ -2367,7 +2547,7 @@ void F_StartTitleScreen(void)
 			camera.x = startpos->x << FRACBITS;
 			camera.y = startpos->y << FRACBITS;
 			camera.subsector = R_PointInSubsector(camera.x, camera.y);
-			camera.z = camera.subsector->sector->floorheight + ((startpos->options >> ZSHIFT) << FRACBITS);
+			camera.z = camera.subsector->sector->floorheight + (startpos->z << FRACBITS);
 			camera.angle = (startpos->angle % 360)*ANG1;
 			camera.aiming = 0;
 		}
@@ -2402,41 +2582,7 @@ void F_StartTitleScreen(void)
 	demoDelayLeft = demoDelayTime;
 	demoIdleLeft = demoIdleTime;
 
-	switch(curttmode)
-	{
-		case TTMODE_OLD:
-		case TTMODE_NONE:
-			ttbanner = W_CachePatchName("TTBANNER", PU_LEVEL);
-			ttwing = W_CachePatchName("TTWING", PU_LEVEL);
-			ttsonic = W_CachePatchName("TTSONIC", PU_LEVEL);
-			ttswave1 = W_CachePatchName("TTSWAVE1", PU_LEVEL);
-			ttswave2 = W_CachePatchName("TTSWAVE2", PU_LEVEL);
-			ttswip1 = W_CachePatchName("TTSWIP1", PU_LEVEL);
-			ttsprep1 = W_CachePatchName("TTSPREP1", PU_LEVEL);
-			ttsprep2 = W_CachePatchName("TTSPREP2", PU_LEVEL);
-			ttspop1 = W_CachePatchName("TTSPOP1", PU_LEVEL);
-			ttspop2 = W_CachePatchName("TTSPOP2", PU_LEVEL);
-			ttspop3 = W_CachePatchName("TTSPOP3", PU_LEVEL);
-			ttspop4 = W_CachePatchName("TTSPOP4", PU_LEVEL);
-			ttspop5 = W_CachePatchName("TTSPOP5", PU_LEVEL);
-			ttspop6 = W_CachePatchName("TTSPOP6", PU_LEVEL);
-			ttspop7 = W_CachePatchName("TTSPOP7", PU_LEVEL);
-			break;
-
-		// don't load alacroix gfx yet; we do that upon first draw.
-		case TTMODE_ALACROIX:
-			break;
-
-		case TTMODE_USER:
-		{
-			UINT16 i;
-			lumpnum_t lumpnum;
-			char lumpname[9];
-
-			LOADTTGFX(ttuser, curttname, TTMAX_USER)
-			break;
-		}
-	}
+	F_CacheTitleScreen();
 }
 
 static void F_UnloadAlacroixGraphics(SINT8 oldttscale)
@@ -2585,12 +2731,13 @@ void F_TitleScreenDrawer(void)
 	if (modeattacking)
 		return; // We likely came here from retrying. Don't do a damn thing.
 
+	if (needpatchrecache && (curttmode != TTMODE_ALACROIX))
+		F_CacheTitleScreen();
+
 	// Draw that sky!
 	if (curbgcolor >= 0)
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
-	else if (titlemapinaction && curbghide && ! hidetitlemap)
-		D_Render();
-	else
+	else if (!curbghide || !titlemapinaction || gamestate == GS_WAITINGPLAYERS)
 		F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
 
 	// Don't draw outside of the title screen, or if the patch isn't there.
@@ -2610,6 +2757,12 @@ void F_TitleScreenDrawer(void)
 		return;
 #endif
 
+	if (needpatchrecache && (curttmode == TTMODE_ALACROIX))
+	{
+		ttloaded[0] = ttloaded[1] = ttloaded[2] = ttloaded[3] = ttloaded[4] = ttloaded[5] = 0;
+		F_LoadAlacroixGraphics(activettscale);
+	}
+
 	switch(curttmode)
 	{
 		case TTMODE_OLD:
@@ -3350,6 +3503,10 @@ void F_TitleScreenTicker(boolean run)
 	if (run)
 		finalecount++;
 
+	// don't trigger if doing anything besides idling on title
+	if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN)
+		return;
+
 	// Execute the titlemap camera settings
 	if (titlemapinaction)
 	{
@@ -3396,10 +3553,6 @@ void F_TitleScreenTicker(boolean run)
 		}
 	}
 
-	// don't trigger if doing anything besides idling on title
-	if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN)
-		return;
-
 	// no demos to play? or, are they disabled?
 	if (!cv_rollingdemos.value || !numDemos)
 		return;
@@ -3474,6 +3627,8 @@ void F_StartContinue(void)
 		return;
 	}
 
+	wipestyleflags = WSF_FADEOUT;
+	F_TryColormapFade(31);
 	G_SetGamestate(GS_CONTINUING);
 	gameaction = ga_nothing;
 
@@ -3552,10 +3707,10 @@ void F_ContinueDrawer(void)
 	if (timetonext >= (11*TICRATE)+10)
 		return;
 
-	V_DrawLevelTitle(x - (V_LevelNameWidth("CONTINUE")>>1), 16, 0, "CONTINUE");
+	V_DrawLevelTitle(x - (V_LevelNameWidth("Continue?")>>1), 16, 0, "Continue?");
 
 	// Two stars...
-	patch = W_CachePatchName("CONTSTAR", PU_CACHE);
+	patch = W_CachePatchName("CONTSTAR", PU_PATCH);
 	V_DrawScaledPatch(x-32, 160, 0, patch);
 	V_DrawScaledPatch(x+32, 160, 0, patch);
 
@@ -3563,14 +3718,14 @@ void F_ContinueDrawer(void)
 	if (timeleft > 9)
 	{
 		numbuf[7] = '1';
-		V_DrawScaledPatch(x - 10, 160, 0, W_CachePatchName(numbuf, PU_CACHE));
+		V_DrawScaledPatch(x - 10, 160, 0, W_CachePatchName(numbuf, PU_PATCH));
 		numbuf[7] = '0';
-		V_DrawScaledPatch(x + 10, 160, 0, W_CachePatchName(numbuf, PU_CACHE));
+		V_DrawScaledPatch(x + 10, 160, 0, W_CachePatchName(numbuf, PU_PATCH));
 	}
 	else
 	{
 		numbuf[7] = '0'+timeleft;
-		V_DrawScaledPatch(x, 160, 0, W_CachePatchName(numbuf, PU_CACHE));
+		V_DrawScaledPatch(x, 160, 0, W_CachePatchName(numbuf, PU_PATCH));
 	}
 
 	// Draw the continue markers! Show continues.
@@ -3597,7 +3752,7 @@ void F_ContinueDrawer(void)
 	}
 
 	// Spotlight
-	V_DrawScaledPatch(x, 140, 0, W_CachePatchName("CONTSPOT", PU_CACHE));
+	V_DrawScaledPatch(x, 140, 0, W_CachePatchName("CONTSPOT", PU_PATCH));
 
 	// warping laser
 	if (continuetime)
@@ -3634,7 +3789,7 @@ void F_ContinueDrawer(void)
 #define drawchar(dx, dy, n)	{\
 								sprdef = &contskins[n]->sprites[cont_spr2[n][0]];\
 								sprframe = &sprdef->spriteframes[cont_spr2[n][1]];\
-								patch = W_CachePatchNum(sprframe->lumppat[cont_spr2[n][2]], PU_CACHE);\
+								patch = W_CachePatchNum(sprframe->lumppat[cont_spr2[n][2]], PU_PATCH);\
 								V_DrawFixedPatch((dx), (dy), FRACUNIT, (sprframe->flip & (1<<cont_spr2[n][2])) ? V_FLIP : 0, patch, contcolormaps[n]);\
 							}
 
@@ -3898,10 +4053,10 @@ void F_CutsceneDrawer(void)
 	{
 		if (cutscenes[cutnum]->scene[scenenum].pichires[picnum])
 			V_DrawSmallScaledPatch(picxpos, picypos, 0,
-				W_CachePatchName(cutscenes[cutnum]->scene[scenenum].picname[picnum], PU_CACHE));
+				W_CachePatchName(cutscenes[cutnum]->scene[scenenum].picname[picnum], PU_PATCH));
 		else
 			V_DrawScaledPatch(picxpos,picypos, 0,
-				W_CachePatchName(cutscenes[cutnum]->scene[scenenum].picname[picnum], PU_CACHE));
+				W_CachePatchName(cutscenes[cutnum]->scene[scenenum].picname[picnum], PU_PATCH));
 	}
 
 	if (dofadenow && rendermode != render_none)
@@ -4387,10 +4542,10 @@ void F_TextPromptDrawer(void)
 	{
 		if (textprompts[cutnum]->page[scenenum].pichires[picnum])
 			V_DrawSmallScaledPatch(picxpos, picypos, 0,
-				W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_CACHE));
+				W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_PATCH));
 		else
 			V_DrawScaledPatch(picxpos,picypos, 0,
-				W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_CACHE));
+				W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_PATCH));
 	}
 
 	// Draw background
@@ -4400,7 +4555,7 @@ void F_TextPromptDrawer(void)
 	if (iconlump != LUMPERROR)
 	{
 		INT32 iconx, icony, scale, scaledsize;
-		patch = W_CachePatchName(textprompts[cutnum]->page[scenenum].iconname, PU_CACHE);
+		patch = W_CachePatchName(textprompts[cutnum]->page[scenenum].iconname, PU_PATCH);
 
 		// scale and center
 		if (patch->width > patch->height)
diff --git a/src/f_finale.h b/src/f_finale.h
index 0002cb3cfa64e0b5ad523edc657ea96ef088276c..efc2de2e663fc1b43c1c87cbe95aa972916f12b7 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -146,7 +146,7 @@ extern boolean WipeStageTitle;
 typedef enum
 {
 	WIPESTYLE_NORMAL,
-	WIPESTYLE_LEVEL
+	WIPESTYLE_COLORMAP
 } wipestyle_t;
 extern wipestyle_t wipestyle;
 
@@ -159,6 +159,11 @@ typedef enum
 } wipestyleflags_t;
 extern wipestyleflags_t wipestyleflags;
 
+// Even my function names are borderline
+boolean F_ShouldColormapFade(void);
+boolean F_TryColormapFade(UINT8 wipecolor);
+void F_DecideWipeStyle(void);
+
 #define FADECOLORMAPDIV 8
 #define FADECOLORMAPROWS (256/FADECOLORMAPDIV)
 
diff --git a/src/f_wipe.c b/src/f_wipe.c
index b88b39ef00986404cdc487f6454509c16b95dd54..a83d104f24ed09696cdf10ea33885b64242e0191 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2013-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -56,7 +56,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
 
 	0,  // wipe_level_toblack
 	UINT8_MAX,  // wipe_intermission_toblack
-	UINT8_MAX,  // wipe_continuing_toblack
+	0,  // wipe_continuing_toblack
 	0,  // wipe_titlescreen_toblack
 	0,  // wipe_timeattack_toblack
 	99, // wipe_credits_toblack
@@ -161,7 +161,7 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
 	{
 		// Determine pixel to use from fademask
 		pcolor = &pMasterPalette[*lump++];
-		if (wipestyle == WIPESTYLE_LEVEL)
+		if (wipestyle == WIPESTYLE_COLORMAP)
 			*mask++ = pcolor->s.red / FADECOLORMAPDIV;
 		else
 			*mask++ = FixedDiv((pcolor->s.red+1)<<FRACBITS, paldiv)>>FRACBITS;
@@ -191,7 +191,7 @@ void F_WipeStageTitle(void)
 {
 	// draw level title
 	if ((WipeStageTitle && st_overlay)
-	&& (wipestyle == WIPESTYLE_LEVEL)
+	&& (wipestyle == WIPESTYLE_COLORMAP)
 	&& !(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)
 	&& *mapheaderinfo[gamemap-1]->lvlttl != '\0')
 	{
@@ -282,7 +282,7 @@ static void F_DoWipe(fademask_t *fademask)
 					relativepos += vid.width;
 				}
 			}
-			else if (*mask >= ((wipestyle == WIPESTYLE_LEVEL) ? FADECOLORMAPROWS : 10))
+			else if (*mask >= 10)
 			{
 				// shortcut - memcpy target to work
 				while (draw_linestogo--)
@@ -293,25 +293,113 @@ static void F_DoWipe(fademask_t *fademask)
 			}
 			else
 			{
-				if (wipestyle == WIPESTYLE_LEVEL)
+				// pointer to transtable that this mask would use
+				transtbl = transtables + ((9 - *mask)<<FF_TRANSSHIFT);
+
+				// DRAWING LOOP
+				while (draw_linestogo--)
 				{
-					int nmask;
-					UINT8 *fade = fadecolormap;
+					w = w_base + relativepos;
+					s = s_base + relativepos;
+					e = e_base + relativepos;
+					draw_rowstogo = draw_rowend - draw_rowstart;
+
+					while (draw_rowstogo--)
+						*w++ = transtbl[ ( *e++ << 8 ) + *s++ ];
+
+					relativepos += vid.width;
+				}
+				// END DRAWING LOOP
+			}
 
-					if (wipestyleflags & WSF_TOWHITE)
-						fade = fadecolormap + (FADECOLORMAPROWS * 256);
+			if (++maskx >= fademask->width)
+				++masky, maskx = 0;
+		} while (++mask < maskend);
+
+		free(scrxpos);
+		free(scrypos);
+	}
+}
+
+static void F_DoColormapWipe(fademask_t *fademask, UINT8 *colormap)
+{
+	// Lactozilla: F_DoWipe for WIPESTYLE_COLORMAP
+	{
+		// wipe screen, start, end
+		UINT8       *w = wipe_scr;
+		const UINT8 *s = wipe_scr_start;
+		const UINT8 *e = wipe_scr_end;
+
+		// first pixel for each screen
+		UINT8       *w_base = w;
+		const UINT8 *s_base = s;
+		const UINT8 *e_base = e;
+
+		// mask data, end
+		UINT8       *transtbl;
+		const UINT8 *mask    = fademask->mask;
+		const UINT8 *maskend = mask + fademask->size;
+
+		// rectangle draw hints
+		UINT32 draw_linestart, draw_rowstart;
+		UINT32 draw_lineend,   draw_rowend;
+		UINT32 draw_linestogo, draw_rowstogo;
+
+		// rectangle coordinates, etc.
+		UINT16* scrxpos = (UINT16*)malloc((fademask->width + 1)  * sizeof(UINT16));
+		UINT16* scrypos = (UINT16*)malloc((fademask->height + 1) * sizeof(UINT16));
+		UINT16 maskx, masky;
+		UINT32 relativepos;
+
+		// ---
+		// Screw it, we do the fixed point math ourselves up front.
+		scrxpos[0] = 0;
+		for (relativepos = 0, maskx = 1; maskx < fademask->width; ++maskx)
+			scrxpos[maskx] = (relativepos += fademask->xscale)>>FRACBITS;
+		scrxpos[fademask->width] = vid.width;
+
+		scrypos[0] = 0;
+		for (relativepos = 0, masky = 1; masky < fademask->height; ++masky)
+			scrypos[masky] = (relativepos += fademask->yscale)>>FRACBITS;
+		scrypos[fademask->height] = vid.height;
+		// ---
+
+		maskx = masky = 0;
+		do
+		{
+			draw_rowstart = scrxpos[maskx];
+			draw_rowend   = scrxpos[maskx + 1];
+			draw_linestart = scrypos[masky];
+			draw_lineend   = scrypos[masky + 1];
 
-					nmask = *mask;
-					if (wipestyleflags & WSF_FADEIN)
-						nmask = (FADECOLORMAPROWS-1) - nmask;
+			relativepos = (draw_linestart * vid.width) + draw_rowstart;
+			draw_linestogo = draw_lineend - draw_linestart;
 
-					transtbl = fade + (nmask * 256);
+			if (*mask == 0)
+			{
+				// shortcut - memcpy source to work
+				while (draw_linestogo--)
+				{
+					M_Memcpy(w_base+relativepos, s_base+relativepos, draw_rowend-draw_rowstart);
+					relativepos += vid.width;
 				}
-				else
+			}
+			else if (*mask >= FADECOLORMAPROWS)
+			{
+				// shortcut - memcpy target to work
+				while (draw_linestogo--)
 				{
-					// pointer to transtable that this mask would use
-					transtbl = transtables + ((9 - *mask)<<FF_TRANSSHIFT);
+					M_Memcpy(w_base+relativepos, e_base+relativepos, draw_rowend-draw_rowstart);
+					relativepos += vid.width;
 				}
+			}
+			else
+			{
+				int nmask = *mask;
+				if (wipestyleflags & WSF_FADEIN)
+					nmask = (FADECOLORMAPROWS-1) - nmask;
+
+				transtbl = colormap + (nmask * 256);
 
 				// DRAWING LOOP
 				while (draw_linestogo--)
@@ -321,16 +409,8 @@ static void F_DoWipe(fademask_t *fademask)
 					e = e_base + relativepos;
 					draw_rowstogo = draw_rowend - draw_rowstart;
 
-					if (wipestyle == WIPESTYLE_LEVEL)
-					{
-						while (draw_rowstogo--)
-							*w++ = transtbl[*e++];
-					}
-					else
-					{
-						while (draw_rowstogo--)
-							*w++ = transtbl[ ( *e++ << 8 ) + *s++ ];
-					}
+					while (draw_rowstogo--)
+						*w++ = transtbl[*e++];
 
 					relativepos += vid.width;
 				}
@@ -382,6 +462,62 @@ void F_WipeEndScreen(void)
 #endif
 }
 
+/** Verifies every condition for a colormapped fade.
+  */
+boolean F_ShouldColormapFade(void)
+{
+	if ((wipestyleflags & (WSF_FADEIN|WSF_FADEOUT)) // only if one of those wipestyleflags are actually set
+	&& !(wipestyleflags & WSF_CROSSFADE)) // and if not crossfading
+	{
+		// World
+		return (gamestate == GS_LEVEL
+		|| gamestate == GS_TITLESCREEN
+		// Finales
+		|| gamestate == GS_CONTINUING
+		|| gamestate == GS_CREDITS
+		|| gamestate == GS_EVALUATION
+		|| gamestate == GS_INTRO
+		|| gamestate == GS_ENDING
+		// Menus
+		|| gamestate == GS_TIMEATTACK);
+	}
+	return false;
+}
+
+/** Decides what wipe style to use.
+  */
+void F_DecideWipeStyle(void)
+{
+	// Set default wipe style
+	wipestyle = WIPESTYLE_NORMAL;
+
+	// Check for colormap wipe style
+	if (F_ShouldColormapFade())
+		wipestyle = WIPESTYLE_COLORMAP;
+}
+
+/** Attempt to run a colormap fade,
+    provided all the conditionals were properly met.
+    Returns true if so.
+    I demand you call F_RunWipe after this function.
+  */
+boolean F_TryColormapFade(UINT8 wipecolor)
+{
+	if (F_ShouldColormapFade())
+	{
+#ifdef HWRENDER
+		if (rendermode == render_opengl)
+			F_WipeColorFill(wipecolor);
+#endif
+		return true;
+	}
+	else
+	{
+		F_WipeColorFill(wipecolor);
+		return false;
+	}
+}
+
 /** After setting up the screens you want to wipe,
   * calling this will do a 'typical' wipe.
   */
@@ -399,18 +535,10 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
 		paldiv = FixedDiv(257<<FRACBITS, 11<<FRACBITS);
 
 	// Init the wipe
+	F_DecideWipeStyle();
 	WipeInAction = true;
 	wipe_scr = screens[0];
 
-	// don't know where else to put this.
-	// this any good?
-	if ((gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN)
-	&& (wipestyleflags & (WSF_FADEIN|WSF_FADEOUT)) // only if one of those wipestyleflags are actually set
-	&& !(wipestyleflags & WSF_CROSSFADE)) // and if not crossfading
-		wipestyle = WIPESTYLE_LEVEL;
-	else
-		wipestyle = WIPESTYLE_NORMAL;
-
 	// lastwipetic should either be 0 or the tic we last wiped
 	// on for fade-to-black
 	for (;;)
@@ -425,21 +553,39 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
 			I_Sleep();
 		lastwipetic = nowtime;
 
-#ifdef HWRENDER
-		if (rendermode == render_opengl)
+		// Wipe styles
+		if (wipestyle == WIPESTYLE_COLORMAP)
 		{
-			// send in the wipe type and wipe frame because we need to cache the graphic
-			if (wipestyle == WIPESTYLE_LEVEL)
+#ifdef HWRENDER
+			if (rendermode == render_opengl)
+			{
+				// send in the wipe type and wipe frame because we need to cache the graphic
 				HWR_DoTintedWipe(wipetype, wipeframe-1);
+			}
 			else
-				HWR_DoWipe(wipetype, wipeframe-1);
-		}
-		else
 #endif
-			F_DoWipe(fmask);
+			{
+				UINT8 *colormap = fadecolormap;
+				if (wipestyleflags & WSF_TOWHITE)
+					colormap += (FADECOLORMAPROWS * 256);
+				F_DoColormapWipe(fmask, colormap);
+			}
 
-		if (wipestyle == WIPESTYLE_LEVEL)
+			// Draw the title card above the wipe
 			F_WipeStageTitle();
+		}
+		else
+		{
+#ifdef HWRENDER
+			if (rendermode == render_opengl)
+			{
+				// send in the wipe type and wipe frame because we need to cache the graphic
+				HWR_DoWipe(wipetype, wipeframe-1);
+			}
+			else
+#endif
+				F_DoWipe(fmask);
+		}
 
 		I_OsPolling();
 		I_UpdateNoBlit();
diff --git a/src/g_game.c b/src/g_game.c
index faaed13c732929d643908ea1f2e3f495ce3b940a..b4f5d4c64583d0177b1db25dd81662232c53f230 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -79,7 +79,7 @@ UINT16 mapmusflags; // Track and reset bit
 UINT32 mapmusposition; // Position to jump to
 
 INT16 gamemap = 1;
-INT16 maptol;
+UINT32 maptol;
 UINT8 globalweather = 0;
 INT32 curWeather = PRECIP_NONE;
 INT32 cursaveslot = 0; // Auto-save 1p savegame slot
@@ -275,6 +275,12 @@ static UINT8 *metalbuffer = NULL;
 static UINT8 *metal_p;
 static UINT16 metalversion;
 
+typedef struct joystickvector2_s
+{
+	INT32 xaxis;
+	INT32 yaxis;
+} joystickvector2_t;
+
 // extra data stuff (events registered this frame while recording)
 static struct {
 	UINT8 flags; // EZT flags
@@ -394,6 +400,11 @@ consvar_t cv_directionchar2 = {"directionchar2", "Movement", CV_SAVE|CV_CALL, di
 consvar_t cv_autobrake = {"autobrake", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake_OnChange, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_autobrake2 = {"autobrake2", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake2_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
+static CV_PossibleValue_t deadzone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}};
+consvar_t cv_deadzone = {"deadzone", "0.25", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_deadzone2 = {"deadzone2", "0.25", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+
 typedef enum
 {
 	AXISNONE = 0,
@@ -401,7 +412,6 @@ typedef enum
 	AXISMOVE,
 	AXISLOOK,
 	AXISSTRAFE,
-	AXISDEAD, //Axises that don't want deadzones
 	AXISJUMP,
 	AXISSPIN,
 	AXISFIRE,
@@ -882,12 +892,6 @@ static INT32 JoyAxis(axis_input_e axissel)
 		retaxis = -JOYAXISRANGE;
 	if (retaxis > (+JOYAXISRANGE))
 		retaxis = +JOYAXISRANGE;
-	if (!Joystick.bGamepadStyle && axissel < AXISDEAD)
-	{
-		const INT32 jdeadzone = JOYAXISRANGE/4;
-		if (-jdeadzone < retaxis && retaxis < jdeadzone)
-			return 0;
-	}
 	if (flp) retaxis = -retaxis; //flip it around
 	return retaxis;
 }
@@ -955,16 +959,75 @@ static INT32 Joy2Axis(axis_input_e axissel)
 		retaxis = -JOYAXISRANGE;
 	if (retaxis > (+JOYAXISRANGE))
 		retaxis = +JOYAXISRANGE;
-	if (!Joystick2.bGamepadStyle && axissel < AXISDEAD)
-	{
-		const INT32 jdeadzone = JOYAXISRANGE/4;
-		if (-jdeadzone < retaxis && retaxis < jdeadzone)
-			return 0;
-	}
 	if (flp) retaxis = -retaxis; //flip it around
 	return retaxis;
 }
 
+// Take a magnitude of two axes, and adjust it to take out the deadzone
+// Will return a value between 0 and JOYAXISRANGE
+static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone)
+{
+	const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT;
+	INT32 deadzoneAppliedValue = 0;
+
+	if (jdeadzone > 0)
+	{
+		if (magnitude > jdeadzone)
+		{
+			INT32 adjustedMagnitude = abs(magnitude);
+			adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
+
+			adjustedMagnitude -= jdeadzone;
+
+			deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
+		}
+	}
+
+	return deadzoneAppliedValue;
+}
+
+// Get the actual sensible radial value for a joystick axis when accounting for a deadzone
+static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvector)
+{
+	INT32 gamepadStyle = Joystick.bGamepadStyle;
+	fixed_t deadZone = cv_deadzone.value;
+
+	if (splitnum == 1)
+	{
+		gamepadStyle = Joystick2.bGamepadStyle;
+		deadZone = cv_deadzone2.value;
+	}
+
+	// When gamepadstyle is "true" the values are just -1, 0, or 1. This is done in the interface code.
+	if (!gamepadStyle)
+	{
+		// Get the total magnitude of the 2 axes
+		INT32 magnitude = (joystickvector->xaxis * joystickvector->xaxis) + (joystickvector->yaxis * joystickvector->yaxis);
+		INT32 normalisedXAxis;
+		INT32 normalisedYAxis;
+		INT32 normalisedMagnitude;
+		double dMagnitude = sqrt((double)magnitude);
+		magnitude = (INT32)dMagnitude;
+
+		// Get the normalised xy values from the magnitude
+		normalisedXAxis = (joystickvector->xaxis * magnitude) / JOYAXISRANGE;
+		normalisedYAxis = (joystickvector->yaxis * magnitude) / JOYAXISRANGE;
+
+		// Apply the deadzone to the magnitude to give a correct value between 0 and JOYAXISRANGE
+		normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone);
+
+		// Apply the deadzone to the xy axes
+		joystickvector->xaxis = (normalisedXAxis * normalisedMagnitude) / JOYAXISRANGE;
+		joystickvector->yaxis = (normalisedYAxis * normalisedMagnitude) / JOYAXISRANGE;
+
+		// Cap the values so they don't go above the correct maximum
+		joystickvector->xaxis = min(joystickvector->xaxis, JOYAXISRANGE);
+		joystickvector->xaxis = max(joystickvector->xaxis, -JOYAXISRANGE);
+		joystickvector->yaxis = min(joystickvector->yaxis, JOYAXISRANGE);
+		joystickvector->yaxis = max(joystickvector->yaxis, -JOYAXISRANGE);
+	}
+}
+
 
 //
 // G_BuildTiccmd
@@ -985,12 +1048,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 {
 	boolean forcestrafe = false;
 	boolean forcefullinput = false;
-	INT32 tspeed, forward, side, axis, altaxis, i;
+	INT32 tspeed, forward, side, axis, strafeaxis, moveaxis, turnaxis, lookaxis, i;
 	const INT32 speed = 1;
 	// these ones used for multiple conditions
 	boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
 	player_t *player = &players[consoleplayer];
 	camera_t *thiscam = &camera;
+	joystickvector2_t movejoystickvector, lookjoystickvector;
 
 	static INT32 turnheld; // for accelerative turning
 	static boolean keyboard_look; // true if lookup/down using keyboard
@@ -1002,7 +1066,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 	// why build a ticcmd if we're paused?
 	// Or, for that matter, if we're being reborn.
 	// ...OR if we're blindfolded. No looking into the floor.
-	if (paused || P_AutoPause() || (gamestate == GS_LEVEL && (player->playerstate == PST_REBORN || ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK)
+	if (paused || P_AutoPause() || (gamestate == GS_LEVEL && (player->playerstate == PST_REBORN || ((gametyperules & GTR_TAG)
 	&& (leveltime < hidetime * TICRATE) && (player->pflags & PF_TAGIT)))))
 	{
 		cmd->angleturn = (INT16)(localangle >> 16);
@@ -1019,22 +1083,27 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 	movebkey = PLAYER1INPUTDOWN(gc_backward);
 
 	mouseaiming = (PLAYER1INPUTDOWN(gc_mouseaiming)) ^
-		(cv_chasecam.value ? cv_chasefreelook.value : cv_alwaysfreelook.value);
+		((cv_chasecam.value && !player->spectator) ? cv_chasefreelook.value : cv_alwaysfreelook.value);
 	analogjoystickmove = cv_usejoystick.value && !Joystick.bGamepadStyle;
 	gamepadjoystickmove = cv_usejoystick.value && Joystick.bGamepadStyle;
 
-	thisjoyaiming = (cv_chasecam.value) ? cv_chasefreelook.value : cv_alwaysfreelook.value;
+	thisjoyaiming = (cv_chasecam.value && !player->spectator) ? cv_chasefreelook.value : cv_alwaysfreelook.value;
 
 	// Reset the vertical look if we're no longer joyaiming
 	if (!thisjoyaiming && joyaiming)
 		localaiming = 0;
 	joyaiming = thisjoyaiming;
 
-	axis = JoyAxis(AXISTURN);
-	if (gamepadjoystickmove && axis != 0)
+	turnaxis = JoyAxis(AXISTURN);
+	lookaxis = JoyAxis(AXISLOOK);
+	lookjoystickvector.xaxis = turnaxis;
+	lookjoystickvector.yaxis = lookaxis;
+	G_HandleAxisDeadZone(0, &lookjoystickvector);
+
+	if (gamepadjoystickmove && lookjoystickvector.xaxis != 0)
 	{
-		turnright = turnright || (axis > 0);
-		turnleft = turnleft || (axis < 0);
+		turnright = turnright || (lookjoystickvector.xaxis > 0);
+		turnleft = turnleft || (lookjoystickvector.xaxis < 0);
 	}
 	forward = side = 0;
 
@@ -1074,10 +1143,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 		if (turnleft)
 			side -= sidemove[speed];
 
-		if (analogjoystickmove && axis != 0)
+		if (analogjoystickmove && lookjoystickvector.xaxis != 0)
 		{
 			// JOYAXISRANGE is supposed to be 1023 (divide by 1024)
-			side += ((axis * sidemove[1]) >> 10);
+			side += ((lookjoystickvector.xaxis * sidemove[1]) >> 10);
 		}
 	}
 	else if (cv_analog.value) // Analog
@@ -1090,45 +1159,48 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 	else
 	{
 		if (turnright)
-			cmd->angleturn = (INT16)(cmd->angleturn - angleturn[tspeed]);
+			cmd->angleturn = (INT16)(cmd->angleturn - ((angleturn[tspeed] * cv_cam_turnmultiplier.value)>>FRACBITS));
 		else if (turnleft)
-			cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]);
+			cmd->angleturn = (INT16)(cmd->angleturn + ((angleturn[tspeed] * cv_cam_turnmultiplier.value)>>FRACBITS));
 
-		if (analogjoystickmove && axis != 0)
+		if (analogjoystickmove && lookjoystickvector.xaxis != 0)
 		{
 			// JOYAXISRANGE should be 1023 (divide by 1024)
-			cmd->angleturn = (INT16)(cmd->angleturn - ((axis * angleturn[1]) >> 10)); // ANALOG!
+			cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * cv_cam_turnmultiplier.value)>>FRACBITS)); // ANALOG!
 		}
 	}
 
-	axis = JoyAxis(AXISSTRAFE);
-	if (gamepadjoystickmove && axis != 0)
+	strafeaxis = JoyAxis(AXISSTRAFE);
+	moveaxis = JoyAxis(AXISMOVE);
+	movejoystickvector.xaxis = strafeaxis;
+	movejoystickvector.yaxis = moveaxis;
+	G_HandleAxisDeadZone(0, &movejoystickvector);
+
+	if (gamepadjoystickmove && movejoystickvector.xaxis != 0)
 	{
-		if (axis < 0)
+		if (movejoystickvector.xaxis > 0)
 			side += sidemove[speed];
-		else if (axis > 0)
+		else if (movejoystickvector.xaxis < 0)
 			side -= sidemove[speed];
 	}
-	else if (analogjoystickmove && axis != 0)
+	else if (analogjoystickmove && movejoystickvector.xaxis != 0)
 	{
 		// JOYAXISRANGE is supposed to be 1023 (divide by 1024)
-		side += ((axis * sidemove[1]) >> 10);
+		side += ((movejoystickvector.xaxis * sidemove[1]) >> 10);
 	}
 
 	// forward with key or button
-	axis = JoyAxis(AXISMOVE);
-	altaxis = JoyAxis(AXISLOOK);
-	if (movefkey || (gamepadjoystickmove && axis < 0)
+	if (movefkey || (gamepadjoystickmove && movejoystickvector.yaxis < 0)
 		|| ((player->powers[pw_carry] == CR_NIGHTSMODE)
-			&& (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && altaxis < 0))))
+			&& (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0))))
 		forward = forwardmove[speed];
-	if (movebkey || (gamepadjoystickmove && axis > 0)
+	if (movebkey || (gamepadjoystickmove && movejoystickvector.yaxis > 0)
 		|| ((player->powers[pw_carry] == CR_NIGHTSMODE)
-			&& (PLAYER1INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && altaxis > 0))))
+			&& (PLAYER1INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0))))
 		forward -= forwardmove[speed];
 
-	if (analogjoystickmove && axis != 0)
-		forward -= ((axis * forwardmove[1]) >> 10); // ANALOG!
+	if (analogjoystickmove && movejoystickvector.yaxis != 0)
+		forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 10); // ANALOG!
 
 	// some people strafe left & right with mouse buttons
 	// those people are weird
@@ -1211,9 +1283,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 			localaiming += (mlooky<<19)*player_invert*screen_invert;
 		}
 
-		axis = JoyAxis(AXISLOOK);
-		if (analogjoystickmove && joyaiming && axis != 0 && cv_lookaxis.value != 0)
-			localaiming += (axis<<16) * screen_invert;
+		if (analogjoystickmove && joyaiming && lookjoystickvector.yaxis != 0 && cv_lookaxis.value != 0)
+			localaiming += (lookjoystickvector.yaxis<<16) * screen_invert;
 
 		// spring back if not using keyboard neither mouselookin'
 		if (!keyboard_look && cv_lookaxis.value == 0 && !joyaiming && !mouseaiming)
@@ -1221,12 +1292,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 
 		if (!(player->powers[pw_carry] == CR_NIGHTSMODE))
 		{
-			if (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
+			if (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0))
 			{
 				localaiming += KB_LOOKSPEED * screen_invert;
 				keyboard_look = true;
 			}
-			else if (PLAYER1INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && axis > 0))
+			else if (PLAYER1INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0))
 			{
 				localaiming -= KB_LOOKSPEED * screen_invert;
 				keyboard_look = true;
@@ -1308,7 +1379,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 	//Reset away view if a command is given.
 	if ((cmd->forwardmove || cmd->sidemove || cmd->buttons)
 		&& displayplayer != consoleplayer)
+	{
+#ifdef HAVE_BLUA
+		// Call ViewpointSwitch hooks here.
+		// The viewpoint was forcibly changed.
+		LUAh_ViewpointSwitch(player, &players[displayplayer], true);
+#endif
 		displayplayer = consoleplayer;
+	}
 }
 
 // like the g_buildticcmd 1 but using mouse2, gamcontrolbis, ...
@@ -1316,12 +1394,13 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 {
 	boolean forcestrafe = false;
 	boolean forcefullinput = false;
-	INT32 tspeed, forward, side, axis, altaxis, i;
+	INT32 tspeed, forward, side, axis, strafeaxis, moveaxis, turnaxis, lookaxis, i;
 	const INT32 speed = 1;
 	// these ones used for multiple conditions
 	boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
 	player_t *player = &players[secondarydisplayplayer];
 	camera_t *thiscam = (player->bot == 2 ? &camera : &camera2);
+	joystickvector2_t movejoystickvector, lookjoystickvector;
 
 	static INT32 turnheld; // for accelerative turning
 	static boolean keyboard_look; // true if lookup/down using keyboard
@@ -1348,22 +1427,27 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 	movebkey = PLAYER2INPUTDOWN(gc_backward);
 
 	mouseaiming = (PLAYER2INPUTDOWN(gc_mouseaiming)) ^
-		(cv_chasecam2.value ? cv_chasefreelook2.value : cv_alwaysfreelook2.value);
+		((cv_chasecam2.value && !player->spectator) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value);
 	analogjoystickmove = cv_usejoystick2.value && !Joystick2.bGamepadStyle;
 	gamepadjoystickmove = cv_usejoystick2.value && Joystick2.bGamepadStyle;
 
-	thisjoyaiming = (cv_chasecam2.value) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value;
+	thisjoyaiming = (cv_chasecam2.value && !player->spectator) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value;
 
 	// Reset the vertical look if we're no longer joyaiming
 	if (!thisjoyaiming && joyaiming)
 		localaiming2 = 0;
 	joyaiming = thisjoyaiming;
 
-	axis = Joy2Axis(AXISTURN);
-	if (gamepadjoystickmove && axis != 0)
+	turnaxis = Joy2Axis(AXISTURN);
+	lookaxis = Joy2Axis(AXISLOOK);
+	lookjoystickvector.xaxis = turnaxis;
+	lookjoystickvector.yaxis = lookaxis;
+	G_HandleAxisDeadZone(1, &lookjoystickvector);
+
+	if (gamepadjoystickmove && lookjoystickvector.xaxis != 0)
 	{
-		turnright = turnright || (axis > 0);
-		turnleft = turnleft || (axis < 0);
+		turnright = turnright || (lookjoystickvector.xaxis > 0);
+		turnleft = turnleft || (lookjoystickvector.xaxis < 0);
 	}
 	forward = side = 0;
 
@@ -1404,10 +1488,10 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 		if (turnleft)
 			side -= sidemove[speed];
 
-		if (analogjoystickmove && axis != 0)
+		if (analogjoystickmove && lookjoystickvector.xaxis != 0)
 		{
 			// JOYAXISRANGE is supposed to be 1023 (divide by 1024)
-			side += ((axis * sidemove[1]) >> 10);
+			side += ((lookjoystickvector.xaxis * sidemove[1]) >> 10);
 		}
 	}
 	else if (cv_analog2.value) // Analog
@@ -1420,45 +1504,48 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 	else
 	{
 		if (turnright)
-			cmd->angleturn = (INT16)(cmd->angleturn - angleturn[tspeed]);
+			cmd->angleturn = (INT16)(cmd->angleturn - (((angleturn[tspeed]<<FRACBITS) * cv_cam2_turnmultiplier.value)>>FRACBITS));
 		else if (turnleft)
-			cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]);
+			cmd->angleturn = (INT16)(cmd->angleturn + (((angleturn[tspeed]<<FRACBITS) * cv_cam2_turnmultiplier.value)>>FRACBITS));
 
-		if (analogjoystickmove && axis != 0)
+		if (analogjoystickmove && lookjoystickvector.xaxis != 0)
 		{
 			// JOYAXISRANGE should be 1023 (divide by 1024)
-			cmd->angleturn = (INT16)(cmd->angleturn - ((axis * angleturn[1]) >> 10)); // ANALOG!
+			cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * cv_cam2_turnmultiplier.value)>>FRACBITS)); // ANALOG!
 		}
 	}
 
-	axis = Joy2Axis(AXISSTRAFE);
-	if (gamepadjoystickmove && axis != 0)
+	strafeaxis = Joy2Axis(AXISSTRAFE);
+	moveaxis = Joy2Axis(AXISMOVE);
+	movejoystickvector.xaxis = strafeaxis;
+	movejoystickvector.yaxis = moveaxis;
+	G_HandleAxisDeadZone(1, &movejoystickvector);
+
+	if (gamepadjoystickmove && movejoystickvector.xaxis != 0)
 	{
-		if (axis < 0)
+		if (movejoystickvector.xaxis > 0)
 			side += sidemove[speed];
-		else if (axis > 0)
+		else if (movejoystickvector.xaxis < 0)
 			side -= sidemove[speed];
 	}
-	else if (analogjoystickmove && axis != 0)
+	else if (analogjoystickmove && movejoystickvector.xaxis != 0)
 	{
 		// JOYAXISRANGE is supposed to be 1023 (divide by 1024)
-		side += ((axis * sidemove[1]) >> 10);
+		side += ((movejoystickvector.xaxis * sidemove[1]) >> 10);
 	}
 
 	// forward with key or button
-	axis = Joy2Axis(AXISMOVE);
-	altaxis = Joy2Axis(AXISLOOK);
-	if (movefkey || (gamepadjoystickmove && axis < 0)
+	if (movefkey || (gamepadjoystickmove && movejoystickvector.yaxis < 0)
 		|| ((player->powers[pw_carry] == CR_NIGHTSMODE)
-			&& (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && altaxis < 0))))
+			&& (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0))))
 		forward = forwardmove[speed];
-	if (movebkey || (gamepadjoystickmove && axis > 0)
+	if (movebkey || (gamepadjoystickmove && movejoystickvector.yaxis > 0)
 		|| ((player->powers[pw_carry] == CR_NIGHTSMODE)
-			&& (PLAYER2INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && altaxis > 0))))
+			&& (PLAYER2INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0))))
 		forward -= forwardmove[speed];
 
-	if (analogjoystickmove && axis != 0)
-		forward -= ((axis * forwardmove[1]) >> 10); // ANALOG!
+	if (analogjoystickmove && movejoystickvector.yaxis != 0)
+		forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 10); // ANALOG!
 
 	// some people strafe left & right with mouse buttons
 	// those people are (still) weird
@@ -1538,9 +1625,8 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 			localaiming2 += (mlook2y<<19)*player_invert*screen_invert;
 		}
 
-		axis = Joy2Axis(AXISLOOK);
-		if (analogjoystickmove && joyaiming && axis != 0 && cv_lookaxis2.value != 0)
-			localaiming2 += (axis<<16) * screen_invert;
+		if (analogjoystickmove && joyaiming && lookjoystickvector.yaxis != 0 && cv_lookaxis2.value != 0)
+			localaiming2 += (lookjoystickvector.yaxis<<16) * screen_invert;
 
 		// spring back if not using keyboard neither mouselookin'
 		if (!keyboard_look && cv_lookaxis2.value == 0 && !joyaiming && !mouseaiming)
@@ -1548,12 +1634,12 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 
 		if (!(player->powers[pw_carry] == CR_NIGHTSMODE))
 		{
-			if (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
+			if (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0))
 			{
 				localaiming2 += KB_LOOKSPEED * screen_invert;
 				keyboard_look = true;
 			}
-			else if (PLAYER2INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && axis > 0))
+			else if (PLAYER2INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0))
 			{
 				localaiming2 -= KB_LOOKSPEED * screen_invert;
 				keyboard_look = true;
@@ -1632,6 +1718,7 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 			G_CopyTiccmd(cmd,  I_BaseTiccmd2(), 1); // empty, or external driver
 			B_BuildTiccmd(player, cmd);
 		}
+		B_HandleFlightIndicator(player);
 	}
 
 	if (cv_analog2.value) {
@@ -1753,6 +1840,7 @@ void G_DoLoadLevel(boolean resetplayer)
 		titlemapinaction = TITLEMAP_OFF;
 
 	G_SetGamestate(GS_LEVEL);
+	I_UpdateMouseGrab();
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
@@ -1761,15 +1849,14 @@ void G_DoLoadLevel(boolean resetplayer)
 	}
 
 	// Setup the level.
-	if (!P_SetupLevel(false)) // this never returns false?
+	if (!P_LoadLevel(false)) // this never returns false?
 	{
 		// fail so reset game stuff
 		Command_ExitGame_f();
 		return;
 	}
 
-	if (!resetplayer)
-		P_FindEmerald();
+	P_FindEmerald();
 
 	displayplayer = consoleplayer; // view the guy you are playing
 	if (!splitscreen && !botingame)
@@ -1804,12 +1891,9 @@ void G_DoLoadLevel(boolean resetplayer)
 //
 void G_StartTitleCard(void)
 {
-	wipestyleflags |= WSF_FADEIN;
-	wipestyleflags &= ~WSF_FADEOUT;
-
 	// The title card has been disabled for this map.
 	// Oh well.
-	if (mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)
+	if (!G_IsTitleCardAvailable())
 	{
 		WipeStageTitle = false;
 		return;
@@ -1828,7 +1912,7 @@ void G_StartTitleCard(void)
 //
 // Run the title card before fading in to the level.
 //
-void G_PreLevelTitleCard(tic_t ticker, boolean update)
+void G_PreLevelTitleCard(void)
 {
 	tic_t starttime = I_GetTime();
 	tic_t endtime = starttime + (PRELEVELTIME*NEWTICRATERATIO);
@@ -1842,13 +1926,33 @@ void G_PreLevelTitleCard(tic_t ticker, boolean update)
 		lasttime = nowtime;
 
 		ST_runTitleCard();
-		ST_preLevelTitleCardDrawer(ticker, update);
+		ST_preLevelTitleCardDrawer();
+		I_FinishUpdate(); // page flip or blit buffer
 
 		if (moviemode)
 			M_SaveFrame();
 		if (takescreenshot) // Only take screenshots after drawing.
 			M_DoScreenShot();
 	}
+	if (!cv_showhud.value)
+		wipestyleflags = WSF_CROSSFADE;
+}
+
+//
+// Returns true if the current level has a title card.
+//
+boolean G_IsTitleCardAvailable(void)
+{
+	// The current level header explicitly disabled the title card.
+	if (mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)
+		return false;
+
+	// The current gametype doesn't have a title card.
+	if (gametyperules & GTR_NOTITLECARD)
+		return false;
+
+	// The title card is available.
+	return true;
 }
 
 INT32 pausedelay = 0;
@@ -1941,6 +2045,11 @@ boolean G_Responder(event_t *ev)
 	if (gamestate == GS_LEVEL && ev->type == ev_keydown
 		&& (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1]))
 	{
+		// ViewpointSwitch Lua hook.
+#ifdef HAVE_BLUA
+		UINT8 canSwitchView = 0;
+#endif
+
 		if (splitscreen || !netgame)
 			displayplayer = consoleplayer;
 		else
@@ -1955,6 +2064,15 @@ boolean G_Responder(event_t *ev)
 				if (!playeringame[displayplayer])
 					continue;
 
+#ifdef HAVE_BLUA
+				// Call ViewpointSwitch hooks here.
+				canSwitchView = LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], false);
+				if (canSwitchView == 1) // Set viewpoint to this player
+					break;
+				else if (canSwitchView == 2) // Skip this player
+					continue;
+#endif
+
 				if (players[displayplayer].spectator)
 					continue;
 
@@ -2323,6 +2441,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	outofcoop = players[player].outofcoop;
 	pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
 
+	if (!betweenmaps)
+		pflags |= (players[player].pflags & PF_FINISHED);
+
 	// As long as we're not in multiplayer, carry over cheatcodes from map to map
 	if (!(netgame || multiplayer))
 		pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP|PF_INVIS));
@@ -2560,7 +2681,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost)
 
 	// -- CTF --
 	// Order: CTF->DM->Coop
-	if (gametype == GT_CTF && players[playernum].ctfteam)
+	if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam)
 	{
 		if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start
 		&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
@@ -2569,8 +2690,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost)
 
 	// -- DM/Tag/CTF-spectator/etc --
 	// Order: DM->CTF->Coop
-	else if (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF
-	 || ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && !(players[playernum].pflags & PF_TAGIT)))
+	else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT))
 	{
 		if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
 		&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
@@ -2616,7 +2736,7 @@ mapthing_t *G_FindCTFStart(INT32 playernum)
 
 	if (!numredctfstarts && !numbluectfstarts) //why even bother, eh?
 	{
-		if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
+		if ((gametyperules & GTR_TEAMFLAGS) && (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)))
 			CONS_Alert(CONS_WARNING, M_GetText("No CTF starts in this map!\n"));
 		return NULL;
 	}
@@ -2771,11 +2891,11 @@ void G_DoReborn(INT32 playernum)
 
 	if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP))
 		resetlevel = true;
-	else if (gametype == GT_COOP && (netgame || multiplayer) && !G_IsSpecialStage(gamemap))
+	else if ((G_GametypeUsesCoopLives() || G_GametypeUsesCoopStarposts()) && (netgame || multiplayer) && !G_IsSpecialStage(gamemap))
 	{
 		boolean notgameover = true;
 
-		if (cv_cooplives.value != 0 && player->lives <= 0) // consider game over first
+		if (G_GametypeUsesCoopLives() && (cv_cooplives.value != 0 && player->lives <= 0)) // consider game over first
 		{
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
@@ -2810,7 +2930,7 @@ void G_DoReborn(INT32 playernum)
 			}
 		}
 
-		if (notgameover && cv_coopstarposts.value == 2)
+		if (G_GametypeUsesCoopStarposts() && (notgameover && cv_coopstarposts.value == 2))
 		{
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
@@ -2845,7 +2965,7 @@ void G_DoReborn(INT32 playernum)
 		}
 		if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD))
 		{
-			P_LoadThingsOnly();
+			P_RespawnThings();
 
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
@@ -2886,7 +3006,7 @@ void G_DoReborn(INT32 playernum)
 			}
 
 			// restore time in netgame (see also p_setup.c)
-			if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2)
+			if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
 			{
 				// is this a hack? maybe
 				tic_t maxstarposttime = 0;
@@ -2957,7 +3077,7 @@ void G_AddPlayer(INT32 playernum)
 			if (!players[i].exiting)
 				notexiting++;
 
-			if (!(cv_coopstarposts.value && (gametype == GT_COOP) && (p->starpostnum < players[i].starpostnum)))
+			if (!(cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (p->starpostnum < players[i].starpostnum)))
 				continue;
 
 			p->starpostscale = players[i].starpostscale;
@@ -2978,7 +3098,7 @@ void G_AddPlayer(INT32 playernum)
 	if (G_GametypeUsesLives() || ((netgame || multiplayer) && gametype == GT_COOP))
 		p->lives = cv_startinglives.value;
 
-	if (countplayers && !notexiting)
+	if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap))
 		P_DoPlayerExit(p);
 }
 
@@ -2997,7 +3117,7 @@ boolean G_EnoughPlayersFinished(void)
 			continue;
 
 		total++;
-		if (players[i].pflags & PF_FINISHED)
+		if ((players[i].pflags & PF_FINISHED) || players[i].exiting)
 			exiting++;
 	}
 
@@ -3022,7 +3142,7 @@ void G_ExitLevel(void)
 				CV_SetValue(&cv_teamscramble, cv_scrambleonchange.value);
 		}
 
-		if (gametype != GT_COOP)
+		if (!(gametyperules & GTR_CAMPAIGN))
 			CONS_Printf(M_GetText("The round has ended.\n"));
 
 		// Remove CEcho text on round end.
@@ -3054,6 +3174,239 @@ const char *Gametype_Names[NUMGAMETYPES] =
 	"CTF" // GT_CTF
 };
 
+// For dehacked
+const char *Gametype_ConstantNames[NUMGAMETYPES] =
+{
+	"GT_COOP", // GT_COOP
+	"GT_COMPETITION", // GT_COMPETITION
+	"GT_RACE", // GT_RACE
+
+	"GT_MATCH", // GT_MATCH
+	"GT_TEAMMATCH", // GT_TEAMMATCH
+
+	"GT_TAG", // GT_TAG
+	"GT_HIDEANDSEEK", // GT_HIDEANDSEEK
+
+	"GT_CTF" // GT_CTF
+};
+
+// Gametype rules
+UINT32 gametypedefaultrules[NUMGAMETYPES] =
+{
+	// Co-op
+	GTR_CAMPAIGN|GTR_LIVES|GTR_FRIENDLY|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES|GTR_CUTSCENES,
+	// Competition
+	GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
+	// Race
+	GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
+
+	// Match
+	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY,
+	// Team Match
+	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
+
+	// Tag
+	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
+	// Hide and Seek
+	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
+
+	// CTF
+	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
+};
+
+//
+// G_SetGametype
+//
+// Set a new gametype, also setting gametype rules accordingly. Yay!
+//
+void G_SetGametype(INT16 gtype)
+{
+	gametype = gtype;
+	gametyperules = gametypedefaultrules[gametype];
+}
+
+//
+// G_AddGametype
+//
+// Add a gametype. Returns the new gametype number.
+//
+INT16 G_AddGametype(UINT32 rules)
+{
+	INT16 newgtype = gametypecount;
+	gametypecount++;
+
+	// Set gametype rules.
+	gametypedefaultrules[newgtype] = rules;
+	Gametype_Names[newgtype] = "???";
+
+	// Update gametype_cons_t accordingly.
+	G_UpdateGametypeSelections();
+
+	return newgtype;
+}
+
+//
+// G_AddGametypeConstant
+//
+// Self-explanatory. Filters out "bad" characters.
+//
+void G_AddGametypeConstant(INT16 gtype, const char *newgtconst)
+{
+	size_t r = 0; // read
+	size_t w = 0; // write
+	char *gtconst = Z_Calloc(strlen(newgtconst) + 3, PU_STATIC, NULL);
+	char *tmpconst = Z_Calloc(strlen(newgtconst), PU_STATIC, NULL);
+
+	// Copy the gametype name.
+	strcpy(tmpconst, newgtconst);
+
+	// Make uppercase.
+	strupr(tmpconst);
+
+	// Prepare to write the new constant string now.
+	strcpy(gtconst, "GT_");
+
+	// Remove characters that will not be allowed in the constant string.
+	for (; r < strlen(tmpconst); r++)
+	{
+		boolean writechar = true;
+		char rc = tmpconst[r];
+		switch (rc)
+		{
+			// Space, at sign and question mark
+			case ' ':
+			case '@':
+			case '?':
+			// Used for operations
+			case '+':
+			case '-':
+			case '*':
+			case '/':
+			case '%':
+			case '^':
+			case '&':
+			case '!':
+			// Part of Lua's syntax
+			case '#':
+			case '=':
+			case '~':
+			case '<':
+			case '>':
+			case '(':
+			case ')':
+			case '{':
+			case '}':
+			case '[':
+			case ']':
+			case ':':
+			case ';':
+			case ',':
+			case '.':
+				writechar = false;
+				break;
+		}
+		if (writechar)
+		{
+			gtconst[3 + w] = rc;
+			w++;
+		}
+	}
+
+	// Free the temporary string.
+	Z_Free(tmpconst);
+
+	// Finally, set the constant string.
+	Gametype_ConstantNames[gtype] = gtconst;
+}
+
+//
+// G_UpdateGametypeSelections
+//
+// Updates gametype_cons_t.
+//
+void G_UpdateGametypeSelections(void)
+{
+	INT32 i;
+	for (i = 0; i < gametypecount; i++)
+	{
+		gametype_cons_t[i].value = i;
+		gametype_cons_t[i].strvalue = Gametype_Names[i];
+	}
+	gametype_cons_t[NUMGAMETYPES].value = 0;
+	gametype_cons_t[NUMGAMETYPES].strvalue = NULL;
+}
+
+//
+// G_SetGametypeDescription
+//
+// Set a description for the specified gametype.
+// (Level platter)
+//
+void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolor, UINT8 rightcolor)
+{
+	if (descriptiontext != NULL)
+		strncpy(gametypedesc[gtype].notes, descriptiontext, 441);
+	gametypedesc[gtype].col[0] = leftcolor;
+	gametypedesc[gtype].col[1] = rightcolor;
+}
+
+// Gametype rankings
+INT16 gametyperankings[NUMGAMETYPES] =
+{
+	GT_COOP,
+	GT_COMPETITION,
+	GT_RACE,
+
+	GT_MATCH,
+	GT_TEAMMATCH,
+
+	GT_TAG,
+	GT_HIDEANDSEEK,
+
+	GT_CTF,
+};
+
+// Gametype to TOL (Type Of Level)
+UINT32 gametypetol[NUMGAMETYPES] =
+{
+	TOL_COOP, // Co-op
+	TOL_COMPETITION, // Competition
+	TOL_RACE, // Race
+
+	TOL_MATCH, // Match
+	TOL_MATCH, // Team Match
+
+	TOL_TAG, // Tag
+	TOL_TAG, // Hide and Seek
+
+	TOL_CTF, // CTF
+};
+
+//
+// G_AddTOL
+//
+// Adds a type of level.
+//
+void G_AddTOL(UINT32 newtol, const char *tolname)
+{
+	TYPEOFLEVEL[numtolinfo].name = Z_StrDup(tolname);
+	TYPEOFLEVEL[numtolinfo].flag = newtol;
+	numtolinfo++;
+
+	TYPEOFLEVEL[numtolinfo].name = NULL;
+	TYPEOFLEVEL[numtolinfo].flag = 0;
+}
+
+//
+// G_AddTOL
+//
+// Assigns a type of level to a gametype.
+//
+void G_AddGametypeTOL(INT16 gtype, UINT32 newtol)
+{
+	gametypetol[gtype] = newtol;
+}
+
 //
 // G_GetGametypeByName
 //
@@ -3063,7 +3416,7 @@ INT32 G_GetGametypeByName(const char *gametypestr)
 {
 	INT32 i;
 
-	for (i = 0; i < NUMGAMETYPES; i++)
+	for (i = 0; i < gametypecount; i++)
 		if (!stricmp(gametypestr, Gametype_Names[i]))
 			return i;
 
@@ -3096,15 +3449,37 @@ boolean G_IsSpecialStage(INT32 mapnum)
 //
 boolean G_GametypeUsesLives(void)
 {
-	 // Coop, Competitive
-	if ((gametype == GT_COOP || gametype == GT_COMPETITION)
+	// Coop, Competitive
+	if ((gametyperules & GTR_LIVES)
 	 && !(modeattacking || metalrecording) // No lives in Time Attack
-	 //&& !G_IsSpecialStage(gamemap)
+	 && !G_IsSpecialStage(gamemap)
 	 && !(maptol & TOL_NIGHTS)) // No lives in NiGHTS
 		return true;
 	return false;
 }
 
+//
+// G_GametypeUsesCoopLives
+//
+// Returns true if the current gametype uses
+// the cooplives CVAR.  False otherwise.
+//
+boolean G_GametypeUsesCoopLives(void)
+{
+	return (gametyperules & (GTR_LIVES|GTR_FRIENDLY)) == (GTR_LIVES|GTR_FRIENDLY);
+}
+
+//
+// G_GametypeUsesCoopStarposts
+//
+// Returns true if the current gametype uses
+// the coopstarposts CVAR.  False otherwise.
+//
+boolean G_GametypeUsesCoopStarposts(void)
+{
+	return (gametyperules & GTR_FRIENDLY);
+}
+
 //
 // G_GametypeHasTeams
 //
@@ -3113,7 +3488,7 @@ boolean G_GametypeUsesLives(void)
 //
 boolean G_GametypeHasTeams(void)
 {
-	return (gametype == GT_TEAMMATCH || gametype == GT_CTF);
+	return (gametyperules & GTR_TEAMS);
 }
 
 //
@@ -3124,7 +3499,7 @@ boolean G_GametypeHasTeams(void)
 //
 boolean G_GametypeHasSpectators(void)
 {
-	return (gametype != GT_COOP && gametype != GT_COMPETITION && gametype != GT_RACE);
+	return (gametyperules & GTR_SPECTATORS);
 }
 
 //
@@ -3135,7 +3510,7 @@ boolean G_GametypeHasSpectators(void)
 //
 boolean G_RingSlingerGametype(void)
 {
-	return ((gametype != GT_COOP && gametype != GT_COMPETITION && gametype != GT_RACE) || (cv_ringslinger.value));
+	return ((gametyperules & GTR_RINGSLINGER) || (cv_ringslinger.value));
 }
 
 //
@@ -3145,7 +3520,7 @@ boolean G_RingSlingerGametype(void)
 //
 boolean G_PlatformGametype(void)
 {
-	return (gametype == GT_COOP || gametype == GT_RACE || gametype == GT_COMPETITION);
+	return (!(gametyperules & GTR_RINGSLINGER));
 }
 
 //
@@ -3155,7 +3530,17 @@ boolean G_PlatformGametype(void)
 //
 boolean G_TagGametype(void)
 {
-	return (gametype == GT_TAG || gametype == GT_HIDEANDSEEK);
+	return (gametyperules & GTR_TAG);
+}
+
+//
+// G_CompetitionGametype
+//
+// For gametypes that are race gametypes, and have lives.
+//
+boolean G_CompetitionGametype(void)
+{
+	return ((gametyperules & GTR_RACE) && (gametyperules & GTR_LIVES));
 }
 
 /** Get the typeoflevel flag needed to indicate support of a gametype.
@@ -3166,18 +3551,9 @@ boolean G_TagGametype(void)
   */
 INT16 G_TOLFlag(INT32 pgametype)
 {
-	if (!multiplayer)                 return TOL_SP;
-	if (pgametype == GT_COOP)         return TOL_COOP;
-	if (pgametype == GT_COMPETITION)  return TOL_COMPETITION;
-	if (pgametype == GT_RACE)         return TOL_RACE;
-	if (pgametype == GT_MATCH)        return TOL_MATCH;
-	if (pgametype == GT_TEAMMATCH)    return TOL_MATCH;
-	if (pgametype == GT_TAG)          return TOL_TAG;
-	if (pgametype == GT_HIDEANDSEEK)  return TOL_TAG;
-	if (pgametype == GT_CTF)          return TOL_CTF;
-
-	CONS_Alert(CONS_ERROR, M_GetText("Unknown gametype! %d\n"), pgametype);
-	return INT16_MAX;
+	if (!multiplayer)
+		return TOL_SP;
+	return gametypetol[pgametype];
 }
 
 /** Select a random map with the given typeoflevel flags.
@@ -3188,7 +3564,7 @@ INT16 G_TOLFlag(INT32 pgametype)
   *         has those flags.
   * \author Graue <graue@oceanbase.org>
   */
-static INT16 RandMap(INT16 tolflags, INT16 pprevmap)
+static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
 {
 	INT16 *okmaps = Z_Malloc(NUMMAPS * sizeof(INT16), PU_STATIC, NULL);
 	INT32 numokmaps = 0;
@@ -3230,9 +3606,13 @@ static void G_UpdateVisited(void)
 		// eh, what the hell
 		if (ultimatemode)
 			mapvisited[gamemap-1] |= MV_ULTIMATE;
-		// may seem incorrect but IS possible in what the main game uses as special stages, and nummaprings will be -1 in NiGHTS
+		// may seem incorrect but IS possible in what the main game uses as mp special stages, and nummaprings will be -1 in NiGHTS
 		if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
+		{
 			mapvisited[gamemap-1] |= MV_PERFECT;
+			if (modeattacking)
+				mapvisited[gamemap-1] |= MV_PERFECTRA;
+		}
 		if (!spec)
 		{
 			// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
@@ -3342,10 +3722,10 @@ static void G_DoCompleted(void)
 		I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
 
 	// wrap around in race
-	if (nextmap >= 1100-1 && nextmap <= 1102-1 && (gametype == GT_RACE || gametype == GT_COMPETITION))
+	if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
 		nextmap = (INT16)(spstage_start-1);
 
-	if ((gottoken = (gametype == GT_COOP && token)))
+	if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token)))
 	{
 		token--;
 
@@ -3365,7 +3745,7 @@ static void G_DoCompleted(void)
 
 	automapactive = false;
 
-	if (gametype != GT_COOP)
+	if (!(gametyperules & GTR_CAMPAIGN))
 	{
 		if (cv_advancemap.value == 0) // Stay on same map.
 			nextmap = prevmap;
@@ -3404,7 +3784,7 @@ void G_AfterIntermission(void)
 
 	HU_ClearCEcho();
 
-	if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene.
+	if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene.
 		F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
 	else
 	{
@@ -3514,7 +3894,7 @@ static void G_DoContinued(void)
 void G_EndGame(void)
 {
 	// Only do evaluation and credits in coop games.
-	if (gametype == GT_COOP)
+	if (gametyperules & GTR_CUTSCENES)
 	{
 		if (nextmap == 1103-1) // end game with ending
 		{
@@ -4132,6 +4512,8 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 {
 	INT32 i;
 
+	Y_CleanupScreenBuffer();
+
 	if (paused)
 	{
 		paused = false;
@@ -4215,7 +4597,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 	automapactive = false;
 	imcontinuing = false;
 
-	if (!skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene.
+	if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene.
 		F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
 	else
 		G_DoLoadLevel(resetplayer);
@@ -4448,6 +4830,61 @@ void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc)
 	Z_Free(freq);
 }
 
+INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
+{
+	boolean usemapcode = false;
+
+	INT32 newmapnum;
+
+	size_t mapnamelen;
+
+	char *p;
+
+	mapnamelen = strlen(mapname);
+
+	if (mapnamelen == 2)/* maybe two digit code */
+	{
+		if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) ))
+			usemapcode = true;
+	}
+	else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0)
+	{
+		if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ))
+			usemapcode = true;
+	}
+
+	if (!usemapcode)
+	{
+		/* Now detect map number in base 10, which no one asked for. */
+		newmapnum = strtol(mapname, &p, 10);
+		if (*p == '\0')/* we got it */
+		{
+			if (newmapnum < 1 || newmapnum > NUMMAPS)
+			{
+				CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
+				return 0;
+			}
+			usemapcode = true;
+		}
+		else
+		{
+			newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL);
+		}
+	}
+
+	if (usemapcode)
+	{
+		/* we can't check mapheaderinfo for this hahahaha */
+		if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR)
+			return 0;
+
+		if (realmapnamep)
+			(*realmapnamep) = G_BuildMapTitle(newmapnum);
+	}
+
+	return newmapnum;
+}
+
 //
 // DEMO RECORDING
 //
@@ -6541,23 +6978,20 @@ void G_AddGhost(char *defdemoname)
 	I_Assert(mthing);
 	{ // 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);
-		gh->mo->angle = FixedAngle(mthing->angle*FRACUNIT);
+		gh->mo->angle = FixedAngle(mthing->angle << FRACBITS);
 		f = gh->mo->floorz;
 		c = gh->mo->ceilingz - mobjinfo[MT_PLAYER].height;
 		if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP))
 		{
-			z = c;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
+			z = c - offset;
 			if (z < f)
 				z = f;
 		}
 		else
 		{
-			z = f;
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
+			z = f + offset;
 			if (z > c)
 				z = c;
 		}
diff --git a/src/g_game.h b/src/g_game.h
index 0a575c099c6badee900584b9851eadf1dfeed3ef..cb51faaca116975402c02d9fe4b6214a43e99a4a 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -72,6 +72,7 @@ extern consvar_t cv_useranalog, cv_useranalog2;
 extern consvar_t cv_analog, cv_analog2;
 extern consvar_t cv_directionchar, cv_directionchar2;
 extern consvar_t cv_autobrake, cv_autobrake2;
+extern consvar_t cv_deadzone, cv_deadzone2;
 extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_jumpaxis,cv_spinaxis,cv_fireaxis,cv_firenaxis;
 extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_jumpaxis2,cv_spinaxis2,cv_fireaxis2,cv_firenaxis2;
 extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest;
@@ -129,6 +130,9 @@ INT32 G_FindMap(const char *query, char **foundmapnamep,
 		mapsearchfreq_t **freqp, INT32 *freqc);
 void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc);
 
+/* Match map name by search + 2 digit map code or map number. */
+INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep);
+
 // XMOD spawning
 mapthing_t *G_FindCTFStart(INT32 playernum);
 mapthing_t *G_FindMatchStart(INT32 playernum);
@@ -141,7 +145,8 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar,
 	boolean SSSG, boolean FLS);
 void G_DoLoadLevel(boolean resetplayer);
 void G_StartTitleCard(void);
-void G_PreLevelTitleCard(tic_t ticker, boolean update);
+void G_PreLevelTitleCard(void);
+boolean G_IsTitleCardAvailable(void);
 void G_DeferedPlayDemo(const char *demo);
 
 // Can be called by the startup code or M_Responder, calls P_SetupLevel.
@@ -201,14 +206,29 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill);
 void G_StopDemo(void);
 boolean G_CheckDemoStatus(void);
 
+extern UINT32 gametypedefaultrules[NUMGAMETYPES];
+extern UINT32 gametypetol[NUMGAMETYPES];
+extern INT16 gametyperankings[NUMGAMETYPES];
+
+void G_SetGametype(INT16 gametype);
+INT16 G_AddGametype(UINT32 rules);
+void G_AddGametypeConstant(INT16 gtype, const char *newgtconst);
+void G_UpdateGametypeSelections(void);
+void G_AddTOL(UINT32 newtol, const char *tolname);
+void G_AddGametypeTOL(INT16 gtype, UINT32 newtol);
+void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolor, UINT8 rightcolor);
+
 INT32 G_GetGametypeByName(const char *gametypestr);
 boolean G_IsSpecialStage(INT32 mapnum);
 boolean G_GametypeUsesLives(void);
+boolean G_GametypeUsesCoopLives(void);
+boolean G_GametypeUsesCoopStarposts(void);
 boolean G_GametypeHasTeams(void);
 boolean G_GametypeHasSpectators(void);
 boolean G_RingSlingerGametype(void);
 boolean G_PlatformGametype(void);
 boolean G_TagGametype(void);
+boolean G_CompetitionGametype(void);
 boolean G_EnoughPlayersFinished(void);
 void G_ExitLevel(void);
 void G_NextLevel(void);
diff --git a/src/g_input.c b/src/g_input.c
index afefbcc3b7629ea03ad57e58522d9a39f41c219b..ac901703fe742867b591fc679e1b0469bb0653cf 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/g_input.h b/src/g_input.h
index d3c6937817b91c050fd3f83d3539147b37362962..d089332c5355305ce5b350ed0ab5fc9d72a0302d 100644
--- a/src/g_input.h
+++ b/src/g_input.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/g_state.h b/src/g_state.h
index 9d34da6a97706ba99fb0d6aac9a25407622b581e..e82a34935e4f200cb3f97dc0d790475a13e31303 100644
--- a/src/g_state.h
+++ b/src/g_state.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c
index 9e454bcd5275dd28150e33f053fc5ee20c79ddb0..6f3dd9fbd5290ead1959d17a70d0429582e6d963 100644
--- a/src/hardware/hw_bsp.c
+++ b/src/hardware/hw_bsp.c
@@ -449,8 +449,12 @@ static poly_t *CutOutSubsecPoly(seg_t *lseg, INT32 count, poly_t *poly)
 	// for each seg of the subsector
 	for (; count--; lseg++)
 	{
-		//x,y,dx,dy (like a divline)
 		line_t *line = lseg->linedef;
+
+		if (lseg->glseg)
+			continue;
+
+		//x,y,dx,dy (like a divline)
 		p1.x = FIXED_TO_FLOAT(lseg->side ? line->v2->x : line->v1->x);
 		p1.y = FIXED_TO_FLOAT(lseg->side ? line->v2->y : line->v1->y);
 		p2.x = FIXED_TO_FLOAT(lseg->side ? line->v1->x : line->v2->x);
@@ -836,8 +840,10 @@ static INT32 SolveTProblem(void)
 		return 0;
 
 	CONS_Debug(DBG_RENDER, "Solving T-joins. This may take a while. Please wait...\n");
+#ifdef HWR_LOADING_SCREEN
 	CON_Drawer(); //let the user know what we are doing
 	I_FinishUpdate(); // page flip or blit buffer
+#endif
 
 	numsplitpoly = 0;
 
@@ -964,9 +970,9 @@ void HWR_CreatePlanePolygons(INT32 bspnum)
 	CONS_Debug(DBG_RENDER, "Creating polygons, please wait...\n");
 #ifdef HWR_LOADING_SCREEN
 	ls_count = ls_percent = 0; // reset the loading status
-#endif
 	CON_Drawer(); //let the user know what we are doing
 	I_FinishUpdate(); // page flip or blit buffer
+#endif
 
 	HWR_ClearPolys();
 
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 6eaafca6d0deec87da0404a1ecb508880e06494c..c47833187b1af03f7308d3844fbcce51baa22bd0 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -33,10 +33,6 @@
 #include "../r_patch.h"
 #include "../p_setup.h"
 
-//Hurdler: 25/04/2000: used for new colormap code in hardware mode
-//static UINT8 *gr_colormap = NULL; // by default it must be NULL ! (because colormap tables are not initialized)
-boolean firetranslucent = false;
-
 // Values set after a call to HWR_ResizeBlock()
 static INT32 blocksize, blockwidth, blockheight;
 
@@ -122,18 +118,16 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm
 
 			texel = source[yfrac>>FRACBITS];
 
-			if (firetranslucent && (transtables[(texel<<8)+0x40000]!=texel))
-				alpha = 0x80;
-			else
-				alpha = 0xff;
-
-			//Hurdler: not perfect, but better than holes
-			if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX && (mipmap->flags & TF_CHROMAKEYED))
-				texel = HWR_CHROMAKEY_EQUIVALENTCOLORINDEX;
 			//Hurdler: 25/04/2000: now support colormap in hardware mode
-			else if (mipmap->colormap)
+			if (mipmap->colormap)
 				texel = mipmap->colormap[texel];
 
+			// transparent pixel
+			if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)
+				alpha = 0x00;
+			else
+				alpha = 0xff;
+
 			// hope compiler will get this switch out of the loops (dreams...)
 			// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
 			// Alam: SRB2 uses Mingw, HUGS
@@ -236,18 +230,16 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block,
 
 			texel = source[yfrac>>FRACBITS];
 
-			if (firetranslucent && (transtables[(texel<<8)+0x40000]!=texel))
-				alpha = 0x80;
-			else
-				alpha = 0xff;
-
-			//Hurdler: not perfect, but better than holes
-			if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX && (mipmap->flags & TF_CHROMAKEYED))
-				texel = HWR_CHROMAKEY_EQUIVALENTCOLORINDEX;
 			//Hurdler: 25/04/2000: now support colormap in hardware mode
-			else if (mipmap->colormap)
+			if (mipmap->colormap)
 				texel = mipmap->colormap[texel];
 
+			// transparent pixel
+			if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX)
+				alpha = 0x00;
+			else
+				alpha = 0xff;
+
 			// hope compiler will get this switch out of the loops (dreams...)
 			// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
 			// Alam: SRB2 uses Mingw, HUGS
@@ -576,7 +568,7 @@ static UINT8 *MakeBlock(GLMipmap_t *grMipmap)
 {
 	UINT8 *block;
 	INT32 bpp, i;
-	UINT16 bu16 = ((0x00 <<8) | HWR_CHROMAKEY_EQUIVALENTCOLORINDEX);
+	UINT16 bu16 = ((0x00 <<8) | HWR_PATCHES_CHROMAKEY_COLORINDEX);
 
 	bpp =  format2bpp[grMipmap->grInfo.format];
 	block = Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(grMipmap->grInfo.data));
@@ -606,6 +598,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
 	texture_t *texture;
 	texpatch_t *patch;
 	patch_t *realpatch;
+	UINT8 *pdata;
 
 	INT32 i;
 	boolean skyspecial = false; //poor hack for Legacy large skies..
@@ -638,7 +631,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
 		INT32 j;
 		RGBA_t col;
 
-		col = V_GetColor(HWR_CHROMAKEY_EQUIVALENTCOLORINDEX);
+		col = V_GetColor(HWR_PATCHES_CHROMAKEY_COLORINDEX);
 		for (j = 0; j < blockheight; j++)
 		{
 			for (i = 0; i < blockwidth; i++)
@@ -654,19 +647,30 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
 	// Composite the columns together.
 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
 	{
-#ifndef NO_PNG_LUMPS
+		boolean dealloc = true;
 		size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump);
-#endif
-		realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
+		pdata = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
+		realpatch = (patch_t *)pdata;
+
 #ifndef NO_PNG_LUMPS
 		if (R_IsLumpPNG((UINT8 *)realpatch, lumplength))
 			realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL, false);
+		else
 #endif
-		HWR_DrawTexturePatchInCache(&grtex->mipmap,
-		                     blockwidth, blockheight,
-		                     texture, patch,
-		                     realpatch);
-		Z_Unlock(realpatch);
+#ifdef WALLFLATS
+		if (texture->type == TEXTURETYPE_FLAT)
+			realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false);
+		else
+#endif
+		{
+			(void)lumplength;
+			dealloc = false;
+		}
+
+		HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, realpatch);
+
+		if (dealloc)
+			Z_Unlock(realpatch);
 	}
 	//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
 	if (format2bpp[grtex->mipmap.grInfo.format]==4)
@@ -760,35 +764,60 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm
 //             CACHING HANDLING
 // =================================================
 
-static size_t gr_numtextures;
-static GLTexture_t *gr_textures; // for ALL Doom textures
-static GLTexture_t *gr_textures2;
+static size_t gr_numtextures = 0; // Texture count
+static GLTexture_t *gr_textures; // For all textures
+static GLTexture_t *gr_flats; // For all (texture) flats, as normal flats don't need to be cached
 
 void HWR_InitTextureCache(void)
 {
-	gr_numtextures = 0;
 	gr_textures = NULL;
-	gr_textures2 = NULL;
+	gr_flats = NULL;
 }
 
-
 // Callback function for HWR_FreeTextureCache.
 static void FreeMipmapColormap(INT32 patchnum, void *patch)
 {
-	GLPatch_t* const grpatch = patch;
+	GLPatch_t* const pat = patch;
 	(void)patchnum; //unused
-	while (grpatch->mipmap->nextcolormap)
+
+	// The patch must be valid, obviously
+	if (!pat)
+		return;
+
+	// The mipmap must be valid, obviously
+	while (pat->mipmap)
 	{
-		GLMipmap_t *grmip = grpatch->mipmap->nextcolormap;
-		grpatch->mipmap->nextcolormap = grmip->nextcolormap;
-		if (grmip->grInfo.data) Z_Free(grmip->grInfo.data);
-		free(grmip);
+		// Confusing at first, but pat->mipmap->nextcolormap
+		// at the beginning of the loop is the first colormap
+		// from the linked list of colormaps.
+		GLMipmap_t *next = NULL;
+
+		// No mipmap in this patch, break out of the loop.
+		if (!pat->mipmap)
+			break;
+
+		// No colormap mipmap either.
+		if (!pat->mipmap->nextcolormap)
+			break;
+
+		// Set the first colormap to the one that comes after it.
+		next = pat->mipmap->nextcolormap;
+		pat->mipmap->nextcolormap = next->nextcolormap;
+
+		// Free image data from memory.
+		if (next->grInfo.data)
+			Z_Free(next->grInfo.data);
+		next->grInfo.data = NULL;
+
+		// Free the old colormap mipmap from memory.
+		free(next);
 	}
 }
 
-void HWR_FreeTextureCache(void)
+void HWR_FreeMipmapCache(void)
 {
 	INT32 i;
+
 	// free references to the textures
 	HWD.pfnClearMipMapCache();
 
@@ -798,51 +827,46 @@ void HWR_FreeTextureCache(void)
 	Z_FreeTag(PU_HWRCACHE_UNLOCKED);
 
 	// Alam: free the Z_Blocks before freeing it's users
-
-	// free all skin after each level: must be done after pfnClearMipMapCache!
+	// free all patch colormaps after each level: must be done after ClearMipMapCache!
 	for (i = 0; i < numwadfiles; i++)
 		M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap);
+}
+
+void HWR_FreeTextureCache(void)
+{
+	// free references to the textures
+	HWR_FreeMipmapCache();
 
 	// now the heap don't have any 'user' pointing to our
 	// texturecache info, we can free it
 	if (gr_textures)
 		free(gr_textures);
-	if (gr_textures2)
-		free(gr_textures2);
+	if (gr_flats)
+		free(gr_flats);
 	gr_textures = NULL;
-	gr_textures2 = NULL;
+	gr_flats = NULL;
 	gr_numtextures = 0;
 }
 
-void HWR_PrepLevelCache(size_t pnumtextures)
+void HWR_LoadTextures(size_t pnumtextures)
 {
-	// problem: the mipmap cache management hold a list of mipmaps.. but they are
-	//           reallocated on each level..
-	//sub-optimal, but 1) just need re-download stuff in hardware cache VERY fast
-	//   2) sprite/menu stuff mixed with level textures so can't do anything else
-
 	// we must free it since numtextures changed
 	HWR_FreeTextureCache();
 
+	// Why not Z_Malloc?
 	gr_numtextures = pnumtextures;
-	gr_textures = calloc(pnumtextures, sizeof (*gr_textures));
-	if (gr_textures == NULL)
-		I_Error("3D can't alloc gr_textures");
-	gr_textures2 = calloc(pnumtextures, sizeof (*gr_textures2));
-	if (gr_textures2 == NULL)
-		I_Error("3D can't alloc gr_textures2");
+	gr_textures = calloc(gr_numtextures, sizeof(*gr_textures));
+	gr_flats = calloc(gr_numtextures, sizeof(*gr_flats));
+
+	// Doesn't tell you which it _is_, but hopefully
+	// should never ever happen (right?!)
+	if ((gr_textures == NULL) || (gr_flats == NULL))
+		I_Error("HWR_LoadTextures: ran out of memory for OpenGL textures. Sad!");
 }
 
 void HWR_SetPalette(RGBA_t *palette)
 {
-	//Hudler: 16/10/99: added for OpenGL gamma correction
-	RGBA_t gamma_correction = {0x7F7F7F7F};
-
-	//Hurdler 16/10/99: added for OpenGL gamma correction
-	gamma_correction.s.red   = (UINT8)cv_grgammared.value;
-	gamma_correction.s.green = (UINT8)cv_grgammagreen.value;
-	gamma_correction.s.blue  = (UINT8)cv_grgammablue.value;
-	HWD.pfnSetPalette(palette, &gamma_correction);
+	HWD.pfnSetPalette(palette);
 
 	// hardware driver will flush there own cache if cache is non paletized
 	// now flush data texture cache so 32 bit texture are recomputed
@@ -863,11 +887,16 @@ GLTexture_t *HWR_GetTexture(INT32 tex)
 	if ((unsigned)tex >= gr_numtextures)
 		I_Error("HWR_GetTexture: tex >= numtextures\n");
 #endif
+
+	// Every texture in memory, stored in the
+	// hardware renderer's bit depth format. Wow!
 	grtex = &gr_textures[tex];
 
+	// Generate texture if missing from the cache
 	if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded)
 		HWR_GenerateTexture(tex, grtex);
 
+	// Tell the hardware driver to bind the current texture to the flat's mipmap
 	HWD.pfnSetTexture(&grtex->mipmap);
 
 	// The system-memory data can be purged now.
@@ -928,6 +957,9 @@ static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum)
 {
 	UINT8 *flat;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	// setup the texture info
 #ifdef GLIDE_API_COMPATIBILITY
 	grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64;
@@ -953,6 +985,9 @@ void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum)
 	if (flatlumpnum == LUMPERROR)
 		return;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	grmip = HWR_GetCachedGLPatch(flatlumpnum)->mipmap;
 	if (!grmip->downloaded && !grmip->grInfo.data)
 		HWR_CacheFlat(grmip, flatlumpnum);
@@ -979,13 +1014,19 @@ void HWR_GetLevelFlat(levelflat_t *levelflat)
 		if ((unsigned)texturenum >= gr_numtextures)
 			I_Error("HWR_GetLevelFlat: texturenum >= numtextures\n");
 #endif
+
+		// Who knows?
 		if (texturenum == 0 || texturenum == -1)
 			return;
-		grtex = &gr_textures2[texturenum];
 
+		// Every texture in memory, stored as a 8-bit flat. Wow!
+		grtex = &gr_flats[texturenum];
+
+		// Generate flat if missing from the cache
 		if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded)
 			HWR_CacheTextureAsFlat(&grtex->mipmap, texturenum);
 
+		// Tell the hardware driver to bind the current texture to the flat's mipmap
 		HWD.pfnSetTexture(&grtex->mipmap);
 
 		// The system-memory data can be purged now.
@@ -1009,6 +1050,7 @@ static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch)
 		HWR_MakePatch(patch, gpatch, grmip, true);
 
 		// You can't free rawpatch for some reason?
+		// (Obviously I can't, sprite rotation needs that...)
 		if (!gpatch->rawpatch)
 			Z_Free(patch);
 	}
@@ -1024,6 +1066,9 @@ static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch)
 // -----------------+
 void HWR_GetPatch(GLPatch_t *gpatch)
 {
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	// is it in hardware cache
 	if (!gpatch->mipmap->downloaded && !gpatch->mipmap->grInfo.data)
 	{
@@ -1036,7 +1081,6 @@ void HWR_GetPatch(GLPatch_t *gpatch)
 
 		// this is inefficient.. but the hardware patch in heap is purgeable so it should
 		// not fragment memory, and besides the REAL cache here is the hardware memory
-		// You can't free rawpatch for some reason?
 		if (!gpatch->rawpatch)
 			Z_Free(ptr);
 	}
@@ -1055,6 +1099,9 @@ void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap)
 {
 	GLMipmap_t *grmip, *newmip;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	if (colormap == colormaps || colormap == NULL)
 	{
 		// Load the default (green) color in doom cache (temporary?) AND hardware cache
@@ -1180,6 +1227,9 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
 {
 	GLPatch_t *grpatch;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	grpatch = HWR_GetCachedGLPatch(lumpnum);
 
 	if (!grpatch->mipmap->downloaded && !grpatch->mipmap->grInfo.data)
@@ -1383,6 +1433,9 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
 {
 	GLMipmap_t *grmip;
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	grmip = HWR_GetCachedGLPatch(fademasklumpnum)->mipmap;
 
 	if (!grmip->downloaded && !grmip->grInfo.data)
diff --git a/src/hardware/hw_data.h b/src/hardware/hw_data.h
index ef57426a4b66b484d3285a2a39f92dd7be14a474..f525e041f2a50a4f97c713061497412748a377bb 100644
--- a/src/hardware/hw_data.h
+++ b/src/hardware/hw_data.h
@@ -48,6 +48,7 @@ struct GLMipmap_s
 
 	struct GLMipmap_s    *nextcolormap;
 	const UINT8          *colormap;
+	INT32                tcindex;
 
 	// opengl
 	struct GLMipmap_s *nextmipmap; // opengl : liste of all texture in opengl driver
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index 965f06980edb0e3843ef8f2b2a07a861243116f1..95882b03eb93bb57fc429a12c648fdd8f5167c7b 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -42,7 +42,7 @@ typedef unsigned char   FBOOLEAN;
 
 // byte value for paletted graphics, which represent the transparent color
 #define HWR_PATCHES_CHROMAKEY_COLORINDEX   255
-#define HWR_CHROMAKEY_EQUIVALENTCOLORINDEX 130
+//#define HWR_CHROMAKEY_EQUIVALENTCOLORINDEX 130
 
 // the chroma key color shows on border sprites, set it to black
 #define HWR_PATCHES_CHROMAKEY_COLORVALUE     (0x00000000)    //RGBA format as in grSstWinOpen()
@@ -115,13 +115,11 @@ typedef struct
 	FLOAT       fovxangle, fovyangle;
 	UINT8       splitscreen;
 	boolean     flip;            // screenflip
-#ifdef ROTSPRITE
 	boolean     roll;
 	SINT8       rollflip;
 	FLOAT       rollangle; // done to not override USE_FTRANSFORM_ANGLEZ
 	UINT8       rotaxis;
 	FLOAT       centerx, centery;
-#endif
 #ifdef USE_FTRANSFORM_MIRROR
 	boolean     mirror;          // SRB2Kart: Encore Mode
 #endif
@@ -220,11 +218,10 @@ typedef struct FSurfaceInfo FSurfaceInfo;
 //Hurdler: added for backward compatibility
 enum hwdsetspecialstate
 {
-	HWD_SET_FOG_TABLE = 1,
+	HWD_SET_MODEL_LIGHTING = 1,
 	HWD_SET_FOG_MODE,
 	HWD_SET_FOG_COLOR,
 	HWD_SET_FOG_DENSITY,
-	HWD_SET_FOV,
 	HWD_SET_TEXTUREFILTERMODE,
 	HWD_SET_TEXTUREANISOTROPICMODE,
 	HWD_NUMSTATE
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 4519e1280018d4f69015cba9e32b4df015d190bb..598a635aa47c18b4b79697efdc131e601ab97607 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -992,7 +992,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// top edge
 	if (clearlines > basewindowy - 8)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_T], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_T], PU_PATCH);
 		for (x = 0; x < baseviewwidth; x += 8)
 			HWR_DrawPatch(patch, basewindowx + x, basewindowy - 8,
 				0);
@@ -1001,7 +1001,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// bottom edge
 	if (clearlines > basewindowy + baseviewheight)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_B], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_B], PU_PATCH);
 		for (x = 0; x < baseviewwidth; x += 8)
 			HWR_DrawPatch(patch, basewindowx + x,
 				basewindowy + baseviewheight, 0);
@@ -1010,7 +1010,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// left edge
 	if (clearlines > basewindowy)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_L], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_L], PU_PATCH);
 		for (y = 0; y < baseviewheight && basewindowy + y < clearlines;
 			y += 8)
 		{
@@ -1022,7 +1022,7 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// right edge
 	if (clearlines > basewindowy)
 	{
-		patch = W_CachePatchNum(viewborderlump[BRDR_R], PU_CACHE);
+		patch = W_CachePatchNum(viewborderlump[BRDR_R], PU_PATCH);
 		for (y = 0; y < baseviewheight && basewindowy+y < clearlines;
 			y += 8)
 		{
@@ -1034,22 +1034,22 @@ void HWR_DrawViewBorder(INT32 clearlines)
 	// Draw beveled corners.
 	if (clearlines > basewindowy - 8)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_TL],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx - 8, basewindowy - 8, 0);
 
 	if (clearlines > basewindowy - 8)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_TR],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx + baseviewwidth, basewindowy - 8, 0);
 
 	if (clearlines > basewindowy+baseviewheight)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_BL],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx - 8, basewindowy + baseviewheight, 0);
 
 	if (clearlines > basewindowy + baseviewheight)
 		HWR_DrawPatch(W_CachePatchNum(viewborderlump[BRDR_BR],
-				PU_CACHE),
+				PU_PATCH),
 			basewindowx + baseviewwidth,
 			basewindowy + baseviewheight, 0);
 }
diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h
index aed1611f111176ba0ab9c907362e154421894825..3314fb01587a8be020b345810d49805e13d981ec 100644
--- a/src/hardware/hw_drv.h
+++ b/src/hardware/hw_drv.h
@@ -39,11 +39,7 @@ EXPORT void HWRAPI(Shutdown) (void);
 #ifdef _WINDOWS
 EXPORT void HWRAPI(GetModeList) (vmode_t **pvidmodes, INT32 *numvidmodes);
 #endif
-#if defined (PURESDL) || defined (macintosh)
-EXPORT void HWRAPI(SetPalette) (INT32 *, RGBA_t *gamma);
-#else
-EXPORT void HWRAPI(SetPalette) (RGBA_t *ppal, RGBA_t *pgamma);
-#endif
+EXPORT void HWRAPI(SetPalette) (RGBA_t *ppal);
 EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl);
 EXPORT void HWRAPI(Draw2DLine) (F2DCoord *v1, F2DCoord *v2, RGBA_t Color);
 EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags);
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 69cf8bf46c1e2549ec356cf5e9048e1fa66e3917..cf98e731739ce59016dd6347f531a1079c59f813 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -100,6 +100,7 @@ void HWR_FreePolyPool(void);
 // --------
 void HWR_InitTextureCache(void);
 void HWR_FreeTextureCache(void);
+void HWR_FreeMipmapCache(void);
 void HWR_FreeExtraSubsectors(void);
 
 void HWR_GetLevelFlat(levelflat_t *levelflat);
@@ -127,6 +128,5 @@ extern consvar_t cv_grrounddown; // on/off
 
 extern INT32 patchformat;
 extern INT32 textureformat;
-extern boolean firetranslucent;
 
 #endif //_HW_GLOB_
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index 4df71d145903532f9336ab2b18c6e7ee5cd6d94b..8545a88b33e05e19bdb18fe23936fb2ed1c39195 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -374,7 +374,6 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_ADST
 	&lspr[NOLIGHT],     // SPR_MCRT
 	&lspr[NOLIGHT],     // SPR_MCSP
-	&lspr[NOLIGHT],     // SPR_NON2
 	&lspr[NOLIGHT],     // SPR_SALD
 	&lspr[NOLIGHT],     // SPR_TRAE
 	&lspr[NOLIGHT],     // SPR_TRAI
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 51c976973d6a0db7aaeeaaa7e055af3592497fce..2f986085a0a38765fd7192f18c91d3cde96bccc3 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -85,62 +85,8 @@ static void HWR_RenderTransparentWalls(void);
 static void HWR_FoggingOn(void);
 static UINT32 atohex(const char *s);
 
-static void CV_filtermode_ONChange(void);
-static void CV_anisotropic_ONChange(void);
-static void CV_FogDensity_ONChange(void);
-static void CV_grFov_OnChange(void);
-// ==========================================================================
-//                                          3D ENGINE COMMANDS & CONSOLE VARS
-// ==========================================================================
-
-static CV_PossibleValue_t grfov_cons_t[] = {{0, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}};
-static CV_PossibleValue_t grfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSAMPLED, "Nearest"},
-	{HWD_SET_TEXTUREFILTER_BILINEAR, "Bilinear"}, {HWD_SET_TEXTUREFILTER_TRILINEAR, "Trilinear"},
-	{HWD_SET_TEXTUREFILTER_MIXED1, "Linear_Nearest"},
-	{HWD_SET_TEXTUREFILTER_MIXED2, "Nearest_Linear"},
-	{HWD_SET_TEXTUREFILTER_MIXED3, "Nearest_Mipmap"},
-	{0, NULL}};
-CV_PossibleValue_t granisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NULL}};
-
 boolean drawsky = true;
 
-// needs fix: walls are incorrectly clipped one column less
-#ifndef NEWCLIP
-static consvar_t cv_grclipwalls = {"gr_clipwalls", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-#endif
-//development variables for diverse uses
-static consvar_t cv_gralpha = {"gr_alpha", "160", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
-static consvar_t cv_grbeta = {"gr_beta", "0", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
-
-consvar_t cv_grrounddown = {"gr_rounddown", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grfov = {"gr_fov", "90", CV_FLOAT|CV_CALL, grfov_cons_t, CV_grFov_OnChange, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grfogdensity = {"gr_fogdensity", "150", CV_CALL|CV_NOINIT, CV_Unsigned,
-                             CV_FogDensity_ONChange, 0, NULL, NULL, 0, 0, NULL};
-
-// Unfortunately, this can no longer be saved..
-consvar_t cv_grfiltermode = {"gr_filtermode", "Nearest", CV_CALL, grfiltermode_cons_t,
-                             CV_filtermode_ONChange, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_granisotropicmode = {"gr_anisotropicmode", "1", CV_CALL, granisotropicmode_cons_t,
-                             CV_anisotropic_ONChange, 0, NULL, NULL, 0, 0, NULL};
-//static consvar_t cv_grzbuffer = {"gr_zbuffer", "On", 0, CV_OnOff};
-consvar_t cv_grcorrecttricks = {"gr_correcttricks", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grsolvetjoin = {"gr_solvetjoin", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-
-static void CV_FogDensity_ONChange(void)
-{
-	HWD.pfnSetSpecialState(HWD_SET_FOG_DENSITY, cv_grfogdensity.value);
-}
-
-static void CV_filtermode_ONChange(void)
-{
-	HWD.pfnSetSpecialState(HWD_SET_TEXTUREFILTERMODE, cv_grfiltermode.value);
-}
-
-static void CV_anisotropic_ONChange(void)
-{
-	HWD.pfnSetSpecialState(HWD_SET_TEXTUREANISOTROPICMODE, cv_granisotropicmode.value);
-}
-
 /*
  * lookuptable for lightvalues
  * calculated as follow:
@@ -886,7 +832,7 @@ static void HWR_DrawSegsSplats(FSurfaceInfo * pSurf)
 		if (!M_PointInBox(segbbox,splat->v1.x,splat->v1.y) && !M_PointInBox(segbbox,splat->v2.x,splat->v2.y))
 			continue;
 
-		gpatch = W_CachePatchNum(splat->patch, PU_CACHE);
+		gpatch = W_CachePatchNum(splat->patch, PU_PATCH);
 		HWR_GetPatch(gpatch);
 
 		wallVerts[0].x = wallVerts[3].x = FIXED_TO_FLOAT(splat->v1.x);
@@ -2473,11 +2419,13 @@ static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacks
 static cliprange_t *   hw_newend;
 static cliprange_t     gr_solidsegs[MAXSEGS];
 
+// needs fix: walls are incorrectly clipped one column less
+static consvar_t cv_grclipwalls = {"gr_clipwalls", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 static void printsolidsegs(void)
 {
 	cliprange_t *       start;
-	if (!hw_newend || cv_grbeta.value != 2)
+	if (!hw_newend)
 		return;
 	for (start = gr_solidsegs;start != hw_newend;start++)
 	{
@@ -3431,7 +3379,7 @@ static void HWR_AddPolyObjectPlanes(void)
 			{
 				HWR_GetLevelFlat(&levelflats[polyobjsector->ceilingpic]);
 				HWR_RenderPolyObjectPlane(po_ptrs[i], true, polyobjsector->ceilingheight, PF_Occlude,
-				                          (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), &levelflats[polyobjsector->floorpic],
+				                          (light == -1 ? gr_frontsector->lightlevel : *gr_frontsector->lightlist[light].lightlevel), &levelflats[polyobjsector->ceilingpic],
 				                          polyobjsector, 255, (light == -1 ? gr_frontsector->extra_colormap : *gr_frontsector->lightlist[light].extra_colormap));
 			}
 		}
@@ -3823,11 +3771,16 @@ static void HWR_Subsector(size_t num)
 
 		while (count--)
 		{
+
+			if (!line->glseg
 #ifdef POLYOBJECTS
-				if (!line->polyseg) // ignore segs that belong to polyobjects
+			    && !line->polyseg // ignore segs that belong to polyobjects
 #endif
+			)
+			{
 				HWR_AddLine(line);
-				line++;
+			}
+			line++;
 		}
 	}
 
@@ -5420,14 +5373,20 @@ static void HWR_DrawSprites(void)
 					if (!cv_grmodels.value || md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f)
 						HWR_DrawSprite(spr);
 					else
-						HWR_DrawModel(spr);
+					{
+						if (!HWR_DrawModel(spr))
+							HWR_DrawSprite(spr);
+					}
 				}
 				else
 				{
 					if (!cv_grmodels.value || md2_models[spr->mobj->sprite].notfound || md2_models[spr->mobj->sprite].scale < 0.0f)
 						HWR_DrawSprite(spr);
 					else
-						HWR_DrawModel(spr);
+					{
+						if (!HWR_DrawModel(spr))
+							HWR_DrawSprite(spr);
+					}
 				}
 		}
 	}
@@ -5547,17 +5506,12 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	fixed_t spr_offset, spr_topoffset;
 #ifdef ROTSPRITE
 	patch_t *rotsprite = NULL;
-	angle_t arollangle;
-	UINT32 rollangle;
+	INT32 rollangle = 0;
 #endif
 
 	if (!thing)
 		return;
 
-#ifdef ROTSPRITE
-	arollangle = thing->rollangle;
-	rollangle = AngleFixed(arollangle)>>FRACBITS;
-#endif
 	this_scale = FIXED_TO_FLOAT(thing->scale);
 
 	// transform the origin point
@@ -5664,11 +5618,11 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	spr_topoffset = spritecachedinfo[lumpoff].topoffset;
 
 #ifdef ROTSPRITE
-	if (rollangle > 0)
+	if (thing->rollangle)
 	{
+		rollangle = R_GetRollAngle(thing->rollangle);
 		if (!sprframe->rotsprite.cached[rot])
 			R_CacheRotSprite(thing->sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, flip);
-		rollangle /= ROTANGDIFF;
 		rotsprite = sprframe->rotsprite.patch[rot][rollangle];
 		if (rotsprite != NULL)
 		{
@@ -5941,7 +5895,7 @@ static void HWR_DrawSkyBackground(player_t *player)
 {
 	if (cv_grskydome.value)
 	{
-		FTransform transform;
+		FTransform dometransform;
 		const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd);
 		postimg_t *type;
 
@@ -5950,27 +5904,27 @@ static void HWR_DrawSkyBackground(player_t *player)
 		else
 			type = &postimgtype;
 
-		memset(&transform, 0x00, sizeof(FTransform));
+		memset(&dometransform, 0x00, sizeof(FTransform));
 
 		//04/01/2000: Hurdler: added for T&L
 		//                     It should replace all other gr_viewxxx when finished
-		transform.anglex = (float)(aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
-		transform.angley = (float)((viewangle-ANGLE_270)>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
+		dometransform.anglex = (float)(aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
+		dometransform.angley = (float)((viewangle-ANGLE_270)>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
 
 		if (*type == postimg_flip)
-			transform.flip = true;
+			dometransform.flip = true;
 		else
-			transform.flip = false;
+			dometransform.flip = false;
 
-		transform.scalex = 1;
-		transform.scaley = (float)vid.width/vid.height;
-		transform.scalez = 1;
-		transform.fovxangle = fpov; // Tails
-		transform.fovyangle = fpov; // Tails
-		transform.splitscreen = splitscreen;
+		dometransform.scalex = 1;
+		dometransform.scaley = (float)vid.width/vid.height;
+		dometransform.scalez = 1;
+		dometransform.fovxangle = fpov; // Tails
+		dometransform.fovyangle = fpov; // Tails
+		dometransform.splitscreen = splitscreen;
 
 		HWR_GetTexture(texturetranslation[skytexture]);
-		HWD.pfnRenderSkyDome(skytexture, textures[skytexture]->width, textures[skytexture]->height, transform);
+		HWD.pfnRenderSkyDome(skytexture, textures[skytexture]->width, textures[skytexture]->height, dometransform);
 	}
 	else
 	{
@@ -6172,6 +6126,8 @@ void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player)
 
 	//04/01/2000: Hurdler: added for T&L
 	//                     It should replace all other gr_viewxxx when finished
+	memset(&atransform, 0x00, sizeof(FTransform));
+
 	atransform.anglex = (float)(aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
 	atransform.angley = (float)(viewangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
 
@@ -6390,6 +6346,8 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
 
 	//04/01/2000: Hurdler: added for T&L
 	//                     It should replace all other gr_viewxxx when finished
+	memset(&atransform, 0x00, sizeof(FTransform));
+
 	atransform.anglex = (float)(aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
 	atransform.angley = (float)(viewangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES);
 
@@ -6584,59 +6542,131 @@ static void HWR_FoggingOn(void)
 //                                                         3D ENGINE COMMANDS
 // ==========================================================================
 
+static CV_PossibleValue_t grsoftwarefog_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "LightPlanes"}, {0, NULL}};
+static CV_PossibleValue_t grmodelinterpolation_cons_t[] = {{0, "Off"}, {1, "Sometimes"}, {2, "Always"}, {0, NULL}};
+
+static void CV_grmodellighting_OnChange(void);
+static void CV_grfiltermode_OnChange(void);
+static void CV_granisotropic_OnChange(void);
+static void CV_grfogdensity_OnChange(void);
+static void CV_grfov_OnChange(void);
+
+static CV_PossibleValue_t grfov_cons_t[] = {{0, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}};
+static CV_PossibleValue_t grfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSAMPLED, "Nearest"},
+	{HWD_SET_TEXTUREFILTER_BILINEAR, "Bilinear"}, {HWD_SET_TEXTUREFILTER_TRILINEAR, "Trilinear"},
+	{HWD_SET_TEXTUREFILTER_MIXED1, "Linear_Nearest"},
+	{HWD_SET_TEXTUREFILTER_MIXED2, "Nearest_Linear"},
+	{HWD_SET_TEXTUREFILTER_MIXED3, "Nearest_Mipmap"},
+	{0, NULL}};
+CV_PossibleValue_t granisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NULL}};
+
+consvar_t cv_grfovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grfog = {"gr_fog", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grfogcolor = {"gr_fogcolor", "AAAAAA", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grsoftwarefog = {"gr_softwarefog", "Off", CV_SAVE, grsoftwarefog_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+#ifdef ALAM_LIGHTING
+consvar_t cv_grdynamiclighting = {"gr_dynamiclighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grstaticlighting  = {"gr_staticlighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grcoronas = {"gr_coronas", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grcoronasize = {"gr_coronasize", "1", CV_SAVE|CV_FLOAT, 0, NULL, 0, NULL, NULL, 0, 0, NULL};
+#endif
+
+consvar_t cv_grmodels = {"gr_models", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grmodelinterpolation = {"gr_modelinterpolation", "Sometimes", CV_SAVE, grmodelinterpolation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grmodellighting = {"gr_modellighting", "Off", CV_SAVE|CV_CALL, CV_OnOff, CV_grmodellighting_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
+consvar_t cv_grspritebillboarding = {"gr_spritebillboarding", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grskydome = {"gr_skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+consvar_t cv_grrounddown = {"gr_rounddown", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grfov = {"gr_fov", "90", CV_FLOAT|CV_CALL, grfov_cons_t, CV_grfov_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grfogdensity = {"gr_fogdensity", "150", CV_CALL|CV_NOINIT, CV_Unsigned,
+                             CV_grfogdensity_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
+consvar_t cv_grfiltermode = {"gr_filtermode", "Nearest", CV_SAVE|CV_CALL, grfiltermode_cons_t,
+                             CV_grfiltermode_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_granisotropicmode = {"gr_anisotropicmode", "1", CV_CALL, granisotropicmode_cons_t,
+                             CV_granisotropic_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
+consvar_t cv_grcorrecttricks = {"gr_correcttricks", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_grsolvetjoin = {"gr_solvetjoin", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
-static void CV_grFov_OnChange(void)
+static void CV_grmodellighting_OnChange(void)
 {
-	if ((netgame || multiplayer) && !cv_debug && cv_grfov.value != 90*FRACUNIT)
-		CV_Set(&cv_grfov, cv_grfov.defaultvalue);
+	if (rendermode == render_opengl)
+		HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, cv_grmodellighting.value);
 }
 
-static void Command_GrStats_f(void)
+static void CV_grfogdensity_OnChange(void)
 {
-	Z_CheckHeap(9875); // debug
+	if (rendermode == render_opengl)
+		HWD.pfnSetSpecialState(HWD_SET_FOG_DENSITY, cv_grfogdensity.value);
+}
 
-	CONS_Printf(M_GetText("Patch info headers: %7s kb\n"), sizeu1(Z_TagUsage(PU_HWRPATCHINFO)>>10));
-	CONS_Printf(M_GetText("3D Texture cache  : %7s kb\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10));
-	CONS_Printf(M_GetText("Plane polygon     : %7s kb\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10));
+static void CV_grfiltermode_OnChange(void)
+{
+	if (rendermode == render_opengl)
+		HWD.pfnSetSpecialState(HWD_SET_TEXTUREFILTERMODE, cv_grfiltermode.value);
 }
 
+static void CV_granisotropic_OnChange(void)
+{
+	if (rendermode == render_opengl)
+		HWD.pfnSetSpecialState(HWD_SET_TEXTUREANISOTROPICMODE, cv_granisotropicmode.value);
+}
 
-// **************************************************************************
-//                                                            3D ENGINE SETUP
-// **************************************************************************
+static void CV_grfov_OnChange(void)
+{
+	if ((netgame || multiplayer) && !cv_debug && cv_grfov.value != 90*FRACUNIT)
+		CV_Set(&cv_grfov, cv_grfov.defaultvalue);
+}
 
-// --------------------------------------------------------------------------
-// Add hardware engine commands & consvars
-// --------------------------------------------------------------------------
 //added by Hurdler: console varibale that are saved
 void HWR_AddCommands(void)
 {
-	CV_RegisterVar(&cv_grrounddown);
+	CV_RegisterVar(&cv_grfovchange);
 	CV_RegisterVar(&cv_grfov);
+
 	CV_RegisterVar(&cv_grfogdensity);
+	CV_RegisterVar(&cv_grfogcolor);
+	CV_RegisterVar(&cv_grfog);
+	CV_RegisterVar(&cv_grsoftwarefog);
+
+#ifdef ALAM_LIGHTING
+	CV_RegisterVar(&cv_grstaticlighting);
+	CV_RegisterVar(&cv_grdynamiclighting);
+	CV_RegisterVar(&cv_grcoronasize);
+	CV_RegisterVar(&cv_grcoronas);
+#endif
+
+	CV_RegisterVar(&cv_grmodellighting);
+	CV_RegisterVar(&cv_grmodelinterpolation);
+	CV_RegisterVar(&cv_grmodels);
+
+	CV_RegisterVar(&cv_grskydome);
+	CV_RegisterVar(&cv_grspritebillboarding);
+
 	CV_RegisterVar(&cv_grfiltermode);
-	CV_RegisterVar(&cv_granisotropicmode);
+	CV_RegisterVar(&cv_grrounddown);
 	CV_RegisterVar(&cv_grcorrecttricks);
 	CV_RegisterVar(&cv_grsolvetjoin);
-}
 
-static inline void HWR_AddEngineCommands(void)
-{
-	// engine state variables
-	//CV_RegisterVar(&cv_grzbuffer);
 #ifndef NEWCLIP
 	CV_RegisterVar(&cv_grclipwalls);
 #endif
+}
 
-	// engine development mode variables
-	// - usage may vary from version to version..
-	CV_RegisterVar(&cv_gralpha);
-	CV_RegisterVar(&cv_grbeta);
+void HWR_AddSessionCommands(void)
+{
+	static boolean alreadycalled = false;
+	if (alreadycalled)
+		return;
 
-	// engine commands
-	COM_AddCommand("gr_stats", Command_GrStats_f);
-}
+	CV_RegisterVar(&cv_granisotropicmode);
 
+	alreadycalled = true;
+}
 
 // --------------------------------------------------------------------------
 // Setup the hardware renderer
@@ -6657,12 +6687,9 @@ void HWR_Startup(void)
 	{
 		CONS_Printf("HWR_Startup()...\n");
 		HWR_InitPolyPool();
-		// add console cmds & vars
-		HWR_AddEngineCommands();
+		HWR_AddSessionCommands();
 		HWR_InitTextureCache();
-
 		HWR_InitModels();
-
 #ifdef ALAM_LIGHTING
 		HWR_InitLight();
 #endif
@@ -6674,6 +6701,17 @@ void HWR_Startup(void)
 	startupdone = true;
 }
 
+// --------------------------------------------------------------------------
+// Called after switching to the hardware renderer
+// --------------------------------------------------------------------------
+void HWR_Switch(void)
+{
+	// Set special states from CVARs
+	HWD.pfnSetSpecialState(HWD_SET_MODEL_LIGHTING, cv_grmodellighting.value);
+	HWD.pfnSetSpecialState(HWD_SET_FOG_DENSITY, cv_grfogdensity.value);
+	HWD.pfnSetSpecialState(HWD_SET_TEXTUREFILTERMODE, cv_grfiltermode.value);
+	HWD.pfnSetSpecialState(HWD_SET_TEXTUREANISOTROPICMODE, cv_granisotropicmode.value);
+}
 
 // --------------------------------------------------------------------------
 // Free resources allocated by the hardware renderer
@@ -6683,6 +6721,7 @@ void HWR_Shutdown(void)
 	CONS_Printf("HWR_Shutdown()\n");
 	HWR_FreeExtraSubsectors();
 	HWR_FreePolyPool();
+	HWR_FreeMipmapCache();
 	HWR_FreeTextureCache();
 	HWD.pfnFlushScreenTextures();
 }
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index e19c557d03ecfd3821a24b17d69da945a2b57b44..5733382263691a6f28d410d023c604b384a9d257 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -29,6 +29,7 @@
 
 // Startup & Shutdown the hardware mode renderer
 void HWR_Startup(void);
+void HWR_Switch(void);
 void HWR_Shutdown(void);
 
 void HWR_drawAMline(const fline_t *fl, INT32 color);
@@ -47,7 +48,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale
 void HWR_MakePatch(const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap);
 void HWR_CreatePlanePolygons(INT32 bspnum);
 void HWR_CreateStaticLightmaps(INT32 bspnum);
-void HWR_PrepLevelCache(size_t pnumtextures);
+void HWR_LoadTextures(size_t pnumtextures);
 void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color);
 void HWR_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT16 actualcolor, UINT8 strength);
 void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT32 actualcolor);	// Lat: separate flags from color since color needs to be an uint to work right.
@@ -57,6 +58,7 @@ UINT8 *HWR_GetScreenshot(void);
 boolean HWR_Screenshot(const char *pathname);
 
 void HWR_AddCommands(void);
+void HWR_AddSessionCommands(void);
 void HWR_CorrectSWTricks(void);
 void transform(float *cx, float *cy, float *cz);
 FBITFIELD HWR_TranstableToAlpha(INT32 transtablenum, FSurfaceInfo *pSurf);
@@ -85,13 +87,11 @@ extern consvar_t cv_grcoronasize;
 extern consvar_t cv_grfov;
 extern consvar_t cv_grmodels;
 extern consvar_t cv_grmodelinterpolation;
+extern consvar_t cv_grmodellighting;
 extern consvar_t cv_grfog;
 extern consvar_t cv_grfogcolor;
 extern consvar_t cv_grfogdensity;
 extern consvar_t cv_grsoftwarefog;
-extern consvar_t cv_grgammared;
-extern consvar_t cv_grgammagreen;
-extern consvar_t cv_grgammablue;
 extern consvar_t cv_grfiltermode;
 extern consvar_t cv_granisotropicmode;
 extern consvar_t cv_grcorrecttricks;
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 9d9171cea468c2b6b9d676d085a88fd3dde885db..3800b6ad98ff9b89af7ee38a7c328a87abcbf580 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -239,7 +239,7 @@ static GrTextureFormat_t PNG_Load(const char *filename, int *w, int *h, GLPatch_
 
 	{
 		png_uint_32 i, pitch = png_get_rowbytes(png_ptr, png_info_ptr);
-		png_bytep PNG_image = Z_Malloc(pitch*height, PU_HWRCACHE, &grpatch->mipmap->grInfo.data);
+		png_bytep PNG_image = Z_Malloc(pitch*height, PU_HWRMODELTEXTURE, &grpatch->mipmap->grInfo.data);
 		png_bytepp row_pointers = png_malloc(png_ptr, height * sizeof (png_bytep));
 		for (i = 0; i < height; i++)
 			row_pointers[i] = PNG_image + i*pitch;
@@ -313,7 +313,7 @@ static GrTextureFormat_t PCX_Load(const char *filename, int *w, int *h,
 
 	pw = *w = header.xmax - header.xmin + 1;
 	ph = *h = header.ymax - header.ymin + 1;
-	image = Z_Malloc(pw*ph*4, PU_HWRCACHE, &grpatch->mipmap->grInfo.data);
+	image = Z_Malloc(pw*ph*4, PU_HWRMODELTEXTURE, &grpatch->mipmap->grInfo.data);
 
 	if (fread(palette, sizeof (UINT8), PALSIZE, file) != PALSIZE)
 	{
@@ -373,6 +373,9 @@ static void md2_loadTexture(md2_t *model)
 	if (!grpatch->mipmap->downloaded && !grpatch->mipmap->grInfo.data)
 	{
 		int w = 0, h = 0;
+		UINT32 size;
+		RGBA_t *image;
+
 #ifdef HAVE_PNG
 		grpatch->mipmap->grInfo.format = PNG_Load(filename, &w, &h, grpatch);
 		if (grpatch->mipmap->grInfo.format == 0)
@@ -389,6 +392,15 @@ static void md2_loadTexture(md2_t *model)
 		grpatch->mipmap->width = (UINT16)w;
 		grpatch->mipmap->height = (UINT16)h;
 
+		// Lactozilla: Apply colour cube
+		image = grpatch->mipmap->grInfo.data;
+		size = w*h;
+		while (size--)
+		{
+			V_CubeApply(&image->s.red, &image->s.green, &image->s.blue);
+			image++;
+		}
+
 #ifdef GLIDE_API_COMPATIBILITY
 		// not correct!
 		grpatch->mipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_256;
@@ -397,7 +409,6 @@ static void md2_loadTexture(md2_t *model)
 #endif
 	}
 	HWD.pfnSetTexture(grpatch->mipmap);
-	HWR_UnlockCachedPatch(grpatch);
 }
 
 // -----------------+
@@ -453,7 +464,6 @@ static void md2_loadBlendTexture(md2_t *model)
 #endif
 	}
 	HWD.pfnSetTexture(grpatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary
-	HWR_UnlockCachedPatch(grpatch);
 
 	Z_Free(filename);
 }
@@ -637,18 +647,20 @@ spritemd2found:
 // 0.7152 to green
 // 0.0722 to blue
 #define SETBRIGHTNESS(brightness,r,g,b) \
-	brightness = (UINT8)(((1063*((UINT16)r)/5000) + (3576*((UINT16)g)/5000) + (361*((UINT16)b)/5000)) / 3)
+	brightness = (UINT8)(((1063*(UINT16)(r))/5000) + ((3576*(UINT16)(g))/5000) + ((361*(UINT16)(b))/5000))
 
 static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, INT32 skinnum, skincolors_t color)
 {
-	UINT8 i;
 	UINT16 w = gpatch->width, h = gpatch->height;
 	UINT32 size = w*h;
 	RGBA_t *image, *blendimage, *cur, blendcolor;
 
+	// vanilla port
+	UINT8 translation[16];
+	memset(translation, 0, sizeof(translation));
+
 	if (grmip->width == 0)
 	{
-
 		grmip->width = gpatch->width;
 		grmip->height = gpatch->height;
 
@@ -658,46 +670,58 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch,
 		grmip->grInfo.format = GR_RGBA;
 	}
 
-	Z_Free(grmip->grInfo.data);
-	grmip->grInfo.data = NULL;
+	if (grmip->grInfo.data)
+	{
+		Z_Free(grmip->grInfo.data);
+		grmip->grInfo.data = NULL;
+	}
 
-	cur = Z_Malloc(size*4, PU_HWRCACHE, &grmip->grInfo.data);
+	cur = Z_Malloc(size*4, PU_HWRMODELTEXTURE, &grmip->grInfo.data);
 	memset(cur, 0x00, size*4);
 
 	image = gpatch->mipmap->grInfo.data;
 	blendimage = blendgpatch->mipmap->grInfo.data;
+	blendcolor = V_GetColor(0); // initialize
 
-	// Average all of the translation's colors
-	if (color == SKINCOLOR_NONE || color >= MAXTRANSLATIONS)
-		blendcolor = V_GetColor(0xff);
-	else
-	{
-		const UINT8 div = 6;
-		const UINT8 start = 4;
-		UINT32 r, g, b;
+	if (color != SKINCOLOR_NONE)
+		memcpy(&translation, &Color_Index[color - 1], 16);
 
-		blendcolor = V_GetColor(Color_Index[color-1][start]);
-		r = (UINT32)(blendcolor.s.red*blendcolor.s.red);
-		g = (UINT32)(blendcolor.s.green*blendcolor.s.green);
-		b = (UINT32)(blendcolor.s.blue*blendcolor.s.blue);
-
-		for (i = 1; i < div; i++)
+	while (size--)
+	{
+		if (skinnum == TC_BOSS)
 		{
-			RGBA_t nextcolor = V_GetColor(Color_Index[color-1][start+i]);
-			r += (UINT32)(nextcolor.s.red*nextcolor.s.red);
-			g += (UINT32)(nextcolor.s.green*nextcolor.s.green);
-			b += (UINT32)(nextcolor.s.blue*nextcolor.s.blue);
-		}
+			// Turn everything below a certain threshold white
+			if ((image->s.red == image->s.green) && (image->s.green == image->s.blue) && image->s.blue <= 82)
+			{
+				// Lactozilla: Invert the colors
+				cur->s.red = cur->s.green = cur->s.blue = (255 - image->s.blue);
+			}
+			else
+			{
+				cur->s.red = image->s.red;
+				cur->s.green = image->s.green;
+				cur->s.blue = image->s.blue;
+			}
 
-		blendcolor.s.red = (UINT8)(FixedSqrt((r/div)<<FRACBITS)>>FRACBITS);
-		blendcolor.s.green = (UINT8)(FixedSqrt((g/div)<<FRACBITS)>>FRACBITS);
-		blendcolor.s.blue = (UINT8)(FixedSqrt((b/div)<<FRACBITS)>>FRACBITS);
-	}
+			cur->s.alpha = image->s.alpha;
+		}
+		else if (skinnum == TC_METALSONIC)
+		{
+			// Turn everything below a certain blue threshold white
+			if (image->s.red == 0 && image->s.green == 0 && image->s.blue <= 82)
+			{
+				cur->s.red = cur->s.green = cur->s.blue = 255;
+			}
+			else
+			{
+				cur->s.red = image->s.red;
+				cur->s.green = image->s.green;
+				cur->s.blue = image->s.blue;
+			}
 
-	// rainbow support, could theoretically support boss ones too
-	if (skinnum == TC_RAINBOW)
-	{
-		while (size--)
+			cur->s.alpha = image->s.alpha;
+		}
+		else if (skinnum == TC_DASHMODE)
 		{
 			if (image->s.alpha == 0 && blendimage->s.alpha == 0)
 			{
@@ -705,66 +729,216 @@ static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch,
 				cur->rgba = image->rgba;
 			}
 			else
+			{
+				UINT8 ialpha = 255 - blendimage->s.alpha, balpha = blendimage->s.alpha;
+				RGBA_t icolor = *image, bcolor;
+
+				memset(&bcolor, 0x00, sizeof(RGBA_t));
+
+				if (blendimage->s.alpha)
+				{
+					bcolor.s.blue = 0;
+					bcolor.s.red = 255;
+					bcolor.s.green = (blendimage->s.red + blendimage->s.green + blendimage->s.blue) / 3;
+				}
+				if (image->s.alpha && image->s.red > image->s.green << 1) // this is pretty arbitrary, but it works well for Metal Sonic
+				{
+					icolor.s.red = image->s.blue;
+					icolor.s.blue = image->s.red;
+				}
+				cur->s.red = (ialpha * icolor.s.red + balpha * bcolor.s.red)/255;
+				cur->s.green = (ialpha * icolor.s.green + balpha * bcolor.s.green)/255;
+				cur->s.blue = (ialpha * icolor.s.blue + balpha * bcolor.s.blue)/255;
+				cur->s.alpha = image->s.alpha;
+			}
+		}
+		else if (skinnum == TC_ALLWHITE)
+		{
+			// Turn everything white
+			cur->s.red = cur->s.green = cur->s.blue = 255;
+			cur->s.alpha = image->s.alpha;
+		}
+		else
+		{
+			UINT16 brightness;
+
+			// Don't bother with blending the pixel if the alpha of the blend pixel is 0
+			if (skinnum == TC_RAINBOW)
+			{
+				if (image->s.alpha == 0 && blendimage->s.alpha == 0)
+				{
+					cur->rgba = image->rgba;
+					cur++; image++; blendimage++;
+					continue;
+				}
+				else
+				{
+					UINT16 imagebright, blendbright;
+					SETBRIGHTNESS(imagebright,image->s.red,image->s.green,image->s.blue);
+					SETBRIGHTNESS(blendbright,blendimage->s.red,blendimage->s.green,blendimage->s.blue);
+					// slightly dumb average between the blend image color and base image colour, usually one or the other will be fully opaque anyway
+					brightness = (imagebright*(255-blendimage->s.alpha))/255 + (blendbright*blendimage->s.alpha)/255;
+				}
+			}
+			else
+			{
+				if (blendimage->s.alpha == 0)
+				{
+					cur->rgba = image->rgba;
+					cur++; image++; blendimage++;
+					continue;
+				}
+				else
+				{
+					SETBRIGHTNESS(brightness,blendimage->s.red,blendimage->s.green,blendimage->s.blue);
+				}
+			}
+
+			// Calculate a sort of "gradient" for the skincolor
+			// (Me splitting this into a function didn't work, so I had to ruin this entire function's groove...)
+			{
+				RGBA_t nextcolor;
+				UINT8 firsti, secondi, mul;
+				UINT32 r, g, b;
+
+				// Rainbow needs to find the closest match to the textures themselves, instead of matching brightnesses to other colors.
+				// Ensue horrible mess.
+				if (skinnum == TC_RAINBOW)
+				{
+					UINT16 brightdif = 256;
+					UINT8 colorbrightnesses[16];
+					INT32 compare, m, d;
+					UINT8 i;
+
+					// Ignore pure white & pitch black
+					if (brightness > 253 || brightness < 2)
+					{
+						cur->rgba = image->rgba;
+						cur++; image++; blendimage++;
+						continue;
+					}
+
+					firsti = 0;
+					mul = 0;
+
+					for (i = 0; i < 16; i++)
+					{
+						RGBA_t tempc = V_GetColor(translation[i]);
+						SETBRIGHTNESS(colorbrightnesses[i], tempc.s.red, tempc.s.green, tempc.s.blue); // store brightnesses for comparison
+					}
+
+					for (i = 0; i < 16; i++)
+					{
+						if (brightness > colorbrightnesses[i]) // don't allow greater matches (because calculating a makeshift gradient for this is already a huge mess as is)
+							continue;
+						compare = abs((INT16)(colorbrightnesses[i]) - (INT16)(brightness));
+						if (compare < brightdif)
+						{
+							brightdif = (UINT16)compare;
+							firsti = i; // best matching color that's equal brightness or darker
+						}
+					}
+
+					secondi = firsti+1; // next color in line
+					if (secondi == 16)
+					{
+						m = (INT16)brightness; // - 0;
+						d = (INT16)colorbrightnesses[firsti]; // - 0;
+					}
+					else
+					{
+						m = (INT16)brightness - (INT16)colorbrightnesses[secondi];
+						d = (INT16)colorbrightnesses[firsti] - (INT16)colorbrightnesses[secondi];
+					}
+
+					if (m >= d)
+						m = d-1;
+
+					// calculate the "gradient" multiplier based on how close this color is to the one next in line
+					if (m <= 0 || d <= 0)
+						mul = 0;
+					else
+						mul = 15 - ((m * 16) / d);
+				}
+				else
+				{
+					// Thankfully, it's normally way more simple.
+					// Just convert brightness to a skincolor value, use remainder to find the gradient multipler
+					firsti = ((UINT8)(255-brightness) / 16);
+					secondi = firsti+1;
+					mul = ((UINT8)(255-brightness) % 16);
+				}
+
+				blendcolor = V_GetColor(translation[firsti]);
+
+				if (mul > 0 // If it's 0, then we only need the first color.
+					&& translation[firsti] != translation[secondi]) // Some colors have duplicate colors in a row, so let's just save the process
+				{
+					if (secondi == 16) // blend to black
+						nextcolor = V_GetColor(31);
+					else
+						nextcolor = V_GetColor(translation[secondi]);
+
+					// Find difference between points
+					r = (UINT32)(nextcolor.s.red - blendcolor.s.red);
+					g = (UINT32)(nextcolor.s.green - blendcolor.s.green);
+					b = (UINT32)(nextcolor.s.blue - blendcolor.s.blue);
+
+					// Find the gradient of the two points
+					r = ((mul * r) / 16);
+					g = ((mul * g) / 16);
+					b = ((mul * b) / 16);
+
+					// Add gradient value to color
+					blendcolor.s.red += r;
+					blendcolor.s.green += g;
+					blendcolor.s.blue += b;
+				}
+			}
+
+			if (skinnum == TC_RAINBOW)
 			{
 				UINT32 tempcolor;
-				UINT16 imagebright, blendbright, finalbright, colorbright;
-				SETBRIGHTNESS(imagebright,image->s.red,image->s.green,image->s.blue);
-				SETBRIGHTNESS(blendbright,blendimage->s.red,blendimage->s.green,blendimage->s.blue);
-				// slightly dumb average between the blend image color and base image colour, usually one or the other will be fully opaque anyway
-				finalbright = (imagebright*(255-blendimage->s.alpha))/255 + (blendbright*blendimage->s.alpha)/255;
+				UINT16 colorbright;
+
 				SETBRIGHTNESS(colorbright,blendcolor.s.red,blendcolor.s.green,blendcolor.s.blue);
+				if (colorbright == 0)
+					colorbright = 1; // no dividing by 0 please
 
-				tempcolor = (finalbright*blendcolor.s.red)/colorbright;
+				tempcolor = (brightness * blendcolor.s.red) / colorbright;
 				tempcolor = min(255, tempcolor);
 				cur->s.red = (UINT8)tempcolor;
-				tempcolor = (finalbright*blendcolor.s.green)/colorbright;
+
+				tempcolor = (brightness * blendcolor.s.green) / colorbright;
 				tempcolor = min(255, tempcolor);
 				cur->s.green = (UINT8)tempcolor;
-				tempcolor = (finalbright*blendcolor.s.blue)/colorbright;
+
+				tempcolor = (brightness * blendcolor.s.blue) / colorbright;
 				tempcolor = min(255, tempcolor);
 				cur->s.blue = (UINT8)tempcolor;
 				cur->s.alpha = image->s.alpha;
 			}
-
-			cur++; image++; blendimage++;
-		}
-	}
-	else
-	{
-		while (size--)
-		{
-			if (blendimage->s.alpha == 0)
-			{
-				// Don't bother with blending the pixel if the alpha of the blend pixel is 0
-				cur->rgba = image->rgba;
-			}
 			else
 			{
+				// Color strength depends on image alpha
 				INT32 tempcolor;
-				INT16 tempmult, tempalpha;
-				tempalpha = -(abs(blendimage->s.red-127)-127)*2;
-				if (tempalpha > 255)
-					tempalpha = 255;
-				else if (tempalpha < 0)
-					tempalpha = 0;
-
-				tempmult = (blendimage->s.red-127)*2;
-				if (tempmult > 255)
-					tempmult = 255;
-				else if (tempmult < 0)
-					tempmult = 0;
-
-				tempcolor = (image->s.red*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.red)/255)) * blendimage->s.alpha)/255;
+
+				tempcolor = ((image->s.red * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.red * blendimage->s.alpha) / 255);
+				tempcolor = min(255, tempcolor);
 				cur->s.red = (UINT8)tempcolor;
-				tempcolor = (image->s.green*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.green)/255)) * blendimage->s.alpha)/255;
+
+				tempcolor = ((image->s.green * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.green * blendimage->s.alpha) / 255);
+				tempcolor = min(255, tempcolor);
 				cur->s.green = (UINT8)tempcolor;
-				tempcolor = (image->s.blue*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.blue)/255)) * blendimage->s.alpha)/255;
+
+				tempcolor = ((image->s.blue * (255-blendimage->s.alpha)) / 255) + ((blendcolor.s.blue * blendimage->s.alpha) / 255);
+				tempcolor = min(255, tempcolor);
 				cur->s.blue = (UINT8)tempcolor;
 				cur->s.alpha = image->s.alpha;
 			}
-
-			cur++; image++; blendimage++;
 		}
+
+		cur++; image++; blendimage++;
 	}
 
 	return;
@@ -777,24 +951,24 @@ static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, INT
 	// mostly copied from HWR_GetMappedPatch, hence the similarities and comment
 	GLMipmap_t *grmip, *newmip;
 
-	if (colormap == colormaps || colormap == NULL)
+	if ((colormap == colormaps || colormap == NULL) && (skinnum > TC_DEFAULT))
 	{
 		// Don't do any blending
 		HWD.pfnSetTexture(gpatch->mipmap);
 		return;
 	}
 
-	// search for the mimmap
+	// search for the mipmap
 	// skip the first (no colormap translated)
 	for (grmip = gpatch->mipmap; grmip->nextcolormap; )
 	{
 		grmip = grmip->nextcolormap;
-		if (grmip->colormap == colormap)
+		if (grmip->colormap == colormap || (skinnum < TC_DEFAULT && grmip->tcindex == skinnum))
 		{
 			if (grmip->downloaded && grmip->grInfo.data)
 			{
 				HWD.pfnSetTexture(grmip); // found the colormap, set it to the correct texture
-				Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
+				Z_ChangeTag(grmip->grInfo.data, PU_HWRMODELTEXTURE);
 				return;
 			}
 		}
@@ -812,16 +986,27 @@ static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, INT
 		I_Error("%s: Out of memory", "HWR_GetMappedPatch");
 	grmip->nextcolormap = newmip;
 	newmip->colormap = colormap;
+	newmip->tcindex = skinnum;
 
 	HWR_CreateBlendedTexture(gpatch, blendgpatch, newmip, skinnum, color);
 
 	HWD.pfnSetTexture(newmip);
-	Z_ChangeTag(newmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
+	Z_ChangeTag(newmip->grInfo.data, PU_HWRMODELTEXTURE);
 }
 
 #define NORMALFOG 0x00000000
 #define FADEFOG 0x19000000
 
+static boolean HWR_AllowModel(mobj_t *mobj)
+{
+	// Signpost overlay. Not needed.
+	if (mobj->state-states == S_PLAY_SIGN)
+		return false;
+
+	// Otherwise, render the model.
+	return true;
+}
+
 static boolean HWR_CanInterpolateModel(mobj_t *mobj, model_t *model)
 {
 	if (cv_grmodelinterpolation.value == 2) // Always interpolate
@@ -897,7 +1082,7 @@ static UINT8 HWR_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t
 // HWR_DrawModel
 //
 
-void HWR_DrawModel(gr_vissprite_t *spr)
+boolean HWR_DrawModel(gr_vissprite_t *spr)
 {
 	FSurfaceInfo Surf;
 
@@ -910,10 +1095,12 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 	UINT8 color[4];
 
 	if (!cv_grmodels.value)
-		return;
+		return false;
 
 	if (spr->precip)
-		return;
+		return false;
+
+	memset(&p, 0x00, sizeof(FTransform));
 
 	// MD2 colormap fix
 	// colormap test
@@ -961,10 +1148,8 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 		const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !(spr->mobj->frame & FF_VERTICALFLIP));
 		spritedef_t *sprdef;
 		spriteframe_t *sprframe;
-#ifdef ROTSPRITE
 		spriteinfo_t *sprinfo;
 		angle_t ang;
-#endif
 		INT32 mod;
 		float finalscale;
 
@@ -988,20 +1173,16 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 		{
 			md2 = &md2_playermodels[(skin_t*)spr->mobj->skin-skins];
 			md2->skin = (skin_t*)spr->mobj->skin-skins;
-#ifdef ROTSPRITE
 			sprinfo = &((skin_t *)spr->mobj->skin)->sprinfo[spr->mobj->sprite2];
-#endif
 		}
 		else
 		{
 			md2 = &md2_models[spr->mobj->sprite];
-#ifdef ROTSPRITE
 			sprinfo = &spriteinfo[spr->mobj->sprite];
-#endif
 		}
 
 		if (md2->error)
-			return; // we already failed loading this before :(
+			return false; // we already failed loading this before :(
 		if (!md2->model)
 		{
 			//CONS_Debug(DBG_RENDER, "Loading model... (%s)", sprnames[spr->mobj->sprite]);
@@ -1017,9 +1198,14 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 			{
 				//CONS_Debug(DBG_RENDER, " FAILED\n");
 				md2->error = true; // prevent endless fail
-				return;
+				return false;
 			}
 		}
+
+		// Lactozilla: Disallow certain models from rendering
+		if (!HWR_AllowModel(spr->mobj))
+			return false;
+
 		//HWD.pfnSetBlend(blend); // This seems to actually break translucency?
 		finalscale = md2->scale;
 		//Hurdler: arf, I don't like that implementation at all... too much crappy
@@ -1034,11 +1220,10 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 
 		if (gpatch && gpatch->mipmap->grInfo.format) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
 		{
-			if ((skincolors_t)spr->mobj->color != SKINCOLOR_NONE &&
-				md2->blendgrpatch && ((GLPatch_t *)md2->blendgrpatch)->mipmap->grInfo.format
+			if (md2->blendgrpatch && ((GLPatch_t *)md2->blendgrpatch)->mipmap->grInfo.format
 				&& gpatch->width == ((GLPatch_t *)md2->blendgrpatch)->width && gpatch->height == ((GLPatch_t *)md2->blendgrpatch)->height)
 			{
-				INT32 skinnum = TC_DEFAULT;
+				INT32 skinnum = INT32_MAX;
 				if ((spr->mobj->flags & (MF_ENEMY|MF_BOSS)) && (spr->mobj->flags2 & MF2_FRET) && !(spr->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash"
 				{
 					if (spr->mobj->type == MT_CYBRAKDEMON || spr->mobj->colorized)
@@ -1048,7 +1233,7 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 					else
 						skinnum = TC_BOSS;
 				}
-				else if (spr->mobj->color)
+				else if ((skincolors_t)spr->mobj->color != SKINCOLOR_NONE)
 				{
 					if (spr->mobj->colorized)
 						skinnum = TC_RAINBOW;
@@ -1066,7 +1251,15 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 					else
 						skinnum = TC_DEFAULT;
 				}
-				HWR_GetBlendedTexture(gpatch, (GLPatch_t *)md2->blendgrpatch, skinnum, spr->colormap, (skincolors_t)spr->mobj->color);
+
+				// Translation or skin number found
+				if (skinnum != INT32_MAX)
+					HWR_GetBlendedTexture(gpatch, (GLPatch_t *)md2->blendgrpatch, skinnum, spr->colormap, (skincolors_t)spr->mobj->color);
+				else
+				{
+					// Sorry nothing
+					HWD.pfnSetTexture(gpatch->mipmap);
+				}
 			}
 			else
 			{
@@ -1185,9 +1378,8 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 			p.angley = FIXED_TO_FLOAT(anglef);
 		}
 
-#ifdef ROTSPRITE
 		p.rollangle = 0.0f;
-		p.rollflip = 0;
+		p.rollflip = 1;
 		p.rotaxis = 0;
 		if (spr->mobj->rollangle)
 		{
@@ -1210,7 +1402,6 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 			else if ((sprframe->rotate & SRF_LEFT) && (ang >= ANGLE_180)) // See from left
 				p.rollflip = -1;
 		}
-#endif
 
 		p.anglex = 0.0f;
 
@@ -1244,6 +1435,8 @@ void HWR_DrawModel(gr_vissprite_t *spr)
 
 		HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, color);
 	}
+
+	return true;
 }
 
 #endif //HWRENDER
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index a5f5fc1174b1e0bc1e9a69fa8e5b75f0d78b9234..6f5985a44ea480226a49da31da82ae902c9650a0 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -45,8 +45,8 @@ extern md2_t md2_models[NUMSPRITES];
 extern md2_t md2_playermodels[MAXSKINS];
 
 void HWR_InitModels(void);
-void HWR_DrawModel(gr_vissprite_t *spr);
 void HWR_AddPlayerModel(INT32 skin);
 void HWR_AddSpriteModel(size_t spritenum);
+boolean HWR_DrawModel(gr_vissprite_t *spr);
 
 #endif // _HW_MD2_H_
diff --git a/src/hardware/r_opengl/ogl_win.c b/src/hardware/r_opengl/ogl_win.c
index 562afe9989e0908f046ece44abe2fcf22d2fa439..e4a71734be199bc7180e6be48f8c0dcd9aaa6fd8 100644
--- a/src/hardware/r_opengl/ogl_win.c
+++ b/src/hardware/r_opengl/ogl_win.c
@@ -564,20 +564,15 @@ EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl)
 //                  : in OpenGL, we store values for conversion of paletted graphics when
 //                  : they are downloaded to the 3D card.
 // -----------------+
-EXPORT void HWRAPI(SetPalette) (RGBA_t *pal, RGBA_t *gamma)
+EXPORT void HWRAPI(SetPalette) (RGBA_t *pal)
 {
-	INT32 i;
-
-	for (i = 0; i < 256; i++)
+	size_t palsize = (sizeof(RGBA_t) * 256);
+	// on a palette change, you have to reload all of the textures
+	if (memcmp(&myPaletteData, pal, palsize))
 	{
-		myPaletteData[i].s.red   = (UINT8)MIN((pal[i].s.red*gamma->s.red)/127,     255);
-		myPaletteData[i].s.green = (UINT8)MIN((pal[i].s.green*gamma->s.green)/127, 255);
-		myPaletteData[i].s.blue  = (UINT8)MIN((pal[i].s.blue*gamma->s.blue)/127,   255);
-		myPaletteData[i].s.alpha = pal[i].s.alpha;
+		memcpy(&myPaletteData, pal, palsize);
+		Flush();
 	}
-
-	// on a palette change, you have to reload all of the textures
-	Flush();
 }
 
 #endif
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 8c0ca7155faf94264e53e59163e6213f5dddc00a..97b3b98be576753f7b815ce3e49d7c3f825e319e 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*-
 //-----------------------------------------------------------------------------
 //
-// Copyright (C) 1998-2018 by Sonic Team Junior.
+// Copyright (C) 1998-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -85,7 +85,7 @@ static GLboolean MipMap = GL_FALSE;
 static GLint min_filter = GL_LINEAR;
 static GLint mag_filter = GL_LINEAR;
 static GLint anisotropic_filter = 0;
-static FTransform  md2_transform;
+static boolean model_lighting = true;
 
 const GLubyte *gl_extensions = NULL;
 
@@ -221,11 +221,6 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...)
 #define pglDrawElements glDrawElements
 #define pglEnableClientState glEnableClientState
 #define pglDisableClientState glDisableClientState
-#define pglClientActiveTexture glClientActiveTexture
-#define pglGenBuffers glGenBuffers
-#define pglBindBuffer glBindBuffer
-#define pglBufferData glBufferData
-#define pglDeleteBuffers glDeleteBuffers
 
 /* Lighting */
 #define pglShadeModel glShadeModel
@@ -331,15 +326,6 @@ typedef void (APIENTRY * PFNglEnableClientState) (GLenum cap);
 static PFNglEnableClientState pglEnableClientState;
 typedef void (APIENTRY * PFNglDisableClientState) (GLenum cap);
 static PFNglDisableClientState pglDisableClientState;
-typedef void (APIENTRY * PFNglGenBuffers) (GLsizei n, GLuint *buffers);
-static PFNglGenBuffers pglGenBuffers;
-typedef void (APIENTRY * PFNglBindBuffer) (GLenum target, GLuint buffer);
-static PFNglBindBuffer pglBindBuffer;
-typedef void (APIENTRY * PFNglBufferData) (GLenum target, GLsizei size, const GLvoid *data, GLenum usage);
-static PFNglBufferData pglBufferData;
-typedef void (APIENTRY * PFNglDeleteBuffers) (GLsizei n, const GLuint *buffers);
-static PFNglDeleteBuffers pglDeleteBuffers;
-
 
 /* Lighting */
 typedef void (APIENTRY * PFNglShadeModel) (GLenum mode);
@@ -397,6 +383,17 @@ static PFNglMultiTexCoord2fv pglMultiTexCoord2fv;
 typedef void (APIENTRY *PFNglClientActiveTexture) (GLenum);
 static PFNglClientActiveTexture pglClientActiveTexture;
 
+/* 1.5 functions for buffers */
+typedef void (APIENTRY * PFNglGenBuffers) (GLsizei n, GLuint *buffers);
+static PFNglGenBuffers pglGenBuffers;
+typedef void (APIENTRY * PFNglBindBuffer) (GLenum target, GLuint buffer);
+static PFNglBindBuffer pglBindBuffer;
+typedef void (APIENTRY * PFNglBufferData) (GLenum target, GLsizei size, const GLvoid *data, GLenum usage);
+static PFNglBufferData pglBufferData;
+typedef void (APIENTRY * PFNglDeleteBuffers) (GLsizei n, const GLuint *buffers);
+static PFNglDeleteBuffers pglDeleteBuffers;
+
+
 /* 1.2 Parms */
 /* GL_CLAMP_TO_EDGE_EXT */
 #ifndef GL_CLAMP_TO_EDGE
@@ -417,6 +414,14 @@ static PFNglClientActiveTexture pglClientActiveTexture;
 #define GL_TEXTURE1 0x84C1
 #endif
 
+/* 1.5 Parms */
+#ifndef GL_ARRAY_BUFFER
+#define GL_ARRAY_BUFFER 0x8892
+#endif
+#ifndef GL_STATIC_DRAW
+#define GL_STATIC_DRAW 0x88E4
+#endif
+
 boolean SetupGLfunc(void)
 {
 #ifndef STATIC_OPENGL
@@ -504,6 +509,8 @@ boolean SetupGLFunc13(void)
 	pglMultiTexCoord2f = GetGLFunc("glMultiTexCoord2f");
 	pglClientActiveTexture = GetGLFunc("glClientActiveTexture");
 	pglMultiTexCoord2fv = GetGLFunc("glMultiTexCoord2fv");
+
+	/* 1.5 funcs */
 	pglGenBuffers = GetGLFunc("glGenBuffers");
 	pglBindBuffer = GetGLFunc("glBindBuffer");
 	pglBufferData = GetGLFunc("glBufferData");
@@ -716,8 +723,8 @@ void Flush(void)
 
 	while (gr_cachehead)
 	{
-		// ceci n'est pas du tout necessaire vu que tu les a charger normalement et
-		// donc il sont dans ta liste !
+		// this is not necessary at all, because you have loaded them normally,
+		// and so they already are in your list!
 #if 0
 		//Hurdler: 25/04/2000: now support colormap in hardware mode
 		FTextureInfo    *tmp = gr_cachehead->nextskin;
@@ -1289,11 +1296,11 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo)
 
 		pTexInfo->nextmipmap = NULL;
 		if (gr_cachetail)
-		{ // insertion en fin de liste
+		{ // insertion at the tail
 			gr_cachetail->nextmipmap = pTexInfo;
 			gr_cachetail = pTexInfo;
 		}
-		else // initialisation de la liste
+		else // initialization of the linked list
 			gr_cachetail = gr_cachehead =  pTexInfo;
 	}
 }
@@ -1427,7 +1434,7 @@ static const boolean gl_ext_arb_vertex_buffer_object = true;
 
 // The texture offset to be applied to the texture coordinates in SkyVertex().
 static int rows, columns;
-static boolean yflip;
+static signed char yflip;
 static int texw, texh;
 static boolean foglayer;
 static float delta = 0.0f;
@@ -1440,17 +1447,17 @@ static INT32 lasttex = -1;
 
 static void SkyVertex(vbo_vertex_t *vbo, int r, int c)
 {
-	const float radians = (M_PIl / 180.0f);
+	const float radians = (float)(M_PIl / 180.0f);
 	const float scale = 10000.0f;
 	const float maxSideAngle = 60.0f;
 
 	float topAngle = (c / (float)columns * 360.0f);
 	float sideAngle = (maxSideAngle * (rows - r) / rows);
-	float height = sin(sideAngle * radians);
-	float realRadius = scale * cos(sideAngle * radians);
-	float x = realRadius * cos(topAngle * radians);
+	float height = (float)(sin(sideAngle * radians));
+	float realRadius = (float)(scale * cos(sideAngle * radians));
+	float x = (float)(realRadius * cos(topAngle * radians));
 	float y = (!yflip) ? scale * height : -scale * height;
-	float z = realRadius * sin(topAngle * radians);
+	float z = (float)(realRadius * sin(topAngle * radians));
 	float timesRepeat = (4 * (256.0f / texw));
 	if (fpclassify(timesRepeat) == FP_ZERO)
 		timesRepeat = 1.0f;
@@ -1652,16 +1659,9 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value)
 {
 	switch (IdState)
 	{
-
-#if 0
-		case 77:
-		{
-			//08/01/00: Hurdler this is a test for mirror
-			if (!Value)
-				ClearBuffer(false, true, 0); // clear depth buffer
+		case HWD_SET_MODEL_LIGHTING:
+			model_lighting = Value;
 			break;
-		}
-#endif
 
 		case HWD_SET_FOG_COLOR:
 		{
@@ -1674,6 +1674,7 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value)
 			pglFogfv(GL_FOG_COLOR, fogcolor);
 			break;
 		}
+
 		case HWD_SET_FOG_DENSITY:
 			pglFogf(GL_FOG_DENSITY, Value*1200/(500*1000000.0f));
 			break;
@@ -2038,16 +2039,25 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 	}
 #endif
 
-	pglLightfv(GL_LIGHT0, GL_POSITION, LightPos);
+	if (model_lighting)
+	{
+		pglLightfv(GL_LIGHT0, GL_POSITION, LightPos);
+		pglShadeModel(GL_SMOOTH);
+	}
 
-	pglShadeModel(GL_SMOOTH);
 	if (color)
 	{
 #ifdef GL_LIGHT_MODEL_AMBIENT
-		pglEnable(GL_LIGHTING);
-		pglMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
-		pglMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
+		if (model_lighting)
+		{
+			pglEnable(GL_LIGHTING);
+			pglMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
+			pglMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
+		}
+		else
 #endif
+			pglColor4ubv((GLubyte*)color);
+
 		if (color[3] < 255)
 			SetBlend(PF_Translucent|PF_Modulated|PF_Clip);
 		else
@@ -2066,7 +2076,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 	pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f);
 	pglRotatef(pos->anglex, 1.0f, 0.0f, 0.0f);
 
-#ifdef ROTSPRITE
 	if (pos->roll)
 	{
 		float roll = (1.0f * pos->rollflip);
@@ -2079,7 +2088,6 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32
 			pglRotatef(pos->rollangle, roll, 0.0f, 0.0f);
 		pglTranslatef(-pos->centerx, -pos->centery, 0);
 	}
-#endif
 
 	pglScalef(scalex, scaley, scalez);
 
@@ -2218,8 +2226,6 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
 	if (stransform)
 	{
 		boolean fovx90;
-		// keep a trace of the transformation for md2
-		memcpy(&md2_transform, stransform, sizeof (md2_transform));
 
 #ifdef USE_FTRANSFORM_MIRROR
 		// mirroring from Kart
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index afca4c77308aec4567caf8561c2407802da84999..f558b8c0c99fc0019695124ce55079b8761a655d 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1173,6 +1173,8 @@ void HU_clearChatChars(void)
 		w_chat[i] = 0; // reset this.
 	chat_on = false;
 	c_input = 0;
+
+	I_UpdateMouseGrab();
 }
 
 #ifndef NONET
@@ -1323,6 +1325,7 @@ boolean HU_Responder(event_t *ev)
 			chat_on = false;
 			c_input = 0; // reset input cursor
 			chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
+			I_UpdateMouseGrab();
 		}
 		else if (c == KEY_ESCAPE
 			|| ((c == gamecontrol[gc_talkkey][0] || c == gamecontrol[gc_talkkey][1]
@@ -1331,6 +1334,7 @@ boolean HU_Responder(event_t *ev)
 		{
 			chat_on = false;
 			c_input = 0; // reset input cursor
+			I_UpdateMouseGrab();
 		}
 		else if ((c == KEY_UPARROW || c == KEY_MOUSEWHEELUP) && chat_scroll > 0 && !OLDCHAT) // CHAT SCROLLING YAYS!
 		{
@@ -2066,7 +2070,7 @@ static void HU_drawGametype(void)
 {
 	const char *strvalue = NULL;
 
-	if (gametype < 0 || gametype >= NUMGAMETYPES)
+	if (gametype < 0 || gametype >= gametypecount)
 		return; // not a valid gametype???
 
 	strvalue = Gametype_Names[gametype];
@@ -2117,6 +2121,9 @@ static void HU_DrawDemoInfo(void)
 //
 void HU_Drawer(void)
 {
+	if (needpatchrecache)
+		R_ReloadHUDGraphics();
+
 #ifndef NONET
 	// draw chat string plus cursor
 	if (chat_on)
@@ -2371,7 +2378,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 
 	for (i = 0; i < scorelines; i++)
 	{
-		if (players[tab[i].num].spectator && gametype != GT_COOP)
+		if (players[tab[i].num].spectator && gametyperankings[gametype] != GT_COOP)
 			continue; //ignore them.
 
 		greycheck = greycheckdef;
@@ -2434,7 +2441,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 			}
 		}
 
-		if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
+		if (G_GametypeUsesLives() && !(G_GametypeUsesCoopLives() && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
 			V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives));
 		else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
 		{
@@ -2447,7 +2454,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 		if (players[tab[i].num].exiting || (players[tab[i].num].pflags & PF_FINISHED))
 			V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon);
 
-		if (gametype == GT_RACE)
+		if (gametyperankings[gametype] == GT_RACE)
 		{
 			if (circuitmap)
 			{
@@ -2541,7 +2548,7 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 		             | (greycheck ? 0 : V_TRANSLUCENT)
 		             | V_ALLOWLOWERCASE, name);
 
-		if (gametype == GT_CTF)
+		if (gametyperules & GTR_TEAMFLAGS)
 		{
 			if (players[tab[i].num].gotflag & GF_REDFLAG) // Red
 				V_DrawFixedPatch((x-10)*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, 0, rflagico, 0);
@@ -2669,7 +2676,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 		             | (greycheck ? V_TRANSLUCENT : 0)
 		             | V_ALLOWLOWERCASE, name);
 
-		if (gametype == GT_CTF)
+		if (gametyperules & GTR_TEAMFLAGS)
 		{
 			if (players[tab[i].num].gotflag & GF_REDFLAG) // Red
 				V_DrawSmallScaledPatch(x-28, y-4, 0, rflagico);
@@ -2726,7 +2733,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 
 	for (i = 0; i < scorelines; i++)
 	{
-		if (players[tab[i].num].spectator && gametype != GT_COOP)
+		if (players[tab[i].num].spectator && gametyperankings[gametype] != GT_COOP)
 			continue; //ignore them.
 
 		greycheck = greycheckdef;
@@ -2743,7 +2750,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 		             | (greycheck ? V_TRANSLUCENT : 0)
 		             | V_ALLOWLOWERCASE, name);
 
-		if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
+		if (G_GametypeUsesLives() && !(G_GametypeUsesCoopLives() && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
 			V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives));
 		else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
 			V_DrawSmallScaledPatch(x-28, y-4, 0, tagico);
@@ -2792,7 +2799,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 		}
 
 		// All data drawn with thin string for space.
-		if (gametype == GT_RACE)
+		if (gametyperankings[gametype] == GT_RACE)
 		{
 			if (circuitmap)
 			{
@@ -2832,7 +2839,7 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor
 
 	for (i = 0; i < scorelines; i++)
 	{
-		if (players[tab[i].num].spectator && gametype != GT_COOP)
+		if (players[tab[i].num].spectator && gametyperankings[gametype] != GT_COOP)
 			continue; //ignore them.
 
 		greycheck = greycheckdef;
@@ -2902,7 +2909,7 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor
 		}
 
 		// All data drawn with thin string for space.
-		if (gametype == GT_RACE)
+		if (gametyperankings[gametype] == GT_RACE)
 		{
 			if (circuitmap)
 			{
@@ -3026,21 +3033,21 @@ static void HU_DrawRankings(void)
 	// draw the current gametype in the lower right
 	HU_drawGametype();
 
-	if (gametype != GT_RACE && gametype != GT_COMPETITION && gametype != GT_COOP)
+	if (gametyperules & (GTR_TIMELIMIT|GTR_POINTLIMIT))
 	{
-		if (cv_timelimit.value && timelimitintics > 0)
+		if ((gametyperules & GTR_TIMELIMIT) && cv_timelimit.value && timelimitintics > 0)
 		{
 			V_DrawCenteredString(64, 8, 0, "TIME");
 			V_DrawCenteredString(64, 16, 0, va("%i:%02i", G_TicsToMinutes(stplyr->realtime, true), G_TicsToSeconds(stplyr->realtime)));
 		}
 
-		if (cv_pointlimit.value > 0)
+		if ((gametyperules & GTR_POINTLIMIT) && cv_pointlimit.value > 0)
 		{
 			V_DrawCenteredString(256, 8, 0, "POINT LIMIT");
 			V_DrawCenteredString(256, 16, 0, va("%d", cv_pointlimit.value));
 		}
 	}
-	else if (gametype == GT_COOP)
+	else if (gametyperankings[gametype] == GT_COOP)
 	{
 		INT32 totalscore = 0;
 		for (i = 0; i < MAXPLAYERS; i++)
@@ -3074,7 +3081,7 @@ static void HU_DrawRankings(void)
 		tab[i].num = -1;
 		tab[i].name = 0;
 
-		if (gametype == GT_RACE && !circuitmap)
+		if (gametyperankings[gametype] == GT_RACE && !circuitmap)
 			tab[i].count = INT32_MAX;
 	}
 
@@ -3083,7 +3090,7 @@ static void HU_DrawRankings(void)
 		if (!playeringame[j])
 			continue;
 
-		if (gametype != GT_COOP && players[j].spectator)
+		if (!G_PlatformGametype() && players[j].spectator)
 			continue;
 
 		for (i = 0; i < MAXPLAYERS; i++)
@@ -3091,10 +3098,10 @@ static void HU_DrawRankings(void)
 			if (!playeringame[i])
 				continue;
 
-			if (gametype != GT_COOP && players[i].spectator)
+			if (!G_PlatformGametype() && players[i].spectator)
 				continue;
 
-			if (gametype == GT_RACE)
+			if (gametyperankings[gametype] == GT_RACE)
 			{
 				if (circuitmap)
 				{
@@ -3117,7 +3124,7 @@ static void HU_DrawRankings(void)
 					}
 				}
 			}
-			else if (gametype == GT_COMPETITION)
+			else if (gametyperankings[gametype] == GT_COMPETITION)
 			{
 				// todo put something more fitting for the gametype here, such as current
 				// number of categories led
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index 47419d29ae187a2dd98f6a162cec7095c6815986..3502a1b2ddfe901a37732a70a0490c3dee5a9541 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_addrinfo.c b/src/i_addrinfo.c
index 94b9214610777079de3c753de193602872c38105..dd3b5ab0f99e85ff6aeaa57095aa66a785f3d0f8 100644
--- a/src/i_addrinfo.c
+++ b/src/i_addrinfo.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2018 by Sonic Team Junior.
+// Copyright (C) 2011-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_addrinfo.h b/src/i_addrinfo.h
index 7c3afc614f0c181d295ff0d26b96ed6be773a0c4..3108c422e17b06e6be5cef69f98344dffdebfbf1 100644
--- a/src/i_addrinfo.h
+++ b/src/i_addrinfo.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2011-2018 by Sonic Team Junior.
+// Copyright (C) 2011-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_joy.h b/src/i_joy.h
index 27013f011671382e34623809e70031600231ed36..5f9b6371234508251eab4b955e8e9e21c1b752c5 100644
--- a/src/i_joy.h
+++ b/src/i_joy.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_net.h b/src/i_net.h
index 0e17077bbcea85fcd80e9c7fc830d981745ba218..e96891d9d9650543869ba41813ae5f9c00bae45c 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_sound.h b/src/i_sound.h
index 4b299985762a5f552a662e3202194e10d7f87adf..fbaa930532a670434d24bb347f6814f6989b424a 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_system.h b/src/i_system.h
index a8d707d162d53d590df28bd89887dba49ec76f98..a60b56310dcc4cfe651dcd53677c732a5cb7ceca 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -184,10 +184,6 @@ void I_StartupMouse(void);
 */
 void I_StartupMouse2(void);
 
-/**	\brief keyboard startup, shutdown, handler
-*/
-void I_StartupKeyboard(void);
-
 /**	\brief  setup timer irq and user timer routine.
 */
 void I_StartupTimer(void);
@@ -292,6 +288,10 @@ void I_GetJoystick2Events(void);
 */
 void I_GetMouseEvents(void);
 
+/**	\brief Checks if the mouse needs to be grabbed
+*/
+void I_UpdateMouseGrab(void);
+
 char *I_GetEnv(const char *name);
 
 INT32 I_PutEnv(char *variable);
diff --git a/src/i_tcp.c b/src/i_tcp.c
index da92f27673fa4edc872b2056bfdbc64bea9d07cf..502eb673243d49ff1cd6bc029b19dc27777493d7 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -209,7 +209,8 @@ static size_t numbans = 0;
 static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
 static boolean init_tcp_driver = false;
 
-static char port_name[8] = DEFAULTPORT;
+static const char *serverport_name = DEFAULTPORT;
+static const char *clientport_name;/* any port */
 
 #ifndef NONET
 
@@ -887,6 +888,7 @@ static boolean UDP_Socket(void)
 #ifdef HAVE_IPV6
 	const INT32 b_ipv6 = M_CheckParm("-ipv6");
 #endif
+	const char *serv;
 
 
 	for (s = 0; s < mysocketses; s++)
@@ -902,11 +904,16 @@ static boolean UDP_Socket(void)
 	hints.ai_socktype = SOCK_DGRAM;
 	hints.ai_protocol = IPPROTO_UDP;
 
+	if (serverrunning)
+		serv = serverport_name;
+	else
+		serv = clientport_name;
+
 	if (M_CheckParm("-bindaddr"))
 	{
 		while (M_IsNextParm())
 		{
-			gaie = I_getaddrinfo(M_GetNextParm(), port_name, &hints, &ai);
+			gaie = I_getaddrinfo(M_GetNextParm(), serv, &hints, &ai);
 			if (gaie == 0)
 			{
 				runp = ai;
@@ -927,7 +934,7 @@ static boolean UDP_Socket(void)
 	}
 	else
 	{
-		gaie = I_getaddrinfo("0.0.0.0", port_name, &hints, &ai);
+		gaie = I_getaddrinfo("0.0.0.0", serv, &hints, &ai);
 		if (gaie == 0)
 		{
 			runp = ai;
@@ -942,8 +949,8 @@ static boolean UDP_Socket(void)
 #ifdef HAVE_MINIUPNPC
 					if (UPNP_support)
 					{
-						I_UPnP_rem(port_name, "UDP");
-						I_UPnP_add(NULL, port_name, "UDP");
+						I_UPnP_rem(serverport_name, "UDP");
+						I_UPnP_add(NULL, serverport_name, "UDP");
 					}
 #endif
 				}
@@ -960,7 +967,7 @@ static boolean UDP_Socket(void)
 		{
 			while (M_IsNextParm())
 			{
-				gaie = I_getaddrinfo(M_GetNextParm(), port_name, &hints, &ai);
+				gaie = I_getaddrinfo(M_GetNextParm(), serv, &hints, &ai);
 				if (gaie == 0)
 				{
 					runp = ai;
@@ -981,7 +988,7 @@ static boolean UDP_Socket(void)
 		}
 		else
 		{
-			gaie = I_getaddrinfo("::", port_name, &hints, &ai);
+			gaie = I_getaddrinfo("::", serv, &hints, &ai);
 			if (gaie == 0)
 			{
 				runp = ai;
@@ -1421,15 +1428,19 @@ boolean I_InitTcpNetwork(void)
 	if (!I_InitTcpDriver())
 		return false;
 
-	if (M_CheckParm("-port"))
+	if (M_CheckParm("-port") || M_CheckParm("-serverport"))
 	// Combined -udpport and -clientport into -port
 	// As it was really redundant having two seperate parms that does the same thing
+	/* Sorry Steel, I'm adding these back. But -udpport is a stupid name. */
 	{
-		if (M_IsNextParm())
-			strcpy(port_name, M_GetNextParm());
-		else
-			strcpy(port_name, "0");
+		/*
+		If it's NULL, that's okay! Because then
+		we'll get a random port from getaddrinfo.
+		*/
+		serverport_name = M_GetNextParm();
 	}
+	if (M_CheckParm("-clientport"))
+		clientport_name = M_GetNextParm();
 
 	// parse network game options,
 	if (M_CheckParm("-server") || dedicated)
diff --git a/src/i_tcp.h b/src/i_tcp.h
index 06680cd9bc5fcdbd889a9249127f5633bbb92467..ea8b0a94fa40d517e2554a47d21e6a02c53e4ce8 100644
--- a/src/i_tcp.h
+++ b/src/i_tcp.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/i_video.h b/src/i_video.h
index 4bb2c58293e3cc96309e6cf577540fcd6f63abd9..76b984d252d9463d43f3f66178636af1c3f885b3 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -44,6 +44,7 @@ extern boolean highcolor;
 /**	\brief setup video mode
 */
 void I_StartupGraphics(void);
+void I_StartupHardwareGraphics(void);
 
 /**	\brief restore old video mode
 */
@@ -84,6 +85,7 @@ INT32 VID_GetModeForSize(INT32 w, INT32 h);
 	\return	currect video mode
 */
 INT32 VID_SetMode(INT32 modenum);
+void VID_CheckRenderer(void);
 
 /**	\brief	The VID_GetModeName function
 
diff --git a/src/info.c b/src/info.c
index e14abd575ee5dee315d4cde2c06e0904ac7f89bc..30f3e96d097f79017ef30894ea6b6c30e80f55a1 100644
--- a/src/info.c
+++ b/src/info.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -635,7 +635,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = {
 	0, // SPR2_TRNS,
 
 	FF_SPR2SUPER|SPR2_STND, // SPR2_NSTD,
-	FF_SPR2SUPER|SPR2_FLT , // SPR2_NFLT,
+	FF_SPR2SUPER|SPR2_FALL, // SPR2_NFLT,
 	0, // SPR2_NFLY, (will never be referenced unless skin 0 lacks this)
 	SPR2_NFLY, // SPR2_NDRL,
 	FF_SPR2SUPER|SPR2_STUN, // SPR2_NSTN,
@@ -1074,15 +1074,11 @@ state_t states[NUMSTATES] =
 	{SPR_MNUD, 2|FF_ANIMATE, 5,  {NULL},           1, 2, S_MINUS_BURST4},   // S_MINUS_BURST3
 	{SPR_MNUD, 3|FF_ANIMATE, 5,  {NULL},           1, 2, S_MINUS_BURST5},   // S_MINUS_BURST4
 	{SPR_MNUD, 4|FF_ANIMATE, 5,  {NULL},           1, 2, S_MINUSDIRT2},     // S_MINUS_BURST5
-	{SPR_MNUS, 3, 1, {A_MinusPopup}, 0, 0, S_MINUS_UPWARD1}, // S_MINUS_POPUP
-	{SPR_MNUS, 0, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD2},   // S_MINUS_UPWARD1
-	{SPR_MNUS, 1, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD3},   // S_MINUS_UPWARD2
-	{SPR_MNUS, 2, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD4},   // S_MINUS_UPWARD3
-	{SPR_MNUS, 3, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD1},   // S_MINUS_UPWARD4
-	{SPR_MNUS, 4, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD2}, // S_MINUS_DOWNWARD1
-	{SPR_MNUS, 5, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD3}, // S_MINUS_DOWNWARD2
-	{SPR_MNUS, 6, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD4}, // S_MINUS_DOWNWARD3
-	{SPR_MNUS, 7, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD1}, // S_MINUS_DOWNWARD4
+	{SPR_MNUS, 3, 1, {A_MinusPopup}, 0, 0, S_MINUS_AERIAL1}, // S_MINUS_POPUP
+	{SPR_MNUS, 0, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL2},   // S_MINUS_AERIAL1
+	{SPR_MNUS, 1, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL3},   // S_MINUS_AERIAL2
+	{SPR_MNUS, 2, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL4},   // S_MINUS_AERIAL3
+	{SPR_MNUS, 3, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL1},   // S_MINUS_AERIAL4
 
 	{SPR_MNUD, FF_ANIMATE, 6, {NULL}, 1, 5, S_MINUSDIRT2}, // S_MINUSDIRT1
 	{SPR_MNUD, 5,          8, {NULL}, 3, 5, S_MINUSDIRT3}, // S_MINUSDIRT2
@@ -1623,13 +1619,13 @@ state_t states[NUMSTATES] =
 	{SPR_BRAK, 18, 0, {A_CheckHealth}, 3, S_CYBRAKDEMON_PAIN3, S_CYBRAKDEMON_CHOOSE_ATTACK1}, // S_CYBRAKDEMON_PAIN2
 	{SPR_BRAK, 18, 0, {A_LinedefExecute}, LE_PINCHPHASE, 0, S_CYBRAKDEMON_CHOOSE_ATTACK1}, // S_CYBRAKDEMON_PAIN3
 	{SPR_BRAK, 18, 1, {A_Repeat}, 1, S_CYBRAKDEMON_DIE1, S_CYBRAKDEMON_DIE2}, // S_CYBRAKDEMON_DIE1
-	{SPR_BRAK, 18, 2, {A_BossScream}, 0, 0, S_CYBRAKDEMON_DIE3}, // S_CYBRAKDEMON_DIE2
+	{SPR_BRAK, 18, 2, {A_BossScream}, 2, 0, S_CYBRAKDEMON_DIE3}, // S_CYBRAKDEMON_DIE2
 	{SPR_BRAK, 18, 0, {A_Repeat}, 52, S_CYBRAKDEMON_DIE2, S_CYBRAKDEMON_DIE4}, // S_CYBRAKDEMON_DIE3
-	{SPR_BRAK, 13, 14, {A_PlaySound}, sfx_bedie2, 0, S_CYBRAKDEMON_DIE5}, // S_CYBRAKDEMON_DIE4
-	{SPR_BRAK, 14, 7, {NULL}, 0, 0, S_CYBRAKDEMON_DIE6}, // S_CYBRAKDEMON_DIE5
-	{SPR_BRAK, 15, 5, {NULL}, 0, 0, S_CYBRAKDEMON_DIE7}, // S_CYBRAKDEMON_DIE6
-	{SPR_BRAK, 16, 3, {A_PlaySound}, sfx_bgxpld, 0, S_CYBRAKDEMON_DIE8}, // S_CYBRAKDEMON_DIE7
-	{SPR_BRAK, 17, -1, {A_BossDeath}, 0, 0, S_NULL}, // S_CYBRAKDEMON_DIE8
+	{SPR_BRAK, 13, 34, {A_BossDeath}, 0, 0, S_CYBRAKDEMON_DIE5}, // S_CYBRAKDEMON_DIE4
+	{SPR_BRAK, 14, 34, {NULL}, 0, 0, S_CYBRAKDEMON_DIE6}, // S_CYBRAKDEMON_DIE5
+	{SPR_BRAK, 15, 34, {NULL}, 0, 0, S_CYBRAKDEMON_DIE7}, // S_CYBRAKDEMON_DIE6
+	{SPR_BRAK, 16, 34, {NULL}, 0, 0, S_CYBRAKDEMON_DIE8}, // S_CYBRAKDEMON_DIE7
+	{SPR_BRAK, 17, 34, {NULL}, sfx_befall, 0, S_CYBRAKDEMON_DIE8}, // S_CYBRAKDEMON_DIE8
 	{SPR_BRAK, 0, 0, {A_SetObjectFlags}, MF_SPECIAL|MF_SHOOTABLE, 2, S_CYBRAKDEMON_IDLE}, // S_CYBRAKDEMON_DEINVINCIBLERIZE
 	{SPR_BRAK, 0, 0, {A_SetObjectFlags}, MF_SPECIAL|MF_SHOOTABLE, 1, S_CYBRAKDEMON_IDLE}, // S_CYBRAKDEMON_INVINCIBLERIZE
 
@@ -3884,11 +3880,11 @@ state_t states[NUMSTATES] =
 	{SPR_ROID, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLED
 	{SPR_ROIE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEE
 	{SPR_ROIF, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEF
-	{SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEG
-	{SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEH
+	{SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEG
+	{SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEH
 	{SPR_ROII, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEI
-	{SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEJ
-	{SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEK
+	{SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEJ
+	{SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEK
 	{SPR_ROIL, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEL
 	{SPR_ROIM, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEM
 	{SPR_ROIN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEN
@@ -4289,7 +4285,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_DETON1,       // spawnstate
 		1,              // spawnhealth
 		S_DETON2,       // seestate
-		sfx_None,       // seesound
+		sfx_s3k86,      // seesound -- sfx_kc57 for a self-propelled deton...
 		1,              // reactiontime
 		sfx_deton,      // attacksound
 		S_NULL,         // painstate
@@ -4496,7 +4492,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
+		MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -4696,7 +4692,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_ROBOHOOD_STAND, // seestate
 		sfx_None,         // seesound
 		TICRATE,          // reactiontime
-		sfx_None,         // attacksound
+		sfx_ngjump,       // attacksound
 		S_NULL,           // painstate
 		0,                // painchance
 		sfx_None,         // painsound
@@ -6277,7 +6273,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,                 // mass
 		0,                 // damage
 		sfx_None,          // activesound
-		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOTHINK, // flags
+		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_SCENERY, // flags
 		S_NULL             // raisestate
 	},
 
@@ -11996,7 +11992,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -12023,7 +12019,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -13373,7 +13369,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		100,            // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_SPECIAL|MF_PAIN|MF_NOGRAVITY, // flags
+		MF_SPECIAL|MF_PAIN|MF_NOGRAVITY|MF_FIRE, // flags
 		S_NULL          // raisestate
 	},
 
@@ -17671,7 +17667,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // damage
 		sfx_None,       // activesound
 		MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_NULL          // raisestate
+		S_EXTRALARGEBUBBLE // raisestate
 	},
 
 	{           // MT_WATERZAP
diff --git a/src/info.h b/src/info.h
index ff346412e33d563665ea090f613bdda5d0d7de8f..261abbac5f9da5812becad7f7a3ddcea35670635 100644
--- a/src/info.h
+++ b/src/info.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -155,11 +155,9 @@ void A_SpawnObjectAbsolute();
 void A_SpawnObjectRelative();
 void A_ChangeAngleRelative();
 void A_ChangeAngleAbsolute();
-#ifdef ROTSPRITE
 void A_RollAngle();
 void A_ChangeRollAngleRelative();
 void A_ChangeRollAngleAbsolute();
-#endif
 void A_PlaySound();
 void A_FindTarget();
 void A_FindTracer();
@@ -1270,14 +1268,10 @@ typedef enum state
 	S_MINUS_BURST4,
 	S_MINUS_BURST5,
 	S_MINUS_POPUP,
-	S_MINUS_UPWARD1,
-	S_MINUS_UPWARD2,
-	S_MINUS_UPWARD3,
-	S_MINUS_UPWARD4,
-	S_MINUS_DOWNWARD1,
-	S_MINUS_DOWNWARD2,
-	S_MINUS_DOWNWARD3,
-	S_MINUS_DOWNWARD4,
+	S_MINUS_AERIAL1,
+	S_MINUS_AERIAL2,
+	S_MINUS_AERIAL3,
+	S_MINUS_AERIAL4,
 
 	// Minus dirt
 	S_MINUSDIRT1,
diff --git a/src/keys.h b/src/keys.h
index cf3a0d060c539483e61026e175e1365da3f5eb3e..8819aaa86054c6cdd98329782636549d09f25bde 100644
--- a/src/keys.h
+++ b/src/keys.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 03f1aff188ccd1db9a086fc65ebb853cb59781c1..2a82ec5129c11410d008090ae41f29e0a5e69a87 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -12,6 +12,7 @@
 
 #include "doomdef.h"
 #ifdef HAVE_BLUA
+#include "fastcmp.h"
 #include "p_local.h"
 #include "p_setup.h" // So we can have P_SetupLevelSky
 #ifdef ESLOPE
@@ -23,6 +24,8 @@
 #include "m_random.h"
 #include "s_sound.h"
 #include "g_game.h"
+#include "m_menu.h"
+#include "y_inter.h"
 #include "hu_stuff.h"	// HU_AddChatText
 #include "console.h"
 #include "d_netcmd.h" // IsPlayerAdmin
@@ -144,10 +147,8 @@ static const struct {
 	{META_MOBJINFO,     "mobjinfo_t"},
 	{META_SFXINFO,      "sfxinfo_t"},
 	{META_SPRITEINFO,   "spriteinfo_t"},
-#ifdef ROTSPRITE
 	{META_PIVOTLIST,    "spriteframepivot_t[]"},
 	{META_FRAMEPIVOT,   "spriteframepivot_t"},
-#endif
 
 	{META_MOBJ,         "mobj_t"},
 	{META_MAPTHING,     "mapthing_t"},
@@ -2632,6 +2633,145 @@ static int lib_sStartMusicCaption(lua_State *L)
 // G_GAME
 ////////////
 
+// Copypasted from lib_cvRegisterVar :]
+static int lib_gAddGametype(lua_State *L)
+{
+	const char *k;
+	lua_Integer i;
+
+	const char *gtname = NULL;
+	const char *gtconst = NULL;
+	const char *gtdescription = NULL;
+	INT16 newgtidx = 0;
+	UINT32 newgtrules = 0;
+	UINT32 newgttol = 0;
+	INT32 newgtpointlimit = 0;
+	INT32 newgttimelimit = 0;
+	UINT8 newgtleftcolor = 0;
+	UINT8 newgtrightcolor = 0;
+	INT16 newgtrankingstype = -1;
+	int newgtinttype = 0;
+
+	luaL_checktype(L, 1, LUA_TTABLE);
+	lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one.
+
+	if (!lua_lumploading)
+		return luaL_error(L, "This function cannot be called from within a hook or coroutine!");
+
+	// Ran out of gametype slots
+	if (gametypecount == NUMGAMETYPEFREESLOTS)
+		return luaL_error(L, "Ran out of free gametype slots!");
+
+#define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to " LUA_QL("G_AddGametype") " (%s)", e);
+#define TYPEERROR(f, t) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t), luaL_typename(L, -1)))
+
+	lua_pushnil(L);
+	while (lua_next(L, 1)) {
+		// stack: gametype table, key/index, value
+		//               1            2        3
+		i = 0;
+		k = NULL;
+		if (lua_isnumber(L, 2))
+			i = lua_tointeger(L, 2);
+		else if (lua_isstring(L, 2))
+			k = lua_tostring(L, 2);
+
+		// Sorry, no gametype rules as key names.
+		if (i == 1 || (k && fasticmp(k, "name"))) {
+			if (!lua_isstring(L, 3))
+				TYPEERROR("name", LUA_TSTRING)
+			gtname = Z_StrDup(lua_tostring(L, 3));
+		} else if (i == 2 || (k && fasticmp(k, "identifier"))) {
+			if (!lua_isstring(L, 3))
+				TYPEERROR("identifier", LUA_TSTRING)
+			gtconst = Z_StrDup(lua_tostring(L, 3));
+		} else if (i == 3 || (k && fasticmp(k, "rules"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("rules", LUA_TNUMBER)
+			newgtrules = (UINT32)lua_tointeger(L, 3);
+		} else if (i == 4 || (k && fasticmp(k, "typeoflevel"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("typeoflevel", LUA_TNUMBER)
+			newgttol = (UINT32)lua_tointeger(L, 3);
+		} else if (i == 5 || (k && fasticmp(k, "rankingtype"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("rankingtype", LUA_TNUMBER)
+			newgtrankingstype = (INT16)lua_tointeger(L, 3);
+		} else if (i == 6 || (k && fasticmp(k, "intermissiontype"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("intermissiontype", LUA_TNUMBER)
+			newgtinttype = (int)lua_tointeger(L, 3);
+		} else if (i == 7 || (k && fasticmp(k, "defaultpointlimit"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("defaultpointlimit", LUA_TNUMBER)
+			newgtpointlimit = (INT32)lua_tointeger(L, 3);
+		} else if (i == 8 || (k && fasticmp(k, "defaulttimelimit"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("defaulttimelimit", LUA_TNUMBER)
+			newgttimelimit = (INT32)lua_tointeger(L, 3);
+		} else if (i == 9 || (k && fasticmp(k, "description"))) {
+			if (!lua_isstring(L, 3))
+				TYPEERROR("description", LUA_TSTRING)
+			gtdescription = Z_StrDup(lua_tostring(L, 3));
+		} else if (i == 10 || (k && fasticmp(k, "headerleftcolor"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("headerleftcolor", LUA_TNUMBER)
+			newgtleftcolor = (UINT8)lua_tointeger(L, 3);
+		} else if (i == 11 || (k && fasticmp(k, "headerrightcolor"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("headerrightcolor", LUA_TNUMBER)
+			newgtrightcolor = (UINT8)lua_tointeger(L, 3);
+		// Key name specified
+		} else if ((!i) && (k && fasticmp(k, "headercolor"))) {
+			if (!lua_isnumber(L, 3))
+				TYPEERROR("headercolor", LUA_TNUMBER)
+			newgtleftcolor = newgtrightcolor = (UINT8)lua_tointeger(L, 3);
+		}
+		lua_pop(L, 1);
+	}
+
+#undef FIELDERROR
+#undef TYPEERROR
+
+	// pop gametype table
+	lua_pop(L, 1);
+
+	// Set defaults
+	if (gtname == NULL)
+		gtname = Z_StrDup("Unnamed gametype");
+	if (gtdescription == NULL)
+		gtdescription = Z_StrDup("???");
+
+	// Add the new gametype
+	newgtidx = G_AddGametype(newgtrules);
+	G_AddGametypeTOL(newgtidx, newgttol);
+	G_SetGametypeDescription(newgtidx, NULL, newgtleftcolor, newgtrightcolor);
+	strncpy(gametypedesc[newgtidx].notes, gtdescription, 441);
+
+	// Not covered by G_AddGametype alone.
+	if (newgtrankingstype == -1)
+		newgtrankingstype = newgtidx;
+	gametyperankings[newgtidx] = newgtrankingstype;
+	intermissiontypes[newgtidx] = newgtinttype;
+	pointlimits[newgtidx] = newgtpointlimit;
+	timelimits[newgtidx] = newgttimelimit;
+
+	// Write the new gametype name.
+	Gametype_Names[newgtidx] = gtname;
+
+	// Write the constant name.
+	if (gtconst == NULL)
+		gtconst = gtname;
+	G_AddGametypeConstant(newgtidx, gtconst);
+
+	// Update gametype_cons_t accordingly.
+	G_UpdateGametypeSelections();
+
+	// done
+	CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]);
+	return 0;
+}
+
 static int lib_gBuildMapName(lua_State *L)
 {
 	INT32 map = luaL_optinteger(L, 1, gamemap);
@@ -2664,23 +2804,17 @@ static int lib_gSetCustomExitVars(lua_State *L)
 	// Supported:
 	//	G_SetCustomExitVars();			[reset to defaults]
 	//	G_SetCustomExitVars(int)		[nextmap override only]
-	//	G_SetCustomExitVars(bool)		[skipstats only]
-	//	G_SetCustomExitVars(int, bool)	[both of the above]
+	//	G_SetCustomExitVars(nil, int)	[skipstats only]
+	//	G_SetCustomExitVars(int, int)	[both of the above]
+
+	nextmapoverride = 0;
+	skipstats = 0;
+
 	if (n >= 1)
 	{
-		if (lua_isnumber(L, 1) || n >= 2)
-		{
-			nextmapoverride = (INT16)luaL_checknumber(L, 1);
-			lua_remove(L, 1); // remove nextmapoverride; skipstats now 1 if available
-		}
-		skipstats = luaL_optinteger(L, 2, 0);
-	}
-	else
-	{
-		nextmapoverride = 0;
-		skipstats = 0;
+		nextmapoverride = (INT16)luaL_optinteger(L, 1, 0);
+		skipstats = (INT16)luaL_optinteger(L, 2, 0);
 	}
-	// ---
 
 	return 0;
 }
@@ -2720,6 +2854,22 @@ static int lib_gGametypeUsesLives(lua_State *L)
 	return 1;
 }
 
+static int lib_gGametypeUsesCoopLives(lua_State *L)
+{
+	//HUDSAFE
+	INLEVEL
+	lua_pushboolean(L, G_GametypeUsesCoopLives());
+	return 1;
+}
+
+static int lib_gGametypeUsesCoopStarposts(lua_State *L)
+{
+	//HUDSAFE
+	INLEVEL
+	lua_pushboolean(L, G_GametypeUsesCoopStarposts());
+	return 1;
+}
+
 static int lib_gGametypeHasTeams(lua_State *L)
 {
 	//HUDSAFE
@@ -2760,6 +2910,14 @@ static int lib_gTagGametype(lua_State *L)
 	return 1;
 }
 
+static int lib_gCompetitionGametype(lua_State *L)
+{
+	//HUDSAFE
+	INLEVEL
+	lua_pushboolean(L, G_CompetitionGametype());
+	return 1;
+}
+
 static int lib_gTicsToHours(lua_State *L)
 {
 	tic_t rtic = luaL_checkinteger(L, 1);
@@ -2997,6 +3155,7 @@ static luaL_Reg lib[] = {
 	{"S_StartMusicCaption", lib_sStartMusicCaption},
 
 	// g_game
+	{"G_AddGametype", lib_gAddGametype},
 	{"G_BuildMapName",lib_gBuildMapName},
 	{"G_DoReborn",lib_gDoReborn},
 	{"G_SetCustomExitVars",lib_gSetCustomExitVars},
@@ -3004,11 +3163,14 @@ static luaL_Reg lib[] = {
 	{"G_ExitLevel",lib_gExitLevel},
 	{"G_IsSpecialStage",lib_gIsSpecialStage},
 	{"G_GametypeUsesLives",lib_gGametypeUsesLives},
+	{"G_GametypeUsesCoopLives",lib_gGametypeUsesCoopLives},
+	{"G_GametypeUsesCoopStarposts",lib_gGametypeUsesCoopStarposts},
 	{"G_GametypeHasTeams",lib_gGametypeHasTeams},
 	{"G_GametypeHasSpectators",lib_gGametypeHasSpectators},
 	{"G_RingSlingerGametype",lib_gRingSlingerGametype},
 	{"G_PlatformGametype",lib_gPlatformGametype},
 	{"G_TagGametype",lib_gTagGametype},
+	{"G_CompetitionGametype",lib_gCompetitionGametype},
 	{"G_TicsToHours",lib_gTicsToHours},
 	{"G_TicsToMinutes",lib_gTicsToMinutes},
 	{"G_TicsToSeconds",lib_gTicsToSeconds},
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
index 2383bb32e1f77812b38f8602f7bccb7765a8cf4f..7f7dc95609770c81f2b974b17e510d3509350d46 100644
--- a/src/lua_blockmaplib.c
+++ b/src/lua_blockmaplib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2016 by Iestyn "Monster Iestyn" Jealous.
-// Copyright (C) 2016 by Sonic Team Junior.
+// Copyright (C) 2016-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index 9e74dac76ce05b746e88e3bb7081b5219d7dfc35..55854082909bfa43c097b586e58f42c2be3f5a95 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -245,7 +245,7 @@ static int lib_comBufAddText(lua_State *L)
 		return LUA_ErrInvalid(L, "player_t");
 	if (plr != &players[consoleplayer])
 		return 0;
-	COM_BufAddText(va("%s\n", luaL_checkstring(L, 2)));
+	COM_BufAddTextEx(va("%s\n", luaL_checkstring(L, 2)), COM_SAFE);
 	return 0;
 }
 
@@ -262,7 +262,7 @@ static int lib_comBufInsertText(lua_State *L)
 		return LUA_ErrInvalid(L, "player_t");
 	if (plr != &players[consoleplayer])
 		return 0;
-	COM_BufInsertText(va("%s\n", luaL_checkstring(L, 2)));
+	COM_BufInsertTextEx(va("%s\n", luaL_checkstring(L, 2)), COM_SAFE);
 	return 0;
 }
 
@@ -427,6 +427,26 @@ static int lib_cvRegisterVar(lua_State *L)
 	return 1;
 }
 
+static int lib_cvFindVar(lua_State *L)
+{
+	consvar_t *cv;
+	if (( cv = CV_FindVar(luaL_checkstring(L,1)) ))
+	{
+		lua_settop(L,1);/* We only want one argument in the stack. */
+		lua_pushlightuserdata(L, cv);/* Now the second value on stack. */
+		luaL_getmetatable(L, META_CVAR);
+		/*
+		The metatable is the last value on the stack, so this
+		applies it to the second value, which is the cvar.
+		*/
+		lua_setmetatable(L,2);
+		lua_pushvalue(L,2);
+		return 1;
+	}
+	else
+		return 0;
+}
+
 // CONS_Printf for a single player
 // Use 'print' in baselib for a global message.
 static int lib_consPrintf(lua_State *L)
@@ -466,6 +486,7 @@ static luaL_Reg lib[] = {
 	{"COM_BufAddText", lib_comBufAddText},
 	{"COM_BufInsertText", lib_comBufInsertText},
 	{"CV_RegisterVar", lib_cvRegisterVar},
+	{"CV_FindVar", lib_cvFindVar},
 	{"CONS_Printf", lib_consPrintf},
 	{NULL, NULL}
 };
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 37b1f3e0663e90882e5a5a3ec78fec4c925c05f7..61e945248ddeb3f82e8291eeae12452649592804 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -20,9 +20,12 @@ enum hook {
 	hook_MapChange,
 	hook_MapLoad,
 	hook_PlayerJoin,
+	hook_PreThinkFrame,
 	hook_ThinkFrame,
+	hook_PostThinkFrame,
 	hook_MobjSpawn,
 	hook_MobjCollide,
+	hook_MobjLineCollide,
 	hook_MobjMoveCollide,
 	hook_TouchSpecial,
 	hook_MobjFuse,
@@ -51,6 +54,9 @@ enum hook {
 	hook_PlayerCanDamage,
 	hook_PlayerQuit,
 	hook_IntermissionThinker,
+	hook_TeamSwitch,
+	hook_ViewpointSwitch,
+	hook_SeenPlayer,
 
 	hook_MAX // last hook
 };
@@ -59,12 +65,16 @@ extern const char *const hookNames[];
 void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load)
 void LUAh_MapLoad(void); // Hook for map load
 void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer
+void LUAh_PreThinkFrame(void); // Hook for frame (before mobj and player thinkers)
 void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers)
+void LUAh_PostThinkFrame(void); // Hook for frame (at end of tick, ie after overlays, precipitation, specials)
 boolean LUAh_MobjHook(mobj_t *mo, enum hook which);
 boolean LUAh_PlayerHook(player_t *plr, enum hook which);
 #define LUAh_MobjSpawn(mo) LUAh_MobjHook(mo, hook_MobjSpawn) // Hook for P_SpawnMobj by mobj type
 UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which);
+UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which);
 #define LUAh_MobjCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjCollide) // Hook for PIT_CheckThing by (thing) mobj type
+#define LUAh_MobjLineCollide(thing, line) LUAh_MobjLineCollideHook(thing, line, hook_MobjLineCollide) // Hook for PIT_CheckThing by (thing) mobj type
 #define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type
 boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type
 #define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
@@ -93,5 +103,10 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAft
 UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage
 void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting
 void LUAh_IntermissionThinker(void); // Hook for Y_Ticker
+boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh....
+UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode
+#ifdef SEENAMES
+boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend); // Hook for MT_NAMECHECK
+#endif
 
 #endif
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 03c7ce9113094ef6425a560bdac2ed9410cb8c76..56a4fa3f7f281f9e930f84f83ced919fef73ee64 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -31,9 +31,12 @@ const char *const hookNames[hook_MAX+1] = {
 	"MapChange",
 	"MapLoad",
 	"PlayerJoin",
+	"PreThinkFrame",
 	"ThinkFrame",
+	"PostThinkFrame",
 	"MobjSpawn",
 	"MobjCollide",
+	"MobjLineCollide",
 	"MobjMoveCollide",
 	"TouchSpecial",
 	"MobjFuse",
@@ -62,6 +65,9 @@ const char *const hookNames[hook_MAX+1] = {
 	"PlayerCanDamage",
 	"PlayerQuit",
 	"IntermissionThinker",
+	"TeamSwitch",
+	"ViewpointSwitch",
+	"SeenPlayer",
 	NULL
 };
 
@@ -121,6 +127,7 @@ static int lib_addHook(lua_State *L)
 	// Take a mobjtype enum which this hook is specifically for.
 	case hook_MobjSpawn:
 	case hook_MobjCollide:
+	case hook_MobjLineCollide:
 	case hook_MobjMoveCollide:
 	case hook_TouchSpecial:
 	case hook_MobjFuse:
@@ -180,6 +187,7 @@ static int lib_addHook(lua_State *L)
 		lastp = &mobjthinkerhooks[hook.s.mt];
 		break;
 	case hook_MobjCollide:
+	case hook_MobjLineCollide:
 	case hook_MobjMoveCollide:
 		lastp = &mobjcollidehooks[hook.s.mt];
 		break;
@@ -203,6 +211,9 @@ static int lib_addHook(lua_State *L)
 	case hook_PlayerSpawn:
 	case hook_FollowMobj:
 	case hook_PlayerCanDamage:
+	case hook_TeamSwitch:
+	case hook_ViewpointSwitch:
+	case hook_SeenPlayer:
 	case hook_ShieldSpawn:
 	case hook_ShieldSpecial:
 		lastp = &playerhooks;
@@ -407,6 +418,29 @@ void LUAh_PlayerJoin(int playernum)
 	lua_settop(gL, 0);
 }
 
+// Hook for frame (before mobj and player thinkers)
+void LUAh_PreThinkFrame(void)
+{
+	hook_p hookp;
+	if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8))))
+		return;
+
+	for (hookp = roothook; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != hook_PreThinkFrame)
+			continue;
+
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		if (lua_pcall(gL, 0, 0, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+		}
+	}
+}
+
 // Hook for frame (after mobj and player thinkers)
 void LUAh_ThinkFrame(void)
 {
@@ -430,6 +464,30 @@ void LUAh_ThinkFrame(void)
 	}
 }
 
+
+// Hook for frame (at end of tick, ie after overlays, precipitation, specials)
+void LUAh_PostThinkFrame(void)
+{
+	hook_p hookp;
+	if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8))))
+		return;
+
+	for (hookp = roothook; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != hook_PostThinkFrame)
+			continue;
+
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		if (lua_pcall(gL, 0, 0, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+		}
+	}
+}
+
 // Hook for mobj collisions
 UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 {
@@ -509,6 +567,84 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
 	return shouldCollide;
 }
 
+UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which)
+{
+	hook_p hookp;
+	UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no.
+	if (!gL || !(hooksAvailable[which/8] & (1<<(which%8))))
+		return 0;
+
+	I_Assert(thing->type < NUMMOBJTYPES);
+
+	lua_settop(gL, 0);
+
+	// Look for all generic mobj collision hooks
+	for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != which)
+			continue;
+
+		if (lua_gettop(gL) == 0)
+		{
+			LUA_PushUserdata(gL, thing, META_MOBJ);
+			LUA_PushUserdata(gL, line, META_LINE);
+		}
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -3);
+		lua_pushvalue(gL, -3);
+		if (lua_pcall(gL, 2, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (!lua_isnil(gL, -1))
+		{ // if nil, leave shouldCollide = 0.
+			if (lua_toboolean(gL, -1))
+				shouldCollide = 1; // Force yes
+			else
+				shouldCollide = 2; // Force no
+		}
+		lua_pop(gL, 1);
+	}
+
+	for (hookp = mobjcollidehooks[thing->type]; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != which)
+			continue;
+
+		if (lua_gettop(gL) == 0)
+		{
+			LUA_PushUserdata(gL, thing, META_MOBJ);
+			LUA_PushUserdata(gL, line, META_LINE);
+		}
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -3);
+		lua_pushvalue(gL, -3);
+		if (lua_pcall(gL, 2, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (!lua_isnil(gL, -1))
+		{ // if nil, leave shouldCollide = 0.
+			if (lua_toboolean(gL, -1))
+				shouldCollide = 1; // Force yes
+			else
+				shouldCollide = 2; // Force no
+		}
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	return shouldCollide;
+}
+
 // Hook for mobj thinkers
 boolean LUAh_MobjThinker(mobj_t *mo)
 {
@@ -657,14 +793,16 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
 			LUA_PushUserdata(gL, source, META_MOBJ);
 			lua_pushinteger(gL, damage);
+			lua_pushinteger(gL, damagetype);
 		}
 		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 		lua_gettable(gL, LUA_REGISTRYINDEX);
-		lua_pushvalue(gL, -5);
-		lua_pushvalue(gL, -5);
-		lua_pushvalue(gL, -5);
-		lua_pushvalue(gL, -5);
-		if (lua_pcall(gL, 4, 1, 0)) {
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		if (lua_pcall(gL, 5, 1, 0)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -745,14 +883,16 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
 			LUA_PushUserdata(gL, source, META_MOBJ);
 			lua_pushinteger(gL, damage);
+			lua_pushinteger(gL, damagetype);
 		}
 		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 		lua_gettable(gL, LUA_REGISTRYINDEX);
-		lua_pushvalue(gL, -5);
-		lua_pushvalue(gL, -5);
-		lua_pushvalue(gL, -5);
-		lua_pushvalue(gL, -5);
-		if (lua_pcall(gL, 4, 1, 0)) {
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		if (lua_pcall(gL, 5, 1, 0)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -823,13 +963,15 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8
 			LUA_PushUserdata(gL, target, META_MOBJ);
 			LUA_PushUserdata(gL, inflictor, META_MOBJ);
 			LUA_PushUserdata(gL, source, META_MOBJ);
+			lua_pushinteger(gL, damagetype);
 		}
 		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 		lua_gettable(gL, LUA_REGISTRYINDEX);
-		lua_pushvalue(gL, -4);
-		lua_pushvalue(gL, -4);
-		lua_pushvalue(gL, -4);
-		if (lua_pcall(gL, 3, 1, 0)) {
+		lua_pushvalue(gL, -5);
+		lua_pushvalue(gL, -5);
+		lua_pushvalue(gL, -5);
+		lua_pushvalue(gL, -5);
+		if (lua_pcall(gL, 4, 1, 0)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
@@ -1346,4 +1488,146 @@ void LUAh_IntermissionThinker(void)
 	}
 }
 
+// Hook for team switching
+// It's just an edit of LUAh_ViewpointSwitch.
+boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble)
+{
+	hook_p hookp;
+	boolean canSwitchTeam = true;
+	if (!gL || !(hooksAvailable[hook_TeamSwitch/8] & (1<<(hook_TeamSwitch%8))))
+		return true;
+
+	lua_settop(gL, 0);
+
+	for (hookp = playerhooks; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != hook_TeamSwitch)
+			continue;
+
+		if (lua_gettop(gL) == 0)
+		{
+			LUA_PushUserdata(gL, player, META_PLAYER);
+			lua_pushinteger(gL, newteam);
+			lua_pushboolean(gL, fromspectators);
+			lua_pushboolean(gL, tryingautobalance);
+			lua_pushboolean(gL, tryingscramble);
+		}
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		lua_pushvalue(gL, -6);
+		if (lua_pcall(gL, 5, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1))
+			canSwitchTeam = false; // Can't switch team
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	return canSwitchTeam;
+}
+
+// Hook for spy mode
+UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced)
+{
+	hook_p hookp;
+	UINT8 canSwitchView = 0; // 0 = default, 1 = force yes, 2 = force no.
+	if (!gL || !(hooksAvailable[hook_ViewpointSwitch/8] & (1<<(hook_ViewpointSwitch%8))))
+		return 0;
+
+	lua_settop(gL, 0);
+	hud_running = true; // local hook
+
+	for (hookp = playerhooks; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != hook_ViewpointSwitch)
+			continue;
+
+		if (lua_gettop(gL) == 0)
+		{
+			LUA_PushUserdata(gL, player, META_PLAYER);
+			LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER);
+			lua_pushboolean(gL, forced);
+		}
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -4);
+		lua_pushvalue(gL, -4);
+		lua_pushvalue(gL, -4);
+		if (lua_pcall(gL, 3, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (!lua_isnil(gL, -1))
+		{ // if nil, leave canSwitchView = 0.
+			if (lua_toboolean(gL, -1))
+				canSwitchView = 1; // Force viewpoint switch
+			else
+				canSwitchView = 2; // Skip viewpoint switch
+		}
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	hud_running = false;
+
+	return canSwitchView;
+}
+
+// Hook for MT_NAMECHECK
+#ifdef SEENAMES
+boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
+{
+	hook_p hookp;
+	boolean hasSeenPlayer = true;
+	if (!gL || !(hooksAvailable[hook_SeenPlayer/8] & (1<<(hook_SeenPlayer%8))))
+		return 0;
+
+	lua_settop(gL, 0);
+	hud_running = true; // local hook
+
+	for (hookp = playerhooks; hookp; hookp = hookp->next)
+	{
+		if (hookp->type != hook_SeenPlayer)
+			continue;
+
+		if (lua_gettop(gL) == 0)
+		{
+			LUA_PushUserdata(gL, player, META_PLAYER);
+			LUA_PushUserdata(gL, seenfriend, META_PLAYER);
+		}
+		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+		lua_gettable(gL, LUA_REGISTRYINDEX);
+		lua_pushvalue(gL, -3);
+		lua_pushvalue(gL, -3);
+		if (lua_pcall(gL, 2, 1, 0)) {
+			if (!hookp->error || cv_debug & DBG_LUA)
+				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+			lua_pop(gL, 1);
+			hookp->error = true;
+			continue;
+		}
+		if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1))
+			hasSeenPlayer = false; // Hasn't seen player
+		lua_pop(gL, 1);
+	}
+
+	lua_settop(gL, 0);
+	hud_running = false;
+
+	return hasSeenPlayer;
+}
+#endif // SEENAMES
+
 #endif
diff --git a/src/lua_hud.h b/src/lua_hud.h
index d1adef7dc09abb760151aa3c0322cf68078e8931..a00a5cb028bca8f63b1e9349757742e79405b703 100644
--- a/src/lua_hud.h
+++ b/src/lua_hud.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2018 by Sonic Team Junior.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -21,6 +21,7 @@ enum hud {
 	// Match / CTF / Tag / Ringslinger
 	hud_weaponrings,
 	hud_powerstones,
+	hud_teamscores,
 	// NiGHTS mode
 	hud_nightslink,
 	hud_nightsdrill,
@@ -33,6 +34,9 @@ enum hud {
 	hud_coopemeralds,
 	hud_tokens,
 	hud_tabemblems,
+	// Intermission
+	hud_intermissiontally,
+	hud_intermissionmessages,
 	hud_MAX
 };
 
@@ -43,4 +47,5 @@ boolean LUA_HudEnabled(enum hud option);
 void LUAh_GameHUD(player_t *stplyr);
 void LUAh_ScoresHUD(void);
 void LUAh_TitleHUD(void);
-void LUAh_TitleCardHUD(player_t *stplyr);
+void LUAh_TitleCardHUD(player_t *stplayr);
+void LUAh_IntermissionHUD(void);
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 9b12dd3c034f824f2b551d3f349b58e9716874f0..58af64359e00abfbd309565aade645280f68a3b2 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2018 by Sonic Team Junior.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -36,6 +36,11 @@ static UINT8 hud_enabled[(hud_MAX/8)+1];
 
 static UINT8 hudAvailable; // hud hooks field
 
+#ifdef LUA_PATCH_SAFETY
+static patchinfo_t *patchinfo, *patchinfohead;
+static int numluapatches;
+#endif
+
 // must match enum hud in lua_hud.h
 static const char *const hud_disable_options[] = {
 	"stagetitle",
@@ -48,6 +53,7 @@ static const char *const hud_disable_options[] = {
 
 	"weaponrings",
 	"powerstones",
+	"teamscores",
 
 	"nightslink",
 	"nightsdrill",
@@ -60,6 +66,9 @@ static const char *const hud_disable_options[] = {
 	"coopemeralds",
 	"tokens",
 	"tabemblems",
+
+	"intermissiontally",
+	"intermissionmessages",
 	NULL};
 
 enum hudinfo {
@@ -92,12 +101,14 @@ static const char *const patch_opt[] = {
 enum hudhook {
 	hudhook_game = 0,
 	hudhook_scores,
+	hudhook_intermission,
 	hudhook_title,
 	hudhook_titlecard
 };
 static const char *const hudhook_opt[] = {
 	"game",
 	"scores",
+	"intermission",
 	"title",
 	"titlecard",
 	NULL};
@@ -250,12 +261,17 @@ static int colormap_get(lua_State *L)
 
 static int patch_get(lua_State *L)
 {
+#ifdef LUA_PATCH_SAFETY
 	patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH));
+#else
+	patchinfo_t *patch = *((patchinfo_t **)luaL_checkudata(L, 1, META_PATCH));
+#endif
 	enum patch field = luaL_checkoption(L, 2, NULL, patch_opt);
 
 	// patches are CURRENTLY always valid, expected to be cached with PU_STATIC
 	// this may change in the future, so patch.valid still exists
-	I_Assert(patch != NULL);
+	if (!patch)
+		return LUA_ErrInvalid(L, "patch_t");
 
 	switch (field)
 	{
@@ -352,8 +368,59 @@ static int libd_patchExists(lua_State *L)
 
 static int libd_cachePatch(lua_State *L)
 {
+#ifdef LUA_PATCH_SAFETY
+	int i;
+	lumpnum_t lumpnum;
+	patchinfo_t *luapat;
+	patch_t *realpatch;
+
 	HUDONLY
-	LUA_PushUserdata(L, W_CachePatchName(luaL_checkstring(L, 1), PU_STATIC), META_PATCH);
+
+	luapat = patchinfohead;
+	lumpnum = W_CheckNumForName(luaL_checkstring(L, 1));
+	if (lumpnum == LUMPERROR)
+		lumpnum = W_GetNumForName("MISSING");
+
+	for (i = 0; i < numluapatches; i++)
+	{
+		// check if already cached
+		if (luapat->wadnum == WADFILENUM(lumpnum) && luapat->lumpnum == LUMPNUM(lumpnum))
+		{
+			LUA_PushUserdata(L, luapat, META_PATCH);
+			return 1;
+		}
+		luapat = luapat->next;
+		if (!luapat)
+			break;
+	}
+
+	if (numluapatches > 0)
+	{
+		patchinfo->next = Z_Malloc(sizeof(patchinfo_t), PU_STATIC, NULL);
+		patchinfo = patchinfo->next;
+	}
+	else
+	{
+		patchinfo = Z_Malloc(sizeof(patchinfo_t), PU_STATIC, NULL);
+		patchinfohead = patchinfo;
+	}
+
+	realpatch = W_CachePatchNum(lumpnum, PU_PATCH);
+
+	patchinfo->width = realpatch->width;
+	patchinfo->height = realpatch->height;
+	patchinfo->leftoffset = realpatch->leftoffset;
+	patchinfo->topoffset = realpatch->topoffset;
+
+	patchinfo->wadnum = WADFILENUM(lumpnum);
+	patchinfo->lumpnum = LUMPNUM(lumpnum);
+
+	LUA_PushUserdata(L, patchinfo, META_PATCH);
+	numluapatches++;
+#else
+	HUDONLY
+	LUA_PushUserdata(L, W_CachePatchName(luaL_checkstring(L, 1), PU_PATCH), META_PATCH);
+#endif
 	return 1;
 }
 
@@ -409,7 +476,7 @@ static int libd_getSpritePatch(lua_State *L)
 		return 0;
 
 	// push both the patch and it's "flip" value
-	LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_STATIC), META_PATCH);
+	LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_PATCH), META_PATCH);
 	lua_pushboolean(L, (sprframe->flip & (1<<angle)) != 0);
 	return 2;
 }
@@ -504,7 +571,7 @@ static int libd_getSprite2Patch(lua_State *L)
 		return 0;
 
 	// push both the patch and it's "flip" value
-	LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_STATIC), META_PATCH);
+	LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_PATCH), META_PATCH);
 	lua_pushboolean(L, (sprframe->flip & (1<<angle)) != 0);
 	return 2;
 }
@@ -513,12 +580,22 @@ static int libd_draw(lua_State *L)
 {
 	INT32 x, y, flags;
 	patch_t *patch;
+#ifdef LUA_PATCH_SAFETY
+	patchinfo_t *luapat;
+#endif
 	const UINT8 *colormap = NULL;
 
 	HUDONLY
 	x = luaL_checkinteger(L, 1);
 	y = luaL_checkinteger(L, 2);
+#ifdef LUA_PATCH_SAFETY
+	luapat = *((patchinfo_t **)luaL_checkudata(L, 3, META_PATCH));
+	patch = W_CachePatchNum((luapat->wadnum<<16)+luapat->lumpnum, PU_PATCH);
+#else
 	patch = *((patch_t **)luaL_checkudata(L, 3, META_PATCH));
+	if (!patch)
+		return LUA_ErrInvalid(L, "patch_t");
+#endif
 	flags = luaL_optinteger(L, 4, 0);
 	if (!lua_isnoneornil(L, 5))
 		colormap = *((UINT8 **)luaL_checkudata(L, 5, META_COLORMAP));
@@ -534,6 +611,9 @@ static int libd_drawScaled(lua_State *L)
 	fixed_t x, y, scale;
 	INT32 flags;
 	patch_t *patch;
+#ifdef LUA_PATCH_SAFETY
+	patchinfo_t *luapat;
+#endif
 	const UINT8 *colormap = NULL;
 
 	HUDONLY
@@ -542,7 +622,14 @@ static int libd_drawScaled(lua_State *L)
 	scale = luaL_checkinteger(L, 3);
 	if (scale < 0)
 		return luaL_error(L, "negative scale");
+#ifdef LUA_PATCH_SAFETY
+	luapat = *((patchinfo_t **)luaL_checkudata(L, 4, META_PATCH));
+	patch = W_CachePatchNum((luapat->wadnum<<16)+luapat->lumpnum, PU_PATCH);
+#else
 	patch = *((patch_t **)luaL_checkudata(L, 4, META_PATCH));
+	if (!patch)
+		return LUA_ErrInvalid(L, "patch_t");
+#endif
 	flags = luaL_optinteger(L, 5, 0);
 	if (!lua_isnoneornil(L, 6))
 		colormap = *((UINT8 **)luaL_checkudata(L, 6, META_COLORMAP));
@@ -1041,6 +1128,10 @@ int LUA_HudLib(lua_State *L)
 {
 	memset(hud_enabled, 0xff, (hud_MAX/8)+1);
 
+#ifdef LUA_PATCH_SAFETY
+	numluapatches = 0;
+#endif
+
 	lua_newtable(L); // HUD registry table
 		lua_newtable(L);
 		luaL_register(L, NULL, lib_draw);
@@ -1050,13 +1141,16 @@ int LUA_HudLib(lua_State *L)
 		lua_rawseti(L, -2, 2); // HUD[2] = game rendering functions array
 
 		lua_newtable(L);
-		lua_rawseti(L, -2, 3); // HUD[2] = scores rendering functions array
+		lua_rawseti(L, -2, 3); // HUD[3] = scores rendering functions array
+
+		lua_newtable(L);
+		lua_rawseti(L, -2, 4); // HUD[4] = intermission rendering functions array
 
 		lua_newtable(L);
-		lua_rawseti(L, -2, 4); // HUD[3] = title rendering functions array
+		lua_rawseti(L, -2, 5); // HUD[5] = title rendering functions array
 
 		lua_newtable(L);
-		lua_rawseti(L, -2, 5); // HUD[4] = title card rendering functions array
+		lua_rawseti(L, -2, 6); // HUD[6] = title card rendering functions array
 	lua_setfield(L, LUA_REGISTRYINDEX, "HUD");
 
 	luaL_newmetatable(L, META_HUDINFO);
@@ -1228,4 +1322,29 @@ void LUAh_TitleCardHUD(player_t *stplayr)
 	hud_running = false;
 }
 
+void LUAh_IntermissionHUD(void)
+{
+	if (!gL || !(hudAvailable & (1<<hudhook_intermission)))
+		return;
+
+	hud_running = true;
+	lua_pop(gL, -1);
+
+	lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
+	I_Assert(lua_istable(gL, -1));
+	lua_rawgeti(gL, -1, 4); // HUD[4] = rendering funcs
+	I_Assert(lua_istable(gL, -1));
+
+	lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
+	I_Assert(lua_istable(gL, -1));
+	lua_remove(gL, -3); // pop HUD
+	lua_pushnil(gL);
+	while (lua_next(gL, -3) != 0) {
+		lua_pushvalue(gL, -3); // graphics library (HUD[1])
+		LUA_Call(gL, 1);
+	}
+	lua_pop(gL, -1);
+	hud_running = false;
+}
+
 #endif
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index bee1d20ec3237ff70710bcd8ff4a33beef54c63d..7a2465ef770ad2c36fde2805cb585360bf20d007 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -266,7 +266,6 @@ static int lib_getSpriteInfo(lua_State *L)
 #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to spriteinfo[] (%s)", e);
 #define TYPEERROR(f, t1, t2) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t1), lua_typename(L, t2)))
 
-#ifdef ROTSPRITE
 static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, int idx)
 {
 	int okcool = 0;
@@ -360,7 +359,6 @@ static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk)
 
 	return 0;
 }
-#endif
 
 static int lib_setSpriteInfo(lua_State *L)
 {
@@ -393,14 +391,11 @@ static int lib_setSpriteInfo(lua_State *L)
 		if (lua_isnumber(L, 2))
 		{
 			i = lua_tointeger(L, 2);
-#ifndef ROTSPRITE
 			i++; // shift index in case of missing rotsprite support
-#endif
 		}
 		else
 			str = luaL_checkstring(L, 2);
 
-#ifdef ROTSPRITE
 		if (i == 1 || (str && fastcmp(str, "pivot")))
 		{
 			// pivot[] is a table
@@ -409,7 +404,6 @@ static int lib_setSpriteInfo(lua_State *L)
 			else
 				FIELDERROR("pivot", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1)))
 		}
-#endif
 
 		lua_pop(L, 1);
 	}
@@ -434,7 +428,6 @@ static int spriteinfo_get(lua_State *L)
 
 	I_Assert(sprinfo != NULL);
 
-#ifdef ROTSPRITE
 	// push spriteframepivot_t userdata
 	if (fastcmp(field, "pivot"))
 	{
@@ -448,7 +441,6 @@ static int spriteinfo_get(lua_State *L)
 		return 1;
 	}
 	else
-#endif
 		return luaL_error(L, LUA_QL("spriteinfo_t") " has no field named " LUA_QS, field);
 
 	return 0;
@@ -473,6 +465,7 @@ static int spriteinfo_set(lua_State *L)
 #ifdef ROTSPRITE
 	if (sprites != NULL)
 		R_FreeSingleRotSprite(&sprites[sprinfo-spriteinfo]);
+#endif
 
 	if (fastcmp(field, "pivot"))
 	{
@@ -488,7 +481,6 @@ static int spriteinfo_set(lua_State *L)
 		}
 	}
 	else
-#endif
 		return luaL_error(L, va("Field %s does not exist in spriteinfo_t", field));
 
 	return 0;
@@ -506,7 +498,6 @@ static int spriteinfo_num(lua_State *L)
 }
 
 // framepivot_t
-#ifdef ROTSPRITE
 static int pivotlist_get(lua_State *L)
 {
 	void **userdata;
@@ -616,7 +607,6 @@ static int framepivot_num(lua_State *L)
 	lua_pushinteger(L, 2);
 	return 1;
 }
-#endif
 
 ////////////////
 // STATE INFO //
@@ -1538,7 +1528,6 @@ int LUA_InfoLib(lua_State *L)
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
 
-#ifdef ROTSPRITE
 	luaL_newmetatable(L, META_PIVOTLIST);
 		lua_pushcfunction(L, pivotlist_get);
 		lua_setfield(L, -2, "__index");
@@ -1560,7 +1549,6 @@ int LUA_InfoLib(lua_State *L)
 		lua_pushcfunction(L, framepivot_num);
 		lua_setfield(L, -2, "__len");
 	lua_pop(L, 1);
-#endif
 
 	lua_newuserdata(L, 0);
 		lua_createtable(L, 0, 2);
diff --git a/src/lua_libs.h b/src/lua_libs.h
index ccf9febaefa9635b4f1e45c427e8915f9f5d9403..6a908d03dbc7069990198f3fa3d940e36bf77177 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -23,10 +23,8 @@ extern lua_State *gL;
 #define META_MOBJINFO "MOBJINFO_T*"
 #define META_SFXINFO "SFXINFO_T*"
 #define META_SPRITEINFO "SPRITEINFO_T*"
-#ifdef ROTSPRITE
 #define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]"
 #define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*"
-#endif
 
 #define META_MOBJ "MOBJ_T*"
 #define META_MAPTHING "MAPTHING_T*"
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 1da232efa0e07bd9899d45333f05fa78aa564c63..309ba86a112dcae1efbbba40bc3850ff10b802e3 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -134,6 +134,7 @@ enum side_e {
 	side_toptexture,
 	side_bottomtexture,
 	side_midtexture,
+	side_line,
 	side_sector,
 	side_special,
 	side_repeatcnt,
@@ -156,15 +157,13 @@ static const char *const side_opt[] = {
 enum vertex_e {
 	vertex_valid = 0,
 	vertex_x,
-	vertex_y,
-	vertex_z
+	vertex_y
 };
 
 static const char *const vertex_opt[] = {
 	"valid",
 	"x",
 	"y",
-	"z",
 	NULL};
 
 enum ffloor_e {
@@ -445,7 +444,7 @@ static int sectorlines_get(lua_State *L)
 	// get the "linecount" by shifting our retrieved memory address of "lines" to where "linecount" is in the sector_t, then dereferencing the result
 	// we need this to determine the array's actual size, and therefore also the maximum value allowed as an index
 	// this only works if seclines is actually a pointer to a sector's lines member in memory, oh boy
-	numoflines = (size_t)(*(seclines - (offsetof(sector_t, lines) - offsetof(sector_t, linecount))));
+	numoflines = (size_t)(*(size_t *)(((size_t)seclines) - (offsetof(sector_t, lines) - offsetof(sector_t, linecount))));
 
 /* OLD HACK
 	// check first linedef to figure which of its sectors owns this sector->lines pointer
@@ -479,7 +478,7 @@ static int sectorlines_num(lua_State *L)
 		return luaL_error(L, "accessed sector_t.lines doesn't exist anymore.");
 
 	// see comments in the _get function above
-	numoflines = (size_t)(*(seclines - (offsetof(sector_t, lines) - offsetof(sector_t, linecount))));
+	numoflines = (size_t)(*(size_t *)(((size_t)seclines) - (offsetof(sector_t, lines) - offsetof(sector_t, linecount))));
 	lua_pushinteger(L, numoflines);
 	return 1;
 }
@@ -869,6 +868,9 @@ static int side_get(lua_State *L)
 	case side_midtexture:
 		lua_pushinteger(L, side->midtexture);
 		return 1;
+	case side_line:
+		LUA_PushUserdata(L, side->line, META_LINE);
+		return 1;
 	case side_sector:
 		LUA_PushUserdata(L, side->sector, META_SECTOR);
 		return 1;
@@ -902,6 +904,7 @@ static int side_set(lua_State *L)
 	switch(field)
 	{
 	case side_valid: // valid
+	case side_line:
 	case side_sector:
 	case side_special:
 	case side_text:
@@ -965,9 +968,6 @@ static int vertex_get(lua_State *L)
 	case vertex_y:
 		lua_pushfixed(L, vertex->y);
 		return 1;
-	case vertex_z:
-		lua_pushfixed(L, vertex->z);
-		return 1;
 	}
 	return 0;
 }
@@ -2071,6 +2071,12 @@ static int mapheaderinfo_get(lua_State *L)
 		lua_pushinteger(L, header->levelselect);
 	else if (fastcmp(field,"bonustype"))
 		lua_pushinteger(L, header->bonustype);
+	else if (fastcmp(field,"ltzzpatch"))
+		lua_pushstring(L, header->ltzzpatch);
+	else if (fastcmp(field,"ltzztext"))
+		lua_pushstring(L, header->ltzztext);
+	else if (fastcmp(field,"ltactdiamond"))
+		lua_pushstring(L, header->ltactdiamond);
 	else if (fastcmp(field,"maxbonuslives"))
 		lua_pushinteger(L, header->maxbonuslives);
 	else if (fastcmp(field,"levelflags"))
diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index 4c5424577da6ae3591d6e7508742e1c6ecd8b7d6..9115c7321d17b13a0bc93c1c55d3ccefbe1d74ae 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index d5c6bd35d44d6623ccaf6e195b4ea6941fa31cc0..22248775153b5afd4ce21c61656fc3f3b73a8a50 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -32,9 +32,7 @@ enum mobj_e {
 	mobj_snext,
 	mobj_sprev,
 	mobj_angle,
-#ifdef ROTSPRITE
 	mobj_rollangle,
-#endif
 	mobj_sprite,
 	mobj_frame,
 	mobj_sprite2,
@@ -101,9 +99,7 @@ static const char *const mobj_opt[] = {
 	"snext",
 	"sprev",
 	"angle",
-#ifdef ROTSPRITE
 	"rollangle",
-#endif
 	"sprite",
 	"frame",
 	"sprite2",
@@ -205,11 +201,9 @@ static int mobj_get(lua_State *L)
 	case mobj_angle:
 		lua_pushangle(L, mo->angle);
 		break;
-#ifdef ROTSPRITE
 	case mobj_rollangle:
 		lua_pushangle(L, mo->rollangle);
 		break;
-#endif
 	case mobj_sprite:
 		lua_pushinteger(L, mo->sprite);
 		break;
@@ -462,11 +456,9 @@ static int mobj_set(lua_State *L)
 		else if (mo->player == &players[secondarydisplayplayer])
 			localangle2 = mo->angle;
 		break;
-#ifdef ROTSPRITE
 	case mobj_rollangle:
 		mo->rollangle = luaL_checkangle(L, 3);
 		break;
-#endif
 	case mobj_sprite:
 		mo->sprite = luaL_checkinteger(L, 3);
 		break;
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 6bda1988a53479b388557e20cf6ab0e207793ce1..c501fbbb2af25a00d3ee35bc57f30b9bc4f37394 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_script.c b/src/lua_script.c
index ec73d7bf70136762eeec08edda22ccdf9ac762d4..eb1afaf09ffc3fb9f6abc8305098be7908669456 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -18,7 +18,9 @@
 #include "w_wad.h"
 #include "p_setup.h"
 #include "r_state.h"
+#include "r_sky.h"
 #include "g_game.h"
+#include "f_finale.h"
 #include "byteptr.h"
 #include "p_saveg.h"
 #include "p_local.h"
@@ -79,8 +81,237 @@ FUNCNORETURN static int LUA_Panic(lua_State *L)
 #endif
 }
 
+// Moved here from lib_getenum.
+int LUA_PushGlobals(lua_State *L, const char *word)
+{
+	if (fastcmp(word,"gamemap")) {
+		lua_pushinteger(L, gamemap);
+		return 1;
+	} else if (fastcmp(word,"maptol")) {
+		lua_pushinteger(L, maptol);
+		return 1;
+	} else if (fastcmp(word,"ultimatemode")) {
+		lua_pushboolean(L, ultimatemode != 0);
+		return 1;
+	} else if (fastcmp(word,"mariomode")) {
+		lua_pushboolean(L, mariomode != 0);
+		return 1;
+	} else if (fastcmp(word,"twodlevel")) {
+		lua_pushboolean(L, twodlevel != 0);
+		return 1;
+	} else if (fastcmp(word,"circuitmap")) {
+		lua_pushboolean(L, circuitmap);
+		return 1;
+	} else if (fastcmp(word,"netgame")) {
+		lua_pushboolean(L, netgame);
+		return 1;
+	} else if (fastcmp(word,"multiplayer")) {
+		lua_pushboolean(L, multiplayer);
+		return 1;
+	} else if (fastcmp(word,"modeattacking")) {
+		lua_pushboolean(L, modeattacking);
+		return 1;
+	} else if (fastcmp(word,"splitscreen")) {
+		lua_pushboolean(L, splitscreen);
+		return 1;
+	} else if (fastcmp(word,"gamecomplete")) {
+		lua_pushboolean(L, gamecomplete);
+		return 1;
+	} else if (fastcmp(word,"devparm")) {
+		lua_pushboolean(L, devparm);
+		return 1;
+	} else if (fastcmp(word,"modifiedgame")) {
+		lua_pushboolean(L, modifiedgame && !savemoddata);
+		return 1;
+	} else if (fastcmp(word,"menuactive")) {
+		lua_pushboolean(L, menuactive);
+		return 1;
+	} else if (fastcmp(word,"paused")) {
+		lua_pushboolean(L, paused);
+		return 1;
+	} else if (fastcmp(word,"bluescore")) {
+		lua_pushinteger(L, bluescore);
+		return 1;
+	} else if (fastcmp(word,"redscore")) {
+		lua_pushinteger(L, redscore);
+		return 1;
+	} else if (fastcmp(word,"timelimit")) {
+		lua_pushinteger(L, cv_timelimit.value);
+		return 1;
+	} else if (fastcmp(word,"pointlimit")) {
+		lua_pushinteger(L, cv_pointlimit.value);
+		return 1;
+	// begin map vars
+	} else if (fastcmp(word,"spstage_start")) {
+		lua_pushinteger(L, spstage_start);
+		return 1;
+	} else if (fastcmp(word,"sstage_start")) {
+		lua_pushinteger(L, sstage_start);
+		return 1;
+	} else if (fastcmp(word,"sstage_end")) {
+		lua_pushinteger(L, sstage_end);
+		return 1;
+	} else if (fastcmp(word,"smpstage_start")) {
+		lua_pushinteger(L, smpstage_start);
+		return 1;
+	} else if (fastcmp(word,"smpstage_end")) {
+		lua_pushinteger(L, smpstage_end);
+		return 1;
+	} else if (fastcmp(word,"titlemap")) {
+		lua_pushinteger(L, titlemap);
+		return 1;
+	} else if (fastcmp(word,"titlemapinaction")) {
+		lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF));
+		return 1;
+	} else if (fastcmp(word,"bootmap")) {
+		lua_pushinteger(L, bootmap);
+		return 1;
+	} else if (fastcmp(word,"tutorialmap")) {
+		lua_pushinteger(L, tutorialmap);
+		return 1;
+	} else if (fastcmp(word,"tutorialmode")) {
+		lua_pushboolean(L, tutorialmode);
+		return 1;
+	// end map vars
+	// begin CTF colors
+	} else if (fastcmp(word,"skincolor_redteam")) {
+		lua_pushinteger(L, skincolor_redteam);
+		return 1;
+	} else if (fastcmp(word,"skincolor_blueteam")) {
+		lua_pushinteger(L, skincolor_blueteam);
+		return 1;
+	} else if (fastcmp(word,"skincolor_redring")) {
+		lua_pushinteger(L, skincolor_redring);
+		return 1;
+	} else if (fastcmp(word,"skincolor_bluering")) {
+		lua_pushinteger(L, skincolor_bluering);
+		return 1;
+	// end CTF colors
+	// begin timers
+	} else if (fastcmp(word,"invulntics")) {
+		lua_pushinteger(L, invulntics);
+		return 1;
+	} else if (fastcmp(word,"sneakertics")) {
+		lua_pushinteger(L, sneakertics);
+		return 1;
+	} else if (fastcmp(word,"flashingtics")) {
+		lua_pushinteger(L, flashingtics);
+		return 1;
+	} else if (fastcmp(word,"tailsflytics")) {
+		lua_pushinteger(L, tailsflytics);
+		return 1;
+	} else if (fastcmp(word,"underwatertics")) {
+		lua_pushinteger(L, underwatertics);
+		return 1;
+	} else if (fastcmp(word,"spacetimetics")) {
+		lua_pushinteger(L, spacetimetics);
+		return 1;
+	} else if (fastcmp(word,"extralifetics")) {
+		lua_pushinteger(L, extralifetics);
+		return 1;
+	} else if (fastcmp(word,"nightslinktics")) {
+		lua_pushinteger(L, nightslinktics);
+		return 1;
+	} else if (fastcmp(word,"gameovertics")) {
+		lua_pushinteger(L, gameovertics);
+		return 1;
+	} else if (fastcmp(word,"ammoremovaltics")) {
+		lua_pushinteger(L, ammoremovaltics);
+		return 1;
+	// end timers
+	} else if (fastcmp(word,"gametype")) {
+		lua_pushinteger(L, gametype);
+		return 1;
+	} else if (fastcmp(word,"gametyperules")) {
+		lua_pushinteger(L, gametyperules);
+		return 1;
+	} else if (fastcmp(word,"leveltime")) {
+		lua_pushinteger(L, leveltime);
+		return 1;
+	} else if (fastcmp(word,"sstimer")) {
+		lua_pushinteger(L, sstimer);
+		return 1;
+	} else if (fastcmp(word,"curWeather")) {
+		lua_pushinteger(L, curWeather);
+		return 1;
+	} else if (fastcmp(word,"globalweather")) {
+		lua_pushinteger(L, globalweather);
+		return 1;
+	} else if (fastcmp(word,"levelskynum")) {
+		lua_pushinteger(L, levelskynum);
+		return 1;
+	} else if (fastcmp(word,"globallevelskynum")) {
+		lua_pushinteger(L, globallevelskynum);
+		return 1;
+	} else if (fastcmp(word,"mapmusname")) {
+		lua_pushstring(L, mapmusname);
+		return 1;
+	} else if (fastcmp(word,"mapmusflags")) {
+		lua_pushinteger(L, mapmusflags);
+		return 1;
+	} else if (fastcmp(word,"mapmusposition")) {
+		lua_pushinteger(L, mapmusposition);
+		return 1;
+	// local player variables, by popular request
+	} else if (fastcmp(word,"consoleplayer")) { // player controlling console (aka local player 1)
+		if (consoleplayer < 0 || !playeringame[consoleplayer])
+			return 0;
+		LUA_PushUserdata(L, &players[consoleplayer], META_PLAYER);
+		return 1;
+	} else if (fastcmp(word,"displayplayer")) { // player visible on screen (aka display player 1)
+		if (displayplayer < 0 || !playeringame[displayplayer])
+			return 0;
+		LUA_PushUserdata(L, &players[displayplayer], META_PLAYER);
+		return 1;
+	} else if (fastcmp(word,"secondarydisplayplayer")) { // local/display player 2, for splitscreen
+		if (!splitscreen || secondarydisplayplayer < 0 || !playeringame[secondarydisplayplayer])
+			return 0;
+		LUA_PushUserdata(L, &players[secondarydisplayplayer], META_PLAYER);
+		return 1;
+	// end local player variables
+	} else if (fastcmp(word,"server")) {
+		if ((!multiplayer || !netgame) && !playeringame[serverplayer])
+			return 0;
+		LUA_PushUserdata(L, &players[serverplayer], META_PLAYER);
+		return 1;
+	} else if (fastcmp(word,"admin")) { // BACKWARDS COMPATIBILITY HACK: This was replaced with IsPlayerAdmin(), but some 2.1 Lua scripts still use the admin variable. It now points to the first admin player in the array.
+		LUA_Deprecated(L, "admin", "IsPlayerAdmin(player)");
+		if (!playeringame[adminplayers[0]] || IsPlayerAdmin(serverplayer))
+			return 0;
+		LUA_PushUserdata(L, &players[adminplayers[0]], META_PLAYER);
+		return 1;
+	} else if (fastcmp(word,"emeralds")) {
+		lua_pushinteger(L, emeralds);
+		return 1;
+	} else if (fastcmp(word,"gravity")) {
+		lua_pushinteger(L, gravity);
+		return 1;
+	} else if (fastcmp(word,"VERSIONSTRING")) {
+		lua_pushstring(L, VERSIONSTRING);
+		return 1;
+	} else if (fastcmp(word, "token")) {
+		lua_pushinteger(L, token);
+		return 1;
+	}
+	return 0;
+}
+
+// See the above.
+int LUA_CheckGlobals(lua_State *L, const char *word)
+{
+	if (fastcmp(word, "redscore"))
+		redscore = (UINT32)luaL_checkinteger(L, 2);
+	else if (fastcmp(word, "bluescore"))
+		bluescore = (UINT32)luaL_checkinteger(L, 2);
+	else
+		return 0;
+
+	// Global variable set, so return and don't error.
+	return 1;
+}
+
 // This function decides which global variables you are allowed to set.
-static int noglobals(lua_State *L)
+static int setglobals(lua_State *L)
 {
 	const char *csname;
 	char *name;
@@ -106,6 +337,9 @@ static int noglobals(lua_State *L)
 		return 0;
 	}
 
+	if (LUA_CheckGlobals(L, csname))
+		return 0;
+
 	Z_Free(name);
 	return luaL_error(L, "Implicit global " LUA_QS " prevented. Create a local variable instead.", csname);
 }
@@ -144,7 +378,7 @@ static void LUA_ClearState(void)
 
 	// lock the global namespace
 	lua_getmetatable(L, LUA_GLOBALSINDEX);
-		lua_pushcfunction(L, noglobals);
+		lua_pushcfunction(L, setglobals);
 		lua_setfield(L, -2, "__newindex");
 		lua_newtable(L);
 		lua_setfield(L, -2, "__metatable");
diff --git a/src/lua_script.h b/src/lua_script.h
index fcbca29379e0acd50aeeab20250696ee16a3233d..8f27dcb4c9b5343095705b9901b7201fc8b854dd 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -54,6 +54,8 @@ void LUA_InvalidatePlayer(player_t *player);
 void LUA_Step(void);
 void LUA_Archive(void);
 void LUA_UnArchive(void);
+int LUA_PushGlobals(lua_State *L, const char *word);
+int LUA_CheckGlobals(lua_State *L, const char *word);
 void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
 void LUA_CVarChanged(const char *name); // lua_consolelib.c
 int Lua_optoption(lua_State *L, int narg,
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 7f68905aa1ec748aa700fb7ced2441d0538d78d1..a4ab075b406aba49de0d39c832df7571b0860812 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2018 by Sonic Team Junior.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/lua_thinkerlib.c b/src/lua_thinkerlib.c
index 877294898e1483541b12d093adf70b9accb9cd35..1462b26efea499a95a4d822023c35e7739f79247 100644
--- a/src/lua_thinkerlib.c
+++ b/src/lua_thinkerlib.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_aatree.c b/src/m_aatree.c
index f19fa1dc04dd37b3b26045d9c954e9572ba408eb..efa92cbe04c60fcc5e2cba3c780f531cf46e0d37 100644
--- a/src/m_aatree.c
+++ b/src/m_aatree.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_aatree.h b/src/m_aatree.h
index e7f1eba0505d72b342b250e324e45bd29be11716..af95f91b8ea0c4c762dd5ac1c047f3ff591924da 100644
--- a/src/m_aatree.h
+++ b/src/m_aatree.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_anigif.c b/src/m_anigif.c
index 901927fa1bd2059660ac18f87451994347842a6a..f761db143b0cb016ee0395e9f045c8cdeb49d444 100644
--- a/src/m_anigif.c
+++ b/src/m_anigif.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 2013-2016 by Matthew "Inuyasha" Walsh.
 // Copyright (C) 2013      by "Ninji".
-// Copyright (C) 2013-2018 by Sonic Team Junior.
+// Copyright (C) 2013-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -20,6 +20,10 @@
 #include "i_video.h"
 #include "m_misc.h"
 
+#ifdef HWRENDER
+#include "hardware/hw_main.h"
+#endif
+
 // GIFs are always little-endian
 #include "byteptr.h"
 
@@ -29,6 +33,7 @@ consvar_t cv_gif_downscale =  {"gif_downscale", "On", CV_SAVE, CV_OnOff, NULL, 0
 #ifdef HAVE_ANIGIF
 static boolean gif_optimize = false; // So nobody can do something dumb
 static boolean gif_downscale = false; // like changing cvars mid output
+static RGBA_t *gif_palette = NULL;
 
 static FILE *gif_out = NULL;
 static INT32 gif_frames = 0;
@@ -428,10 +433,7 @@ static void GIF_headwrite(void)
 
 	// write color table
 	{
-		RGBA_t *pal = ((cv_screenshot_colorprofile.value)
-		? pLocalPalette
-		: pMasterPalette);
-
+		RGBA_t *pal = gif_palette;
 		for (i = 0; i < 256; i++)
 		{
 			WRITEUINT8(p, pal[i].s.red);
@@ -457,6 +459,32 @@ const UINT8 gifframe_gchead[4] = {0x21,0xF9,0x04,0x04}; // GCE, bytes, packed by
 static UINT8 *gifframe_data = NULL;
 static size_t gifframe_size = 8192;
 
+#ifdef HWRENDER
+static void hwrconvert(void)
+{
+	UINT8 *linear = HWR_GetScreenshot();
+	UINT8 *dest = screens[2];
+	UINT8 r, g, b;
+	INT32 x, y;
+	size_t i = 0;
+
+	InitColorLUT(gif_palette);
+
+	for (y = 0; y < vid.height; y++)
+	{
+		for (x = 0; x < vid.width; x++, i += 3)
+		{
+			r = (UINT8)linear[i];
+			g = (UINT8)linear[i + 1];
+			b = (UINT8)linear[i + 2];
+			dest[(y * vid.width) + x] = colorlookup[r >> SHIFTCOLORBITS][g >> SHIFTCOLORBITS][b >> SHIFTCOLORBITS];
+		}
+	}
+
+	free(linear);
+}
+#endif
+
 //
 // GIF_framewrite
 // writes a frame into the file.
@@ -482,7 +510,12 @@ static void GIF_framewrite(void)
 		GIF_optimizeregion(cur_screen, movie_screen, &blitx, &blity, &blitw, &blith);
 
 		// blit to temp screen
-		I_ReadScreen(movie_screen);
+		if (rendermode == render_soft)
+			I_ReadScreen(movie_screen);
+#ifdef HWRENDER
+		else if (rendermode == render_opengl)
+			hwrconvert();
+#endif
 	}
 	else
 	{
@@ -491,7 +524,18 @@ static void GIF_framewrite(void)
 		blith = vid.height;
 
 		if (gif_frames == 0)
-			I_ReadScreen(movie_screen);
+		{
+			if (rendermode == render_soft)
+				I_ReadScreen(movie_screen);
+#ifdef HWRENDER
+			else if (rendermode == render_opengl)
+			{
+				hwrconvert();
+				VID_BlitLinearScreen(screens[2], screens[0], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
+			}
+#endif
+		}
+
 		movie_screen = screens[0];
 	}
 
@@ -580,7 +624,7 @@ static void GIF_framewrite(void)
 //
 INT32 GIF_open(const char *filename)
 {
-#ifdef HWRENDER
+#if 0
 	if (rendermode != render_soft)
 	{
 		CONS_Alert(CONS_WARNING, M_GetText("GIFs cannot be taken in non-software modes!\n"));
@@ -594,6 +638,16 @@ INT32 GIF_open(const char *filename)
 
 	gif_optimize = (!!cv_gif_optimize.value);
 	gif_downscale = (!!cv_gif_downscale.value);
+
+	// GIF color table
+	// In hardware mode, uses the master palette
+	gif_palette = ((cv_screenshot_colorprofile.value
+#ifdef HWRENDER
+	&& (rendermode == render_soft)
+#endif
+	) ? pLocalPalette
+	: pMasterPalette);
+
 	GIF_headwrite();
 	gif_frames = 0;
 	return 1;
diff --git a/src/m_anigif.h b/src/m_anigif.h
index 6d94ecf8ceece4ab6ca8a04caca15a0a1c10e29e..592d2cf19a8d2f1aceb8f26336e272fdbbfa5c87 100644
--- a/src/m_anigif.h
+++ b/src/m_anigif.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2013-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 2013-2018 by Sonic Team Junior.
+// Copyright (C) 2013-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_argv.c b/src/m_argv.c
index bb43ad57cef30eee074bfc7db6111300eb6afc20..935f4b9e3f2786b4a369af8b9b06a949c0961036 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_argv.h b/src/m_argv.h
index d654dcc195778eddb6907adfeed55aac2116e100..57c3f9d50deee6e88df919c19bde29fe06ce5c34 100644
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_bbox.c b/src/m_bbox.c
index 0c57de43e45f9315f56d50a022d0c22ad0282163..5af1b4ef8c632aa566075f12982afc9642eb6b9d 100644
--- a/src/m_bbox.c
+++ b/src/m_bbox.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_bbox.h b/src/m_bbox.h
index db5c2b4a7b3e815a34280216216a2608d6eb6f8a..dce3cf15a8faf5101b9968689a5c550a6eadc4cb 100644
--- a/src/m_bbox.h
+++ b/src/m_bbox.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 4371f9e5b2264d4efa109e58f25b3c7b4bf5751b..58bb2962c7ae60f966146c583297bb8cb242ad30 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -517,6 +517,7 @@ void Command_Teleport_f(void)
 		if (!starpostnum) // spawnpoints...
 		{
 			mapthing_t *mt;
+			fixed_t offset;
 
 			if (starpostpath >= numcoopstarts)
 			{
@@ -527,6 +528,7 @@ void Command_Teleport_f(void)
 			mt = playerstarts[starpostpath]; // Given above check, should never be NULL.
 			intx = mt->x<<FRACBITS;
 			inty = mt->y<<FRACBITS;
+			offset = mt->z<<FRACBITS;
 
 			ss = R_IsPointInSubsector(intx, inty);
 			if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
@@ -538,17 +540,9 @@ void Command_Teleport_f(void)
 			// Flagging a player's ambush will make them start on the ceiling
 			// Objectflip inverts
 			if (!!(mt->options & MTF_AMBUSH) ^ !!(mt->options & MTF_OBJECTFLIP))
-			{
-				intz = ss->sector->ceilingheight - p->mo->height;
-				if (mt->options >> ZSHIFT)
-					intz -= ((mt->options >> ZSHIFT) << FRACBITS);
-			}
+				intz = ss->sector->ceilingheight - p->mo->height - offset;
 			else
-			{
-				intz = ss->sector->floorheight;
-				if (mt->options >> ZSHIFT)
-					intz += ((mt->options >> ZSHIFT) << FRACBITS);
-			}
+				intz = ss->sector->floorheight + offset;
 
 			if (mt->options & MTF_OBJECTFLIP) // flip the player!
 			{
@@ -778,7 +772,7 @@ void Command_CauseCfail_f(void)
 	P_SetThingPosition(players[consoleplayer].mo);
 
 	// CTF consistency test
-	if (gametype == GT_CTF)
+	if (gametyperules & GTR_TEAMFLAGS)
 	{
 		if (blueflag) {
 			P_RemoveMobj(blueflag);
@@ -1106,7 +1100,7 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c
 #else
 		fixed_t cheight = sec->ceilingheight;
 #endif
-		mt->options = (UINT16)((cheight - player->mo->z - player->mo->height)>>FRACBITS);
+		mt->z = (UINT16)((cheight - player->mo->z - player->mo->height)>>FRACBITS);
 	}
 	else
 	{
@@ -1115,12 +1109,11 @@ static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean c
 #else
 		fixed_t fheight = sec->floorheight;
 #endif
-		mt->options = (UINT16)((player->mo->z - fheight)>>FRACBITS);
+		mt->z = (UINT16)((player->mo->z - fheight)>>FRACBITS);
 	}
-	mt->options <<= ZSHIFT;
 	mt->angle = (INT16)(FixedInt(AngleFixed(player->mo->angle)));
 
-	mt->options |= (UINT16)cv_opflags.value;
+	mt->options = (mt->z << ZSHIFT) | (UINT16)cv_opflags.value;
 	return mt;
 }
 
@@ -1187,21 +1180,21 @@ void OP_NightsObjectplace(player_t *player)
 		mt->options = (mt->options & ~(UINT16)cv_opflags.value) | (UINT16)cv_ophoopflags.value;
 		mt->angle = (INT16)(mt->angle+(INT16)((FixedInt(FixedDiv(temp*FRACUNIT, 360*(FRACUNIT/256))))<<8));
 
-		P_SpawnHoopsAndRings(mt, false);
+		P_SpawnHoop(mt);
 	}
 
 	// This places a bumper!
 	if (cmd->buttons & BT_TOSSFLAG)
 	{
 		UINT16 vertangle = (UINT16)(player->anotherflyangle % 360);
-		UINT16 newflags, newz;
+		UINT16 newflags;
 
 		player->pflags |= PF_ATTACKDOWN;
 		if (!OP_HeightOkay(player, false))
 			return;
 
 		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_NIGHTSBUMPER].doomednum, false);
-		newz = min((mt->options >> ZSHIFT) - (mobjinfo[MT_NIGHTSBUMPER].height/4), 0);
+		mt->z = min(mt->z - (mobjinfo[MT_NIGHTSBUMPER].height/4), 0);
 			// height offset: from P_TouchSpecialThing case MT_NIGHTSBUMPER
 
 		// clockwise
@@ -1232,7 +1225,7 @@ void OP_NightsObjectplace(player_t *player)
 		else // forward
 			newflags = 0;
 
-		mt->options = (newz << ZSHIFT) | newflags;
+		mt->options = (mt->z << ZSHIFT) | newflags;
 
 		// if NiGHTS is facing backwards, orient the Thing angle forwards so that the sprite angle
 		// displays correctly. Backwards movement via the Thing flags is unaffected.
@@ -1250,7 +1243,7 @@ void OP_NightsObjectplace(player_t *player)
 			return;
 
 		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_BLUESPHERE].doomednum, false);
-		P_SpawnHoopsAndRings(mt, false);
+		P_SpawnMapThing(mt);
 	}
 
 	// This places a ring!
@@ -1261,7 +1254,7 @@ void OP_NightsObjectplace(player_t *player)
 			return;
 
 		mt = OP_CreateNewMapThing(player, (UINT16)mobjinfo[MT_RING].doomednum, false);
-		P_SpawnHoopsAndRings(mt, false);
+		P_SpawnMapThing(mt);
 	}
 
 	// This places a custom object as defined in the console cv_mapthingnum.
@@ -1293,15 +1286,10 @@ void OP_NightsObjectplace(player_t *player)
 		mt = OP_CreateNewMapThing(player, (UINT16)cv_mapthingnum.value, false);
 		mt->angle = angle;
 
-		if (mt->type == 300 // Ring
-		|| mt->type == 308 || mt->type == 309 // Team Rings
-		|| mt->type == 1706 // Sphere
-		|| (mt->type >= 600 && mt->type <= 609) // Placement patterns
-		|| mt->type == 1705 || mt->type == 1713 // NiGHTS Hoops
-		|| mt->type == 1800) // Mario Coin
-		{
-			P_SpawnHoopsAndRings(mt, false);
-		}
+		if (mt->type >= 600 && mt->type <= 609) // Placement patterns
+			P_SpawnItemPattern(mt, false);
+		else if (mt->type == 1705 || mt->type == 1713) // NiGHTS Hoops
+			P_SpawnHoop(mt);
 		else
 			P_SpawnMapThing(mt);
 	}
@@ -1438,19 +1426,14 @@ void OP_ObjectplaceMovement(player_t *player)
 			return;
 
 		mt = OP_CreateNewMapThing(player, (UINT16)spawnthing, ceiling);
-		if (mt->type == 300 // Ring
-		|| mt->type == 308 || mt->type == 309 // Team Rings
-		|| mt->type == 1706 // Nights Wing
-		|| (mt->type >= 600 && mt->type <= 609) // Placement patterns
-		|| mt->type == 1705 || mt->type == 1713 // NiGHTS Hoops
-		|| mt->type == 1800) // Mario Coin
-		{
-			P_SpawnHoopsAndRings(mt, false);
-		}
+		if (mt->type >= 600 && mt->type <= 609) // Placement patterns
+			P_SpawnItemPattern(mt, false);
+		else if (mt->type == 1705 || mt->type == 1713) // NiGHTS Hoops
+			P_SpawnHoop(mt);
 		else
 			P_SpawnMapThing(mt);
 
-		CONS_Printf(M_GetText("Placed object type %d at %d, %d, %d, %d\n"), mt->type, mt->x, mt->y, mt->options>>ZSHIFT, mt->angle);
+		CONS_Printf(M_GetText("Placed object type %d at %d, %d, %d, %d\n"), mt->type, mt->x, mt->y, mt->z, mt->angle);
 	}
 }
 
diff --git a/src/m_cheat.h b/src/m_cheat.h
index f5d59120b5ef825d8f2b8aed6310ad69db1abda4..3c4403e7ff1efcc34d40a5d2bd3a3a027c1a15c4 100644
--- a/src/m_cheat.h
+++ b/src/m_cheat.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_cond.c b/src/m_cond.c
index b7520aba78e1be64ef810e46bfbb58c3d593b155..1e761fdb4a5134d0f8102e02083725a673b26c0e 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -284,6 +284,8 @@ void M_SilentUpdateUnlockablesAndEmblems(void)
 			continue;
 		unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
 	}
+
+	players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
 }
 
 // Emblem unlocking shit
diff --git a/src/m_cond.h b/src/m_cond.h
index f6d011a8f1b9a54f7a305c4312efd6268599b9c0..2d08579b39b8c7e9a267b3adb8bf97924b77baca 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 2012-2018 by Sonic Team Junior.
+// Copyright (C) 2012-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_dllist.h b/src/m_dllist.h
index 18f3511442afde28f2272977a526e59864462b65..8f8d4c1fb9b498395248b535a04884d4348589c8 100644
--- a/src/m_dllist.h
+++ b/src/m_dllist.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2005      by James Haley
-// Copyright (C) 2005-2018 by Sonic Team Junior.
+// Copyright (C) 2005-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_fixed.c b/src/m_fixed.c
index 5e78967396a4b9802dc29e301a14112f03b3168a..6be54c4865e3dd7100b48b3763eaf53c8d9599cb 100644
--- a/src/m_fixed.c
+++ b/src/m_fixed.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 08bc958841cba10fa9d7b2db2f11d98d8cb0f5bc..02d4c73ff57ee33e802d1d8ccc31291888893335 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_menu.c b/src/m_menu.c
index 303ba3453cebfdad6f8e496f8588c00e23933ee0..0769edae6cc3b7fc1f9b3806732b64e33c0c3e6b 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2011-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -309,7 +309,8 @@ static void M_ChangeControl(INT32 choice);
 // Video & Sound
 menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef;
 #ifdef HWRENDER
-menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef, OP_OpenGLColorDef;
+static void M_OpenGLOptionsMenu(void);
+menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef;
 #endif
 menu_t OP_SoundOptionsDef;
 menu_t OP_SoundAdvancedDef;
@@ -355,7 +356,6 @@ static void M_DrawScreenshotMenu(void);
 static void M_DrawMonitorToggles(void);
 #ifdef HWRENDER
 static void M_OGL_DrawFogMenu(void);
-static void M_OGL_DrawColorMenu(void);
 #endif
 #ifndef NONET
 static void M_DrawScreenshotMenu(void);
@@ -389,6 +389,9 @@ static void M_ResetCvars(void);
 
 // Consvar onchange functions
 static void Newgametype_OnChange(void);
+#ifdef HWRENDER
+static void Newrenderer_OnChange(void);
+#endif
 static void Dummymares_OnChange(void);
 
 // ==========================================================================
@@ -413,6 +416,11 @@ CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1];
 
 consvar_t cv_newgametype = {"newgametype", "Co-op", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
+#ifdef HWRENDER
+consvar_t cv_newrenderer = {"newrenderer", "Software", CV_HIDEN|CV_CALL, cv_renderer_t, Newrenderer_OnChange, 0, NULL, NULL, 0, 0, NULL};
+static int newrenderer_set = 1;/* Software doesn't need confirmation! */
+#endif
+
 static CV_PossibleValue_t serversort_cons_t[] = {
 	{0,"Ping"},
 	{1,"Modified State"},
@@ -606,7 +614,7 @@ static menuitem_t MISC_ChangeTeamMenu[] =
 	{IT_WHITESTRING|IT_CALL,         NULL, "Confirm",           M_ConfirmTeamChange,    90},
 };
 
-static const gtdesc_t gametypedesc[] =
+gtdesc_t gametypedesc[NUMGAMETYPES] =
 {
 	{{ 54,  54}, "Play through the single-player campaign with your friends, teaming up to beat Dr Eggman's nefarious challenges!"},
 	{{103, 103}, "Speed your way through the main acts, competing in several different categories to see who's the best."},
@@ -859,7 +867,7 @@ static menuitem_t SP_NightsAttackLevelSelectMenu[] =
 // Single Player Nights Attack
 static menuitem_t SP_NightsAttackMenu[] =
 {
-	{IT_STRING|IT_CALL,        NULL, "Level Select...",  &M_NightsAttackLevelSelect,  52},
+	{IT_STRING|IT_KEYHANDLER,        NULL, "Level Select...",  &M_HandleTimeAttackLevelSelect,  52},
 	{IT_STRING|IT_CVAR,        NULL, "Show Records For", &cv_dummymares,              62},
 
 	{IT_DISABLED,              NULL, "Guest Option...",  &SP_NightsGuestReplayDef,    100},
@@ -1119,6 +1127,8 @@ static menuitem_t OP_Joystick1Menu[] =
 
 	{IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook, 120},
 	{IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook,  130},
+	{IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER,
+	                      NULL, "Deadzone",               &cv_deadzone,       140 },
 };
 
 static menuitem_t OP_Joystick2Menu[] =
@@ -1135,6 +1145,8 @@ static menuitem_t OP_Joystick2Menu[] =
 
 	{IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook2,120},
 	{IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130},
+	{IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER,
+	                      NULL, "Deadzone",               &cv_deadzone2,      140 },
 };
 
 static menuitem_t OP_JoystickSetMenu[1+MAX_JOYSTICKS];
@@ -1174,11 +1186,12 @@ static menuitem_t OP_CameraOptionsMenu[] =
 	{IT_STRING  | IT_CVAR, NULL, "Third-person Camera"  , &cv_chasecam , 10},
 	{IT_STRING  | IT_CVAR, NULL, "Flip Camera with Gravity"  , &cv_flipcam , 20},
 	{IT_STRING  | IT_CVAR, NULL, "Orbital Looking"  , &cv_cam_orbit , 30},
-	{IT_STRING	|	IT_CVAR, NULL, "Downhill Slope Adjustment", &cv_cam_adjust, 40},
+	{IT_STRING  | IT_CVAR, NULL, "Downhill Slope Adjustment", &cv_cam_adjust, 40},
 
-	{IT_STRING	|	IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Distance", &cv_cam_dist, 60},
-	{IT_STRING	| IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Height", &cv_cam_height, 70},
-	{IT_STRING	|	IT_CVAR	| IT_CV_FLOATSLIDER, NULL, "Camera Speed", &cv_cam_speed, 80},
+	{IT_STRING  | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Distance", &cv_cam_dist, 60},
+	{IT_STRING  | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Height", &cv_cam_height, 70},
+	{IT_STRING  | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Camera Spacial Speed", &cv_cam_speed, 80},
+	{IT_STRING  | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Camera Turning Speed", &cv_cam_turnmultiplier, 90},
 
 	{IT_STRING  | IT_CVAR, NULL, "Crosshair", &cv_crosshair, 100},
 };
@@ -1188,11 +1201,12 @@ static menuitem_t OP_Camera2OptionsMenu[] =
 	{IT_STRING  | IT_CVAR, NULL, "Third-person Camera"  , &cv_chasecam2 , 10},
 	{IT_STRING  | IT_CVAR, NULL, "Flip Camera with Gravity"  , &cv_flipcam2 , 20},
 	{IT_STRING  | IT_CVAR, NULL, "Orbital Looking"  , &cv_cam2_orbit , 30},
-	{IT_STRING	|	IT_CVAR, NULL, "Downhill Slope Adjustment", &cv_cam2_adjust, 40},
+	{IT_STRING  | IT_CVAR, NULL, "Downhill Slope Adjustment", &cv_cam2_adjust, 40},
 
-	{IT_STRING	|	IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Distance", &cv_cam2_dist, 60},
-	{IT_STRING	| IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Height", &cv_cam2_height, 70},
-	{IT_STRING	|	IT_CVAR	| IT_CV_FLOATSLIDER, NULL, "Camera Speed", &cv_cam2_speed, 80},
+	{IT_STRING  | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Distance", &cv_cam2_dist, 60},
+	{IT_STRING  | IT_CVAR | IT_CV_INTEGERSTEP, NULL, "Camera Height", &cv_cam2_height, 70},
+	{IT_STRING  | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Camera Spacial Speed", &cv_cam2_speed, 80},
+	{IT_STRING  | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Camera Turning Speed", &cv_cam2_turnmultiplier, 90},
 
 	{IT_STRING  | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 100},
 };
@@ -1206,9 +1220,10 @@ static menuitem_t OP_VideoOptionsMenu[] =
 	{IT_STRING|IT_CVAR,      NULL, "Fullscreen",             &cv_fullscreen,         11},
 #endif
 	{IT_STRING | IT_CVAR, NULL, "Vertical Sync",                &cv_vidwait,         16},
-
 #ifdef HWRENDER
-	{IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef,          21},
+	{IT_STRING | IT_CVAR, NULL, "Renderer",                     &cv_newrenderer,        21},
+#else
+	{IT_TRANSTEXT | IT_PAIR, "Renderer", "Software",            &cv_renderer,           21},
 #endif
 
 	{IT_HEADER, NULL, "Color Profile", NULL, 30},
@@ -1249,6 +1264,11 @@ static menuitem_t OP_VideoOptionsMenu[] =
 	{IT_STRING | IT_CVAR, NULL, "Show FPS",                  &cv_ticrate,         190},
 	{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw",       &cv_homremoval,      195},
 	{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"",       &cv_showfocuslost,   200},
+
+#ifdef HWRENDER
+	{IT_HEADER, NULL, "Renderer", NULL, 208},
+	{IT_CALL | IT_STRING, NULL, "OpenGL Options...",         M_OpenGLOptionsMenu, 214},
+#endif
 };
 
 static menuitem_t OP_VideoModeMenu[] =
@@ -1300,21 +1320,25 @@ static menuitem_t OP_ColorOptionsMenu[] =
 #ifdef HWRENDER
 static menuitem_t OP_OpenGLOptionsMenu[] =
 {
-	{IT_STRING|IT_CVAR,         NULL, "Models",              &cv_grmodels,             10},
-	{IT_STRING|IT_CVAR,         NULL, "Model interpolation", &cv_grmodelinterpolation, 20},
+	{IT_HEADER, NULL, "3D Models", NULL, 0},
+	{IT_STRING|IT_CVAR,         NULL, "Models",              &cv_grmodels,             12},
+	{IT_STRING|IT_CVAR,         NULL, "Model interpolation", &cv_grmodelinterpolation, 22},
+	{IT_STRING|IT_CVAR,         NULL, "Model lighting",      &cv_grmodellighting, 32},
 
-	{IT_STRING|IT_CVAR,         NULL, "Field of view",   &cv_grfov,            40},
-	{IT_STRING|IT_CVAR,         NULL, "Quality",         &cv_scr_depth,        50},
-	{IT_STRING|IT_CVAR,         NULL, "Texture Filter",  &cv_grfiltermode,     60},
-	{IT_STRING|IT_CVAR,         NULL, "Anisotropic",     &cv_granisotropicmode,70},
-#if defined (_WINDOWS) && (!((defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)))
-	{IT_STRING|IT_CVAR,         NULL, "Fullscreen",      &cv_fullscreen,       80},
-#endif
+	{IT_HEADER, NULL, "General", NULL, 51},
+	{IT_STRING|IT_CVAR,         NULL, "Field of view",   &cv_grfov,            63},
+	{IT_STRING|IT_CVAR,         NULL, "Quality",         &cv_scr_depth,        73},
+	{IT_STRING|IT_CVAR,         NULL, "Texture Filter",  &cv_grfiltermode,     83},
+	{IT_STRING|IT_CVAR,         NULL, "Anisotropic",     &cv_granisotropicmode,93},
+
+	{IT_HEADER, NULL, "Miscellaneous", NULL, 112},
+	{IT_SUBMENU|IT_STRING,      NULL, "Fog...",          &OP_OpenGLFogDef,          124},
 #ifdef ALAM_LIGHTING
-	{IT_SUBMENU|IT_STRING,      NULL, "Lighting...",     &OP_OpenGLLightingDef,     100},
+	{IT_SUBMENU|IT_STRING,      NULL, "Lighting...",     &OP_OpenGLLightingDef,     134},
+#endif
+#if defined (_WINDOWS) && (!((defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)))
+	{IT_STRING|IT_CVAR,         NULL, "Fullscreen",      &cv_fullscreen,            144},
 #endif
-	{IT_SUBMENU|IT_STRING,      NULL, "Fog...",          &OP_OpenGLFogDef,          110},
-	{IT_SUBMENU|IT_STRING,      NULL, "Gamma...",        &OP_OpenGLColorDef,        120},
 };
 
 #ifdef ALAM_LIGHTING
@@ -1334,13 +1358,6 @@ static menuitem_t OP_OpenGLFogMenu[] =
 	{IT_STRING|IT_CVAR,       NULL, "Fog density", &cv_grfogdensity, 30},
 	{IT_STRING|IT_CVAR,       NULL, "Software Fog",&cv_grsoftwarefog,40},
 };
-
-static menuitem_t OP_OpenGLColorMenu[] =
-{
-	{IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "red",   &cv_grgammared,   10},
-	{IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "green", &cv_grgammagreen, 20},
-	{IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "blue",  &cv_grgammablue,  30},
-};
 #endif
 
 static menuitem_t OP_SoundOptionsMenu[] =
@@ -2007,6 +2024,14 @@ menu_t OP_MonitorToggleDef =
 };
 
 #ifdef HWRENDER
+static void M_OpenGLOptionsMenu(void)
+{
+	if (rendermode == render_opengl)
+		M_SetupNextMenu(&OP_OpenGLOptionsDef);
+	else
+		M_StartMessage(M_GetText("You must be in OpenGL mode\nto access this menu.\n\n(Press a key)\n"), NULL, MM_NOTHING);
+}
+
 menu_t OP_OpenGLOptionsDef = DEFAULTMENUSTYLE(
 	MN_OP_MAIN + (MN_OP_VIDEO << 6) + (MN_OP_OPENGL << 12),
 	"M_VIDEO", OP_OpenGLOptionsMenu, &OP_VideoOptionsDef, 30, 30);
@@ -2027,18 +2052,6 @@ menu_t OP_OpenGLFogDef =
 	0,
 	NULL
 };
-menu_t OP_OpenGLColorDef =
-{
-	MN_OP_MAIN + (MN_OP_VIDEO << 6) + (MN_OP_OPENGL << 12) + (MN_OP_OPENGL_COLOR << 18),
-	"M_VIDEO",
-	sizeof (OP_OpenGLColorMenu)/sizeof (menuitem_t),
-	&OP_OpenGLOptionsDef,
-	OP_OpenGLColorMenu,
-	M_OGL_DrawColorMenu,
-	60, 40,
-	0,
-	NULL
-};
 #endif
 menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE(
 	MN_OP_MAIN + (MN_OP_DATA << 6),
@@ -2224,6 +2237,46 @@ static void Newgametype_OnChange(void)
 	}
 }
 
+#ifdef HWRENDER
+static void Newrenderer_AREYOUSURE(INT32 c)
+{
+	int n;
+	switch (c)
+	{
+		case 'y':
+		case KEY_ENTER:
+			n = cv_newrenderer.value;
+			newrenderer_set |= n;
+			CV_SetValue(&cv_renderer, n);
+			break;
+		default:
+			CV_StealthSetValue(&cv_newrenderer, cv_renderer.value);
+	}
+}
+
+static void Newrenderer_OnChange(void)
+{
+	/* Well this works for now because there's only two options. */
+	int n;
+	n = cv_newrenderer.value;
+	newrenderer_set |= cv_renderer.value;
+	if (( newrenderer_set & n ))
+		CV_SetValue(&cv_renderer, n);
+	else
+	{
+		M_StartMessage(
+				"The OpenGL renderer is incomplete.\n"
+				"Some visuals may fail to appear, or\n"
+				"appear incorrectly.\n"
+				"Do you still want to switch to it?\n"
+				"\n"
+				"(Press 'y' or 'n')",
+				Newrenderer_AREYOUSURE, MM_YESNO
+		);
+	}
+}
+#endif/*HWRENDER*/
+
 void Screenshot_option_Onchange(void)
 {
 	OP_ScreenshotOptionsMenu[op_screenshot_folder].status =
@@ -2824,6 +2877,7 @@ static void M_GoBack(INT32 choice)
 
 			menuactive = false;
 			wipetypepre = menupres[M_GetYoungestChildMenu()].exitwipe;
+			I_UpdateMouseGrab();
 			D_StartTitle();
 		}
 		else
@@ -2930,7 +2984,7 @@ static void M_NextOpt(void)
 			itemOn = 0;
 		else
 			itemOn++;
-	} while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE);
+	} while (oldItemOn != itemOn && ( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE ));
 }
 
 static void M_PrevOpt(void)
@@ -2942,7 +2996,7 @@ static void M_PrevOpt(void)
 			itemOn = currentMenu->numitems - 1;
 		else
 			itemOn--;
-	} while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE);
+	} while (oldItemOn != itemOn && ( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE ));
 }
 
 // lock out further input in a tic when important buttons are pressed
@@ -3025,7 +3079,7 @@ boolean M_Responder(event_t *ev)
 		}
 		else if (ev->type == ev_joystick  && ev->data1 == 0 && joywait < I_GetTime())
 		{
-			const INT32 jdeadzone = JOYAXISRANGE/4;
+			const INT32 jdeadzone = (JOYAXISRANGE * cv_deadzone.value) / FRACUNIT;
 			if (ev->data3 != INT32_MAX)
 			{
 				if (Joystick.bGamepadStyle || abs(ev->data3) > jdeadzone)
@@ -3168,10 +3222,7 @@ boolean M_Responder(event_t *ev)
 
 			case KEY_ESCAPE: // Pop up menu
 				if (chat_on)
-				{
 					HU_clearChatChars();
-					chat_on = false;
-				}
 				else
 					M_StartControlPanel();
 				return true;
@@ -3549,6 +3600,8 @@ void M_ClearMenus(boolean callexitmenufunc)
 		currentMenu = &MainDef; // Not like it matters
 	menuactive = false;
 	hidetitlemap = false;
+
+	I_UpdateMouseGrab();
 }
 
 //
@@ -3576,11 +3629,11 @@ void M_SetupNextMenu(menu_t *menudef)
 
 	// the curent item can be disabled,
 	// this code go up until an enabled item found
-	if ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE)
+	if (( (currentMenu->menuitems[itemOn].status & IT_TYPE) & IT_SPACE ))
 	{
 		for (i = 0; i < currentMenu->numitems; i++)
 		{
-			if ((currentMenu->menuitems[i].status & IT_TYPE) != IT_SPACE)
+			if (!( (currentMenu->menuitems[i].status & IT_TYPE) & IT_SPACE ))
 			{
 				itemOn = i;
 				break;
@@ -3664,14 +3717,6 @@ void M_Init(void)
 	quitmsg[QUIT3MSG5] = M_GetText("You'll be back to play soon, though...\n......right?\n\n(Press 'Y' to quit)");
 	quitmsg[QUIT3MSG6] = M_GetText("Aww, is Egg Rock Zone too\ndifficult for you?\n\n(Press 'Y' to quit)");
 
-#ifdef HWRENDER
-	// Permanently hide some options based on render mode
-	if (rendermode == render_soft)
-		OP_VideoOptionsMenu[4].status = IT_DISABLED;
-	else if (rendermode == render_opengl)
-		OP_ScreenshotOptionsMenu[op_screenshot_colorprofile].status = IT_GRAYEDOUT;
-#endif
-
 	/*
 	Well the menu sucks for forcing us to have an item set
 	at all if every item just calls the same function, and
@@ -3748,19 +3793,19 @@ static void M_DrawThermo(INT32 x, INT32 y, consvar_t *cv)
 	centerlump[1] = W_GetNumForName("M_THERMM");
 	cursorlump = W_GetNumForName("M_THERMO");
 
-	V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_CACHE));
+	V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_PATCH));
 	xx += SHORT(p->width) - SHORT(p->leftoffset);
 	for (i = 0; i < 16; i++)
 	{
-		V_DrawScaledPatch(xx, y, V_WRAPX, W_CachePatchNum(centerlump[i & 1], PU_CACHE));
+		V_DrawScaledPatch(xx, y, V_WRAPX, W_CachePatchNum(centerlump[i & 1], PU_PATCH));
 		xx += 8;
 	}
-	V_DrawScaledPatch(xx, y, 0, W_CachePatchNum(rightlump, PU_CACHE));
+	V_DrawScaledPatch(xx, y, 0, W_CachePatchNum(rightlump, PU_PATCH));
 
 	xx = (cv->value - cv->PossibleValue[0].value) * (15*8) /
 		(cv->PossibleValue[1].value - cv->PossibleValue[0].value);
 
-	V_DrawScaledPatch((x + 8) + xx, y, 0, W_CachePatchNum(cursorlump, PU_CACHE));
+	V_DrawScaledPatch((x + 8) + xx, y, 0, W_CachePatchNum(cursorlump, PU_PATCH));
 }
 
 //  A smaller 'Thermo', with range given as percents (0-100)
@@ -3772,9 +3817,9 @@ static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop)
 
 	x = BASEVIDWIDTH - x - SLIDER_WIDTH;
 
-	V_DrawScaledPatch(x, y, 0, W_CachePatchName("M_SLIDEL", PU_CACHE));
+	V_DrawScaledPatch(x, y, 0, W_CachePatchName("M_SLIDEL", PU_PATCH));
 
-	p =  W_CachePatchName("M_SLIDEM", PU_CACHE);
+	p =  W_CachePatchName("M_SLIDEM", PU_PATCH);
 	for (i = 1; i < SLIDER_RANGE; i++)
 		V_DrawScaledPatch (x+i*8, y, 0,p);
 
@@ -3786,11 +3831,11 @@ static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop)
 			'\x1D' | V_YELLOWMAP, false);
 	}
 
-	p = W_CachePatchName("M_SLIDER", PU_CACHE);
+	p = W_CachePatchName("M_SLIDER", PU_PATCH);
 	V_DrawScaledPatch(x+i*8, y, 0, p);
 
 	// draw the slider cursor
-	p = W_CachePatchName("M_SLIDEC", PU_CACHE);
+	p = W_CachePatchName("M_SLIDEC", PU_PATCH);
 
 	for (i = 0; cv->PossibleValue[i+1].strvalue; i++);
 
@@ -3843,15 +3888,15 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
 	// draw left side
 	cx = x;
 	cy = y;
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TL], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TL], PU_PATCH));
 	cy += boff;
-	p = W_CachePatchNum(viewborderlump[BRDR_L], PU_CACHE);
+	p = W_CachePatchNum(viewborderlump[BRDR_L], PU_PATCH);
 	for (n = 0; n < boxlines; n++)
 	{
 		V_DrawScaledPatch(cx, cy, V_WRAPY, p);
 		cy += step;
 	}
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BL], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BL], PU_PATCH));
 
 	// draw middle
 	V_DrawFlatFill(x + boff, y + boff, width*step, boxlines*step, st_borderpatchnum);
@@ -3860,23 +3905,23 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
 	cy = y;
 	while (width > 0)
 	{
-		V_DrawScaledPatch(cx, cy, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_T], PU_CACHE));
-		V_DrawScaledPatch(cx, y + boff + boxlines*step, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_B], PU_CACHE));
+		V_DrawScaledPatch(cx, cy, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_T], PU_PATCH));
+		V_DrawScaledPatch(cx, y + boff + boxlines*step, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_B], PU_PATCH));
 		width--;
 		cx += step;
 	}
 
 	// draw right side
 	cy = y;
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TR], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TR], PU_PATCH));
 	cy += boff;
-	p = W_CachePatchNum(viewborderlump[BRDR_R], PU_CACHE);
+	p = W_CachePatchNum(viewborderlump[BRDR_R], PU_PATCH);
 	for (n = 0; n < boxlines; n++)
 	{
 		V_DrawScaledPatch(cx, cy, V_WRAPY, p);
 		cy += step;
 	}
-	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BR], PU_CACHE));
+	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BR], PU_PATCH));
 */
 }
 
@@ -3887,7 +3932,7 @@ static void M_DrawStaticBox(fixed_t x, fixed_t y, INT32 flags, fixed_t w, fixed_
 	patch_t *patch;
 	fixed_t sw, pw;
 
-	patch = W_CachePatchName("LSSTATIC", PU_CACHE);
+	patch = W_CachePatchName("LSSTATIC", PU_PATCH);
 	pw = SHORT(patch->width) - (sw = w*2); //FixedDiv(w, scale); -- for scale FRACUNIT/2
 
 	/*if (pw > 0) -- model code for modders providing weird LSSTATIC
@@ -3916,15 +3961,15 @@ static void M_DrawSaveLoadBorder(INT32 x,INT32 y)
 {
 	INT32 i;
 
-	V_DrawScaledPatch (x-8,y+7,0,W_CachePatchName("M_LSLEFT",PU_CACHE));
+	V_DrawScaledPatch (x-8,y+7,0,W_CachePatchName("M_LSLEFT",PU_PATCH));
 
 	for (i = 0;i < 24;i++)
 	{
-		V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSCNTR",PU_CACHE));
+		V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSCNTR",PU_PATCH));
 		x += 8;
 	}
 
-	V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSRGHT",PU_CACHE));
+	V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSRGHT",PU_PATCH));
 }
 #endif
 
@@ -3967,10 +4012,10 @@ static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y)
 		lasttype = curtype;
 
 		if (emblem->collected)
-			V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_CACHE),
+			V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
 			                       R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
 		else
-			V_DrawSmallScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_CACHE));
+			V_DrawSmallScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_PATCH));
 
 		emblem = M_GetLevelEmblems(-1);
 		x -= 12;
@@ -3981,7 +4026,7 @@ static void M_DrawMenuTitle(void)
 {
 	if (currentMenu->menutitlepic)
 	{
-		patch_t *p = W_CachePatchName(currentMenu->menutitlepic, PU_CACHE);
+		patch_t *p = W_CachePatchName(currentMenu->menutitlepic, PU_PATCH);
 
 		if (p->height > 24) // title is larger than normal
 		{
@@ -4033,13 +4078,13 @@ static void M_DrawGenericMenu(void)
 					if (currentMenu->menuitems[i].status & IT_CENTER)
 					{
 						patch_t *p;
-						p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
+						p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_PATCH);
 						V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p);
 					}
 					else
 					{
 						V_DrawScaledPatch(x, y, 0,
-							W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE));
+							W_CachePatchName(currentMenu->menuitems[i].patch, PU_PATCH));
 					}
 				}
 				/* FALLTHRU */
@@ -4108,7 +4153,7 @@ static void M_DrawGenericMenu(void)
 			case IT_GRAYPATCH:
 				if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0])
 					V_DrawMappedPatch(x, y, 0,
-						W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap);
+						W_CachePatchName(currentMenu->menuitems[i].patch,PU_PATCH), graymap);
 				y += LINEHEIGHT;
 				break;
 			case IT_TRANSTEXT:
@@ -4142,12 +4187,12 @@ static void M_DrawGenericMenu(void)
 		|| ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_NOTHING))
 	{
 		V_DrawScaledPatch(currentMenu->x + SKULLXOFF, cursory - 5, 0,
-			W_CachePatchName("M_CURSOR", PU_CACHE));
+			W_CachePatchName("M_CURSOR", PU_PATCH));
 	}
 	else
 	{
 		V_DrawScaledPatch(currentMenu->x - 24, cursory, 0,
-			W_CachePatchName("M_CURSOR", PU_CACHE));
+			W_CachePatchName("M_CURSOR", PU_PATCH));
 		V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text);
 	}
 }
@@ -4271,7 +4316,18 @@ static void M_DrawGenericScrollMenu(void)
 					}
 					break;
 			case IT_TRANSTEXT:
-				V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);
+				switch (currentMenu->menuitems[i].status & IT_TYPE)
+				{
+					case IT_PAIR:
+						V_DrawString(x, y,
+								V_TRANSLUCENT, currentMenu->menuitems[i].patch);
+						V_DrawRightAlignedString(BASEVIDWIDTH - x, y,
+								V_TRANSLUCENT, currentMenu->menuitems[i].text);
+						break;
+					default:
+						V_DrawString(x, y,
+								V_TRANSLUCENT, currentMenu->menuitems[i].text);
+				}
 				break;
 			case IT_QUESTIONMARKS:
 				V_DrawString(x, y, V_TRANSLUCENT|V_OLDSPACING, M_CreateSecretMenuOption(currentMenu->menuitems[i].text));
@@ -4285,7 +4341,7 @@ static void M_DrawGenericScrollMenu(void)
 
 	// DRAW THE SKULL CURSOR
 	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0,
-		W_CachePatchName("M_CURSOR", PU_CACHE));
+		W_CachePatchName("M_CURSOR", PU_PATCH));
 }
 
 static void M_DrawPauseMenu(void)
@@ -4406,10 +4462,10 @@ static void M_DrawPauseMenu(void)
 				continue;
 
 			if (emblem->collected)
-				V_DrawSmallMappedPatch(40, 44 + (i*8), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_CACHE),
+				V_DrawSmallMappedPatch(40, 44 + (i*8), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
 				                       R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
 			else
-				V_DrawSmallScaledPatch(40, 44 + (i*8), 0, W_CachePatchName("NEEDIT", PU_CACHE));
+				V_DrawSmallScaledPatch(40, 44 + (i*8), 0, W_CachePatchName("NEEDIT", PU_PATCH));
 
 			switch (emblem->type)
 			{
@@ -4455,13 +4511,13 @@ static void M_DrawCenteredMenu(void)
 					if (currentMenu->menuitems[i].status & IT_CENTER)
 					{
 						patch_t *p;
-						p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
+						p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_PATCH);
 						V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p);
 					}
 					else
 					{
 						V_DrawScaledPatch(x, y, 0,
-							W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE));
+							W_CachePatchName(currentMenu->menuitems[i].patch, PU_PATCH));
 					}
 				}
 				/* FALLTHRU */
@@ -4536,7 +4592,7 @@ static void M_DrawCenteredMenu(void)
 			case IT_GRAYPATCH:
 				if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0])
 					V_DrawMappedPatch(x, y, 0,
-						W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap);
+						W_CachePatchName(currentMenu->menuitems[i].patch,PU_PATCH), graymap);
 				y += LINEHEIGHT;
 				break;
 		}
@@ -4547,12 +4603,12 @@ static void M_DrawCenteredMenu(void)
 		|| ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_NOTHING))
 	{
 		V_DrawScaledPatch(x + SKULLXOFF, cursory - 5, 0,
-			W_CachePatchName("M_CURSOR", PU_CACHE));
+			W_CachePatchName("M_CURSOR", PU_PATCH));
 	}
 	else
 	{
 		V_DrawScaledPatch(x - V_StringWidth(currentMenu->menuitems[itemOn].text, 0)/2 - 24, cursory, 0,
-			W_CachePatchName("M_CURSOR", PU_CACHE));
+			W_CachePatchName("M_CURSOR", PU_PATCH));
 		V_DrawCenteredString(x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text);
 	}
 }
@@ -4700,6 +4756,9 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
 			if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE))
 				return true;
 
+			if (gt >= 0 && gt < gametypecount && (mapheaderinfo[mapnum]->typeoflevel & gametypetol[gt]))
+				return true;
+
 			return false;
 
 		case LLM_LEVELSELECT:
@@ -4793,6 +4852,22 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
 	return rows;
 }
 
+//
+// M_CacheLevelPlatter
+//
+// Cache every patch used by the level platter.
+//
+static void M_CacheLevelPlatter(void)
+{
+	levselp[0][0] = W_CachePatchName("SLCT1LVL", PU_PATCH);
+	levselp[0][1] = W_CachePatchName("SLCT2LVL", PU_PATCH);
+	levselp[0][2] = W_CachePatchName("BLANKLVL", PU_PATCH);
+
+	levselp[1][0] = W_CachePatchName("SLCT1LVW", PU_PATCH);
+	levselp[1][1] = W_CachePatchName("SLCT2LVW", PU_PATCH);
+	levselp[1][2] = W_CachePatchName("BLANKLVW", PU_PATCH);
+}
+
 //
 // M_PrepareLevelPlatter
 //
@@ -4942,24 +5017,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
 	}
 #endif
 
-	if (levselp[0][0]) // never going to have some provided but not all, saves individually checking
-	{
-		W_UnlockCachedPatch(levselp[0][0]);
-		W_UnlockCachedPatch(levselp[0][1]);
-		W_UnlockCachedPatch(levselp[0][2]);
-
-		W_UnlockCachedPatch(levselp[1][0]);
-		W_UnlockCachedPatch(levselp[1][1]);
-		W_UnlockCachedPatch(levselp[1][2]);
-	}
-
-	levselp[0][0] = W_CachePatchName("SLCT1LVL", PU_STATIC);
-	levselp[0][1] = W_CachePatchName("SLCT2LVL", PU_STATIC);
-	levselp[0][2] = W_CachePatchName("BLANKLVL", PU_STATIC);
-
-	levselp[1][0] = W_CachePatchName("SLCT1LVW", PU_STATIC);
-	levselp[1][1] = W_CachePatchName("SLCT2LVW", PU_STATIC);
-	levselp[1][2] = W_CachePatchName("BLANKLVW", PU_STATIC);
+	M_CacheLevelPlatter();
 
 	return true;
 }
@@ -5180,6 +5238,9 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
 	if (map <= 0)
 		return;
 
+	if (needpatchrecache)
+		M_CacheLevelPlatter();
+
 	//  A 564x100 image of the level as entry MAPxxW
 	if (!(levelselect.rows[row].mapavailable[col]))
 	{
@@ -5189,7 +5250,7 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
 	else
 	{
 		if (W_CheckNumForName(va("%sW", G_BuildMapName(map))) != LUMPERROR)
-			patch = W_CachePatchName(va("%sW", G_BuildMapName(map)), PU_CACHE);
+			patch = W_CachePatchName(va("%sW", G_BuildMapName(map)), PU_PATCH);
 		else
 			patch = levselp[1][2]; // don't static to indicate that it's just a normal level
 
@@ -5211,6 +5272,9 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
 	if (map <= 0)
 		return;
 
+	if (needpatchrecache)
+		M_CacheLevelPlatter();
+
 	//  A 160x100 image of the level as entry MAPxxP
 	if (!(levelselect.rows[row].mapavailable[col]))
 	{
@@ -5220,7 +5284,7 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
 	else
 	{
 		if (W_CheckNumForName(va("%sP", G_BuildMapName(map))) != LUMPERROR)
-			patch = W_CachePatchName(va("%sP", G_BuildMapName(map)), PU_CACHE);
+			patch = W_CachePatchName(va("%sP", G_BuildMapName(map)), PU_PATCH);
 		else
 			patch = levselp[0][2]; // don't static to indicate that it's just a normal level
 
@@ -5280,8 +5344,8 @@ static void M_DrawLevelPlatterRow(UINT8 row, INT32 y)
 // new menus
 static void M_DrawRecordAttackForeground(void)
 {
-	patch_t *fg = W_CachePatchName("RECATKFG", PU_CACHE);
-	patch_t *clock = W_CachePatchName("RECCLOCK", PU_CACHE);
+	patch_t *fg = W_CachePatchName("RECATKFG", PU_PATCH);
+	patch_t *clock = W_CachePatchName("RECCLOCK", PU_PATCH);
 	angle_t fa;
 
 	INT32 i;
@@ -5319,7 +5383,7 @@ static void M_DrawNightsAttackMountains(void)
 {
 	static INT32 bgscrollx;
 	INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
-	patch_t *background = W_CachePatchName(curbgname, PU_CACHE);
+	patch_t *background = W_CachePatchName(curbgname, PU_PATCH);
 	INT32 x = FixedInt(bgscrollx) % SHORT(background->width);
 	INT32 y = BASEVIDHEIGHT - SHORT(background->height)*2;
 
@@ -5342,16 +5406,16 @@ static void M_DrawNightsAttackBackground(void)
 	INT32 i;
 
 	// top
-	patch_t *backtopfg = W_CachePatchName("NTSATKT1", PU_CACHE);
-	patch_t *fronttopfg = W_CachePatchName("NTSATKT2", PU_CACHE);
+	patch_t *backtopfg = W_CachePatchName("NTSATKT1", PU_PATCH);
+	patch_t *fronttopfg = W_CachePatchName("NTSATKT2", PU_PATCH);
 	INT32 backtopwidth = SHORT(backtopfg->width);
 	//INT32 backtopheight = SHORT(backtopfg->height);
 	INT32 fronttopwidth = SHORT(fronttopfg->width);
 	//INT32 fronttopheight = SHORT(fronttopfg->height);
 
 	// bottom
-	patch_t *backbottomfg = W_CachePatchName("NTSATKB1", PU_CACHE);
-	patch_t *frontbottomfg = W_CachePatchName("NTSATKB2", PU_CACHE);
+	patch_t *backbottomfg = W_CachePatchName("NTSATKB1", PU_PATCH);
+	patch_t *frontbottomfg = W_CachePatchName("NTSATKB2", PU_PATCH);
 	INT32 backbottomwidth = SHORT(backbottomfg->width);
 	INT32 backbottomheight = SHORT(backbottomfg->height);
 	INT32 frontbottomwidth = SHORT(frontbottomfg->width);
@@ -5361,7 +5425,7 @@ static void M_DrawNightsAttackBackground(void)
 	M_DrawNightsAttackMountains();
 
 	// back top foreground patch
-	x = -(ntsatkdrawtimer%backtopwidth);
+	x = 0-(ntsatkdrawtimer%backtopwidth);
 	V_DrawScaledPatch(x, y, V_SNAPTOTOP|V_SNAPTOLEFT, backtopfg);
 	for (i = 0; i < 3; i++)
 	{
@@ -5372,7 +5436,7 @@ static void M_DrawNightsAttackBackground(void)
 	}
 
 	// front top foreground patch
-	x = -((ntsatkdrawtimer*2)%fronttopwidth);
+	x = 0-((ntsatkdrawtimer*2)%fronttopwidth);
 	V_DrawScaledPatch(x, y, V_SNAPTOTOP|V_SNAPTOLEFT, fronttopfg);
 	for (i = 0; i < 3; i++)
 	{
@@ -5383,7 +5447,7 @@ static void M_DrawNightsAttackBackground(void)
 	}
 
 	// back bottom foreground patch
-	x = -(ntsatkdrawtimer%backbottomwidth);
+	x = 0-(ntsatkdrawtimer%backbottomwidth);
 	y = BASEVIDHEIGHT - backbottomheight;
 	V_DrawScaledPatch(x, y, V_SNAPTOBOTTOM|V_SNAPTOLEFT, backbottomfg);
 	for (i = 0; i < 3; i++)
@@ -5395,7 +5459,7 @@ static void M_DrawNightsAttackBackground(void)
 	}
 
 	// front bottom foreground patch
-	x = -((ntsatkdrawtimer*2)%frontbottomwidth);
+	x = 0-((ntsatkdrawtimer*2)%frontbottomwidth);
 	y = BASEVIDHEIGHT - frontbottomheight;
 	V_DrawScaledPatch(x, y, V_SNAPTOBOTTOM|V_SNAPTOLEFT, frontbottomfg);
 	for (i = 0; i < 3; i++)
@@ -5495,7 +5559,7 @@ static void M_DrawLevelPlatterMenu(void)
 
 #if 0
 	if (levelselect.rows[lsrow].maplist[lscol] > 0)
-		V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE));
+		V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_PATCH));
 #endif
 
 	// handle movement of cursor box
@@ -5765,7 +5829,7 @@ static void M_DrawImageDef(void)
 		V_DrawScaledPic(0,0,0,W_GetNumForName(currentMenu->menuitems[itemOn].text));
 	else
 	{
-		patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text,PU_CACHE);
+		patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text,PU_PATCH);
 		if (patch->width <= BASEVIDWIDTH)
 			V_DrawScaledPatch(0,0,0,patch);
 		else
@@ -5824,6 +5888,27 @@ static void M_AddonsOptions(INT32 choice)
 #define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make add-ons!"
 //#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make add-ons!"
 
+static void M_LoadAddonsPatches(void)
+{
+	addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_PATCH);
+	addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_PATCH);
+	addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_PATCH);
+	addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_PATCH);
+	addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_PATCH);
+	addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_PATCH);
+#ifdef USE_KART
+	addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_PATCH);
+#endif
+	addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_PATCH);
+	addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_PATCH);
+	addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_PATCH);
+	addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_PATCH);
+	addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_PATCH);
+	addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_PATCH);
+	addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_PATCH);
+	addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_PATCH);
+}
+
 static void M_Addons(INT32 choice)
 {
 	const char *pathname = ".";
@@ -5867,30 +5952,7 @@ static void M_Addons(INT32 choice)
 	else
 		dir_on[menudepthleft] = 0;
 
-	if (addonsp[0]) // never going to have some provided but not all, saves individually checking
-	{
-		size_t i;
-		for (i = 0; i < NUM_EXT+5; i++)
-			W_UnlockCachedPatch(addonsp[i]);
-	}
-
-	addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_STATIC);
-	addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_STATIC);
-	addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_STATIC);
-	addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_STATIC);
-	addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_STATIC);
-	addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_STATIC);
-#ifdef USE_KART
-	addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_STATIC);
-#endif
-	addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_STATIC);
-	addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_STATIC);
-	addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_STATIC);
-	addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_STATIC);
-	addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_STATIC);
-	addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_STATIC);
-	addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_STATIC);
-	addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_STATIC);
+	M_LoadAddonsPatches();
 
 	MISC_AddonsDef.prevMenu = currentMenu;
 	M_SetupNextMenu(&MISC_AddonsDef);
@@ -6030,6 +6092,10 @@ static void M_DrawAddons(void)
 		return;
 	}
 
+	// Lactozilla: Load addons menu patches.
+	if (needpatchrecache)
+		M_LoadAddonsPatches();
+
 	if (Playing())
 		V_DrawCenteredString(BASEVIDWIDTH/2, 5, warningflags, "Adding files mid-game may cause problems.");
 	else
@@ -6386,11 +6452,7 @@ static void M_PandorasBox(INT32 choice)
 	else
 		CV_StealthSetValue(&cv_dummylives, max(players[consoleplayer].lives, 1));
 	CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues);
-	SR_PandorasBox[6].status = ((players[consoleplayer].charflags & SF_SUPER)
-#ifndef DEVELOP
-	|| cv_skin.value == 1
-#endif
-	) ? (IT_GRAYEDOUT) : (IT_STRING | IT_CALL);
+	SR_PandorasBox[6].status = (players[consoleplayer].charflags & SF_SUPER) ? (IT_GRAYEDOUT) : (IT_STRING | IT_CALL);
 	SR_PandorasBox[7].status = (emeralds == ((EMERALD7)*2)-1) ? (IT_GRAYEDOUT) : (IT_STRING | IT_CALL);
 	M_SetupNextMenu(&SR_PandoraDef);
 }
@@ -6692,7 +6754,7 @@ static void M_DrawChecklist(void)
 		|| !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS
 		|| (!unlockables[i].unlocked && unlockables[i].showconditionset && !M_Achieved(unlockables[i].showconditionset)))
 		{
-			i++;
+			i += 1;
 			continue;
 		}
 
@@ -6975,13 +7037,13 @@ static void M_DrawEmblemHints(void)
 		if (emblem->collected)
 		{
 			collected = V_GREENMAP;
-			V_DrawMappedPatch(12, 12+(28*j), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_CACHE),
+			V_DrawMappedPatch(12, 12+(28*j), 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_PATCH),
 				R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
 		}
 		else
 		{
 			collected = 0;
-			V_DrawScaledPatch(12, 12+(28*j), 0, W_CachePatchName("NEEDIT", PU_CACHE));
+			V_DrawScaledPatch(12, 12+(28*j), 0, W_CachePatchName("NEEDIT", PU_PATCH));
 		}
 
 		if (emblem->hint[0])
@@ -7035,40 +7097,42 @@ static tic_t st_time = 0;
 static patch_t* st_radio[9];
 static patch_t* st_launchpad[4];
 
-static void M_SoundTest(INT32 choice)
+static void M_CacheSoundTest(void)
 {
-	INT32 ul = skyRoomMenuTranslations[choice-1];
 	UINT8 i;
 	char buf[8];
 
-	soundtestpage = (UINT8)(unlockables[ul].variable);
-	if (!soundtestpage)
-		soundtestpage = 1;
-
-	if (!S_PrepareSoundTest())
-	{
-		M_StartMessage(M_GetText("No selectable tracks found.\n"),NULL,MM_NOTHING);
-		return;
-	}
-
 	STRBUFCPY(buf, "M_RADIOn");
 	for (i = 0; i < 9; i++)
 	{
-		if (st_radio[i])
-			W_UnlockCachedPatch(st_radio[i]);
 		buf[7] = (char)('0'+i);
-		st_radio[i] = W_CachePatchName(buf, PU_STATIC);
+		st_radio[i] = W_CachePatchName(buf, PU_PATCH);
 	}
 
 	STRBUFCPY(buf, "M_LPADn");
 	for (i = 0; i < 4; i++)
 	{
-		if (st_launchpad[i])
-			W_UnlockCachedPatch(st_launchpad[i]);
 		buf[6] = (char)('0'+i);
-		st_launchpad[i] = W_CachePatchName(buf, PU_STATIC);
+		st_launchpad[i] = W_CachePatchName(buf, PU_PATCH);
+	}
+}
+
+static void M_SoundTest(INT32 choice)
+{
+	INT32 ul = skyRoomMenuTranslations[choice-1];
+
+	soundtestpage = (UINT8)(unlockables[ul].variable);
+	if (!soundtestpage)
+		soundtestpage = 1;
+
+	if (!S_PrepareSoundTest())
+	{
+		M_StartMessage(M_GetText("No selectable tracks found.\n"),NULL,MM_NOTHING);
+		return;
 	}
 
+	M_CacheSoundTest();
+
 	curplaying = NULL;
 	st_time = 0;
 
@@ -7086,6 +7150,9 @@ static void M_DrawSoundTest(void)
 	fixed_t hscale = FRACUNIT/2, vscale = FRACUNIT/2, bounce = 0;
 	UINT8 frame[4] = {0, 0, -1, SKINCOLOR_RUBY};
 
+	if (needpatchrecache)
+		M_CacheSoundTest();
+
 	// let's handle the ticker first. ideally we'd tick this somewhere else, BUT...
 	if (curplaying)
 	{
@@ -7658,6 +7725,17 @@ static INT32 saveSlotSelected = 1;
 static INT32 loadgamescroll = 0;
 static UINT8 loadgameoffset = 0;
 
+static void M_CacheLoadGameData(void)
+{
+	savselp[0] = W_CachePatchName("SAVEBACK", PU_PATCH);
+	savselp[1] = W_CachePatchName("SAVENONE", PU_PATCH);
+	savselp[2] = W_CachePatchName("ULTIMATE", PU_PATCH);
+
+	savselp[3] = W_CachePatchName("GAMEDONE", PU_PATCH);
+	savselp[4] = W_CachePatchName("BLACXLVL", PU_PATCH);
+	savselp[5] = W_CachePatchName("BLANKLVL", PU_PATCH);
+}
+
 static void M_DrawLoadGameData(void)
 {
 	INT32 i, savetodraw, x, y, hsep = 90;
@@ -7666,6 +7744,9 @@ static void M_DrawLoadGameData(void)
 	if (vid.width != BASEVIDWIDTH*vid.dupx)
 		hsep = (hsep*vid.width)/(BASEVIDWIDTH*vid.dupx);
 
+	if (needpatchrecache)
+		M_CacheLoadGameData();
+
 	for (i = -2; i <= 2; i++)
 	{
 		savetodraw = (saveSlotSelected + i + numsaves)%numsaves;
@@ -7774,7 +7855,7 @@ static void M_DrawLoadGameData(void)
 				{
 					lumpnum_t lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName((savegameinfo[savetodraw].gamemap) & 8191)));
 					if (lumpnum != LUMPERROR)
-						patch = W_CachePatchNum(lumpnum, PU_CACHE);
+						patch = W_CachePatchNum(lumpnum, PU_PATCH);
 					else
 						patch = savselp[5];
 				}
@@ -7830,7 +7911,7 @@ static void M_DrawLoadGameData(void)
 					goto skipbot;
 				colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin, charbotskin->prefcolor, 0);
 				sprframe = &sprdef->spriteframes[0];
-				patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
+				patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 
 				V_DrawFixedPatch(
 					tempx + (18<<FRACBITS),
@@ -7841,7 +7922,7 @@ static void M_DrawLoadGameData(void)
 				Z_Free(colormap);
 
 				tempx -= (20<<FRACBITS);
-				flip = V_FLIP;
+				//flip = V_FLIP;
 			}
 skipbot:
 			// signpost image
@@ -7852,7 +7933,7 @@ skipbot:
 			if (!sprdef->numframes)
 				goto skipsign;
 			sprframe = &sprdef->spriteframes[0];
-			patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
+			patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 
 			V_DrawFixedPatch(
 				tempx,
@@ -7876,7 +7957,7 @@ skipsign:
 			if (!sprdef->numframes)
 				goto skiplife;
 			sprframe = &sprdef->spriteframes[0];
-			patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
+			patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 
 			V_DrawFixedPatch(
 				(tempx + 4)<<FRACBITS,
@@ -7887,7 +7968,7 @@ skiplife:
 			if (colormap)
 				Z_Free(colormap);
 
-			patch = W_CachePatchName("STLIVEX", PU_CACHE);
+			patch = W_CachePatchName("STLIVEX", PU_PATCH);
 
 			V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
 			tempx += 16;
@@ -7903,14 +7984,14 @@ skiplife:
 			// continues
 			if (savegameinfo[savetodraw].continues > 0)
 			{
-				V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTSAVE", PU_CACHE));
+				V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTSAVE", PU_PATCH));
 				V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
 				V_DrawString(tempx + 16, y, 0, va("%d", savegameinfo[savetodraw].continues));
 			}
 			else
 			{
-				V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTNONE", PU_CACHE));
-				V_DrawScaledPatch(tempx + 9, y + 2, 0, W_CachePatchName("STNONEX", PU_CACHE));
+				V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTNONE", PU_PATCH));
+				V_DrawScaledPatch(tempx + 9, y + 2, 0, W_CachePatchName("STNONEX", PU_PATCH));
 				V_DrawString(tempx + 16, y, V_GRAYMAP, "0");
 			}
 		}
@@ -8142,24 +8223,7 @@ static void M_ReadSaveStrings(void)
 		M_ReadSavegameInfo(i);
 	}
 
-	if (savselp[0]) // never going to have some provided but not all, saves individually checking
-	{
-		W_UnlockCachedPatch(savselp[0]);
-		W_UnlockCachedPatch(savselp[1]);
-		W_UnlockCachedPatch(savselp[2]);
-
-		W_UnlockCachedPatch(savselp[3]);
-		W_UnlockCachedPatch(savselp[4]);
-		W_UnlockCachedPatch(savselp[5]);
-	}
-
-	savselp[0] = W_CachePatchName("SAVEBACK", PU_STATIC);
-	savselp[1] = W_CachePatchName("SAVENONE", PU_STATIC);
-	savselp[2] = W_CachePatchName("ULTIMATE", PU_STATIC);
-
-	savselp[3] = W_CachePatchName("GAMEDONE", PU_STATIC);
-	savselp[4] = W_CachePatchName("BLACXLVL", PU_STATIC);
-	savselp[5] = W_CachePatchName("BLANKLVL", PU_STATIC);
+	M_CacheLoadGameData();
 }
 
 //
@@ -8339,20 +8403,53 @@ void M_ForceSaveSlotSelected(INT32 sslot)
 // CHARACTER SELECT
 // ================
 
+// lactozilla: sometimes the renderer changes and these patches don't exist anymore
+static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum)
+{
+	if (!(description[i].picname[0]))
+	{
+		if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_CHARSEL)
+		{
+			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);
+		}
+		else
+			description[i].charpic = W_CachePatchName("MISSING", PU_PATCH);
+	}
+	else
+		description[i].charpic = W_CachePatchName(description[i].picname, PU_PATCH);
+
+	if (description[i].nametag[0])
+		description[i].namepic = W_CachePatchName(description[i].nametag, PU_PATCH);
+}
+
+static void M_CacheCharacterSelect(void)
+{
+	INT32 i, skinnum;
+
+	for (i = 0; i < 32; i++)
+	{
+		if (!description[i].used)
+			continue;
+
+		// Already set in M_SetupChoosePlayer
+		skinnum = description[i].skinnum[0];
+		if ((skinnum != -1) && (R_SkinUsable(-1, skinnum)))
+			M_CacheCharacterSelectEntry(i, skinnum);
+	}
+}
+
 static void M_SetupChoosePlayer(INT32 choice)
 {
 	INT32 skinnum;
 	UINT8 i;
-	UINT8 firstvalid = 255;
-	UINT8 lastvalid = 0;
+	UINT8 firstvalid = 255, lastvalid = 255;
 	boolean allowed = false;
 	char *and;
 	(void)choice;
 
-	if (!(mapheaderinfo[startmap-1]
-			&& (mapheaderinfo[startmap-1]->forcecharacter[0] != '\0'
-			|| (mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS)) // remove this later when everyone gets their own nights sprites, maybe
-		))
+	if (!mapheaderinfo[startmap-1] || mapheaderinfo[startmap-1]->forcecharacter[0] == '\0')
 	{
 		for (i = 0; i < 32; i++) // Handle charsels, availability, and unlocks.
 		{
@@ -8362,6 +8459,8 @@ static void M_SetupChoosePlayer(INT32 choice)
 				if (and)
 				{
 					char firstskin[SKINNAMESIZE+1];
+					if (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);
@@ -8388,44 +8487,23 @@ static void M_SetupChoosePlayer(INT32 choice)
 					if (i == char_on)
 						allowed = true;
 
-					if (!(description[i].picname[0]))
-					{
-						if (skins[skinnum].sprites[SPR2_XTRA].numframes >= XTRA_CHARSEL+1)
-						{
-							spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
-							spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL];
-							description[i].charpic = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
-						}
-						else
-							description[i].charpic = W_CachePatchName("MISSING", PU_CACHE);
-					}
-					else
-						description[i].charpic = W_CachePatchName(description[i].picname, PU_CACHE);
-
-					if (description[i].nametag[0])
-					{
-						const char *nametag = description[i].nametag;
-						description[i].namepic = NULL;
-						if (W_LumpExists(nametag))
-							description[i].namepic = W_CachePatchName(nametag, PU_CACHE);
-					}
+					M_CacheCharacterSelectEntry(i, skinnum);
 				}
 				// else -- Technically, character select icons without corresponding skins get bundled away behind this too. Sucks to be them.
 			}
 		}
 	}
 
-	if (firstvalid != 255)
-	{ // One last bit of order we can't do in the iteration above.
-		description[firstvalid].prev = lastvalid;
-		description[lastvalid].next = firstvalid;
-	}
-	else // We're being forced into a specific character, so might as well just skip it.
+	if (firstvalid == lastvalid) // We're being forced into a specific character, so might as well just skip it.
 	{
-		M_ChoosePlayer(-1);
+		M_ChoosePlayer(firstvalid);
 		return;
 	}
 
+	// One last bit of order we can't do in the iteration above.
+	description[firstvalid].prev = lastvalid;
+	description[lastvalid].next = firstvalid;
+
 	M_ChangeMenuMusic("_chsel", true);
 
 	/* the menus suck -James */
@@ -8548,8 +8626,8 @@ static void M_DrawSetupChoosePlayerMenu(void)
 	UINT8 *colormap = NULL;
 	INT32 prev = -1, next = -1;
 
-	patch_t *charbg = W_CachePatchName("CHARBG", PU_CACHE);
-	patch_t *charfg = W_CachePatchName("CHARFG", PU_CACHE);
+	patch_t *charbg = W_CachePatchName("CHARBG", PU_PATCH);
+	patch_t *charfg = W_CachePatchName("CHARFG", PU_PATCH);
 	INT16 bgheight = SHORT(charbg->height);
 	INT16 fgheight = SHORT(charfg->height);
 	INT16 bgwidth = SHORT(charbg->width);
@@ -8557,6 +8635,10 @@ static void M_DrawSetupChoosePlayerMenu(void)
 	INT32 x, y;
 	INT32 w = (vid.width/vid.dupx);
 
+	// lactozilla: the renderer changed so recache patches
+	if (needpatchrecache)
+		M_CacheCharacterSelect();
+
 	if (abs(char_scroll) > FRACUNIT)
 		char_scroll -= (char_scroll>>2);
 	else // close enough.
@@ -8735,7 +8817,7 @@ static void M_DrawSetupChoosePlayerMenu(void)
 	// Alternative menu header
 #ifdef CHOOSEPLAYER_DRAWHEADER
 	{
-		patch_t *header = W_CachePatchName("M_PICKP", PU_CACHE);
+		patch_t *header = W_CachePatchName("M_PICKP", PU_PATCH);
 		INT32 xtitle = 146;
 		INT32 ytitle = (35 - SHORT(header->height))/2;
 		V_DrawFixedPatch(xtitle<<FRACBITS, ytitle<<FRACBITS, FRACUNIT/2, 0, header, NULL);
@@ -8752,7 +8834,7 @@ static void M_ChoosePlayer(INT32 choice)
 	UINT8 skinnum;
 
 	// skip this if forcecharacter or no characters available
-	if (choice == -1)
+	if (choice == 255)
 	{
 		skinnum = botskin = 0;
 		botingame = false;
@@ -8905,10 +8987,10 @@ static void M_DrawStatsMaps(int location)
 			exemblem = &extraemblems[i];
 
 			if (exemblem->collected)
-				V_DrawSmallMappedPatch(292, y, 0, W_CachePatchName(M_GetExtraEmblemPatch(exemblem, false), PU_CACHE),
+				V_DrawSmallMappedPatch(292, y, 0, W_CachePatchName(M_GetExtraEmblemPatch(exemblem, false), PU_PATCH),
 				                       R_GetTranslationColormap(TC_DEFAULT, M_GetExtraEmblemColor(exemblem), GTC_CACHE));
 			else
-				V_DrawSmallScaledPatch(292, y, 0, W_CachePatchName("NEEDIT", PU_CACHE));
+				V_DrawSmallScaledPatch(292, y, 0, W_CachePatchName("NEEDIT", PU_PATCH));
 
 			V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE,
 				(!exemblem->collected && exemblem->showconditionset && !M_Achieved(exemblem->showconditionset))
@@ -8987,7 +9069,7 @@ static void M_DrawLevelStats(void)
 		V_DrawString(20, 56, V_GREENMAP, "(complete)");
 
 	V_DrawString(36, 64, 0, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems));
-	V_DrawSmallScaledPatch(20, 64, 0, W_CachePatchName("EMBLICON", PU_STATIC));
+	V_DrawSmallScaledPatch(20, 64, 0, W_CachePatchName("EMBLICON", PU_PATCH));
 
 	sprintf(beststr, "%u", bestscore);
 	V_DrawString(BASEVIDWIDTH/2, 48, V_YELLOWMAP, "SCORE:");
@@ -9119,19 +9201,19 @@ void M_DrawTimeAttackMenu(void)
 	}
 
 	// DRAW THE SKULL CURSOR
-	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE));
+	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_PATCH));
 	V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text);
 
 	// Character face!
 	{
-		if (skins[cv_chooseskin.value-1].sprites[SPR2_XTRA].numframes >= XTRA_CHARSEL+1)
+		if (skins[cv_chooseskin.value-1].sprites[SPR2_XTRA].numframes > XTRA_CHARSEL)
 		{
 			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_CACHE);
+			PictureOfUrFace = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 		}
 		else
-			PictureOfUrFace = W_CachePatchName("MISSING", PU_CACHE);
+			PictureOfUrFace = W_CachePatchName("MISSING", PU_PATCH);
 
 		if (PictureOfUrFace->width >= 256)
 			V_DrawTinyScaledPatch(224, 120, 0, PictureOfUrFace);
@@ -9159,9 +9241,9 @@ void M_DrawTimeAttackMenu(void)
 		lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
 
 		if (lumpnum != LUMPERROR)
-			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
+			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
 		else
-			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
+			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
 
 		y = 32+lsheadingheight;
 		V_DrawSmallScaledPatch(216, y, 0, PictureOfLevel);
@@ -9206,7 +9288,7 @@ void M_DrawTimeAttackMenu(void)
 					goto skipThisOne;
 			}
 
-			empatch = W_CachePatchName(M_GetEmblemPatch(em, true), PU_CACHE);
+			empatch = W_CachePatchName(M_GetEmblemPatch(em, true), PU_PATCH);
 
 			empatx = SHORT(empatch->leftoffset)/2;
 			empaty = SHORT(empatch->topoffset)/2;
@@ -9215,7 +9297,7 @@ void M_DrawTimeAttackMenu(void)
 				V_DrawSmallMappedPatch(104+76+empatx, yHeight+lsheadingheight/2+empaty, 0, empatch,
 				                       R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
 			else
-				V_DrawSmallScaledPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDITL", PU_CACHE));
+				V_DrawSmallScaledPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDITL", PU_PATCH));
 
 			skipThisOne:
 			em = M_GetLevelEmblems(-1);
@@ -9248,7 +9330,7 @@ void M_DrawTimeAttackMenu(void)
 
 		V_DrawString(104-72, 73+lsheadingheight/2, V_YELLOWMAP, "RINGS:");
 
-		V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((mapvisited[cv_nextmap.value-1] & MV_PERFECT) ? V_YELLOWMAP : 0), beststr);
+		V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((mapvisited[cv_nextmap.value-1] & MV_PERFECTRA) ? V_YELLOWMAP : 0), beststr);
 
 		V_DrawRightAlignedString(104+72, 83+lsheadingheight/2, V_ALLOWLOWERCASE, reqrings);
 	}
@@ -9288,7 +9370,10 @@ static void M_HandleTimeAttackLevelSelect(INT32 choice)
 			break;
 
 		case KEY_ENTER:
-			M_TimeAttackLevelSelect(0);
+			if (levellistmode == LLM_NIGHTSATTACK)
+				M_NightsAttackLevelSelect(0);
+			else
+				M_TimeAttackLevelSelect(0);
 			break;
 
 		case KEY_ESCAPE:
@@ -9392,7 +9477,7 @@ void M_DrawNightsAttackMenu(void)
 	}
 
 	// DRAW THE SKULL CURSOR
-	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE));
+	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_PATCH));
 	V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text);
 
 	// Level record list
@@ -9400,6 +9485,7 @@ void M_DrawNightsAttackMenu(void)
 	{
 		emblem_t *em;
 		INT32 yHeight;
+		INT32 xpos;
 		patch_t *PictureOfLevel;
 		lumpnum_t lumpnum;
 		char beststr[40];
@@ -9415,11 +9501,28 @@ void M_DrawNightsAttackMenu(void)
 		lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
 
 		if (lumpnum != LUMPERROR)
-			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
+			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
 		else
-			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
+			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
 
-		V_DrawSmallScaledPatch(208, 32+lsheadingheight, 0, PictureOfLevel);
+		y = 32+lsheadingheight;
+		V_DrawSmallScaledPatch(208, y, 0, PictureOfLevel);
+
+		// Draw press ESC to exit string on main nights attack menu
+		if (currentMenu == &SP_NightsAttackDef)
+		{
+			if (itemOn == nalevel)
+			{
+				/* Draw arrows !! */
+				y = y + 25 - 4;
+				V_DrawCharacter(208 - 10 - (skullAnimCounter/5), y,
+						'\x1C' | V_YELLOWMAP, false);
+				V_DrawCharacter(208 + 80 + 2 + (skullAnimCounter/5), y,
+						'\x1D' | V_YELLOWMAP, false);
+			}
+			// Draw press ESC to exit string on main record attack menu
+			V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit"));
+		}
 
 		// Super Sonic
 		M_DrawNightsAttackSuperSonic();
@@ -9459,17 +9562,23 @@ void M_DrawNightsAttackMenu(void)
 			{
 				switch (em->type)
 				{
-					case ET_NGRADE: yHeight = 48; break;
-					case ET_NTIME:  yHeight = 68; break;
+					case ET_NGRADE:
+						xpos = 104+38;
+						yHeight = 48;
+						break;
+					case ET_NTIME:
+						xpos = 104+76;
+						yHeight = 68;
+						break;
 					default:
 						goto skipThisOne;
 				}
 
 				if (em->collected)
-					V_DrawSmallMappedPatch(104+38, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em, false), PU_CACHE),
+					V_DrawSmallMappedPatch(xpos, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em, false), PU_PATCH),
 																 R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
 				else
-					V_DrawSmallScaledPatch(104+38, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_CACHE));
+					V_DrawSmallScaledPatch(xpos, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_PATCH));
 
 				skipThisOne:
 				em = M_GetLevelEmblems(-1);
@@ -9477,10 +9586,6 @@ void M_DrawNightsAttackMenu(void)
 		}
 	}
 
-	// Draw press ESC to exit string on main nights attack menu
-	if (currentMenu == &SP_NightsAttackDef)
-		V_DrawString(104-72, 180, V_TRANSLUCENT, M_GetText("Press ESC to exit"));
-
 	// ALWAYS DRAW level even when not on this menu!
 	if (currentMenu != &SP_NightsAttackDef)
 		V_DrawString(SP_NightsAttackDef.x, SP_NightsAttackDef.y + SP_TimeAttackMenu[nalevel].alphaKey, V_TRANSLUCENT, SP_NightsAttackMenu[nalevel].text);
@@ -9509,8 +9614,8 @@ 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_CACHE);
-	ntssupersonic[1] = W_CachePatchName("NTSSONC2", PU_CACHE);
+	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
@@ -9896,7 +10001,7 @@ static void M_DrawRoomMenu(void)
 static void M_DrawConnectMenu(void)
 {
 	UINT16 i;
-	const char *gt = "Unknown";
+	char *gt;
 	INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
 
 	for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
@@ -9940,14 +10045,17 @@ static void M_DrawConnectMenu(void)
 		V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags,
 		                     va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time)));
 
-		gt = "Unknown";
-		if (serverlist[slindex].info.gametype < NUMGAMETYPES)
-			gt = Gametype_Names[serverlist[slindex].info.gametype];
+		gt = serverlist[slindex].info.gametypename;
 
 		V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
 		                         va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
 
-		V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, va("Gametype: %s", gt));
+		if (strlen(gt) > 11)
+			gt = va("Gametype: %.11s...", gt);
+		else
+			gt = va("Gametype: %s", gt);
+
+		V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt);
 
 		MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
 	}
@@ -9988,7 +10096,15 @@ SERVER_LIST_ENTRY_COMPARATOR(time)
 SERVER_LIST_ENTRY_COMPARATOR(numberofplayer)
 SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer)
 SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer)
-SERVER_LIST_ENTRY_COMPARATOR(gametype)
+
+static int ServerListEntryComparator_gametypename(const void *entry1, const void *entry2)
+{
+	const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2;
+	int c;
+	if (( c = strcasecmp(sa->info.gametypename, sb->info.gametypename) ))
+		return c;
+	return strcmp(sa->info.servername, sb->info.servername); \
+}
 
 // Special one for modified state.
 static int ServerListEntryComparator_modified(const void *entry1, const void *entry2)
@@ -10028,7 +10144,7 @@ void M_SortServerList(void)
 		qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse);
 		break;
 	case 5:		// Gametype.
-		qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype);
+		qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename);
 		break;
 	}
 #endif
@@ -10232,9 +10348,9 @@ static void M_DrawServerMenu(void)
 		lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
 
 		if (lumpnum != LUMPERROR)
-			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
+			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
 		else
-			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
+			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
 
 		V_DrawSmallScaledPatch(319 - (currentMenu->x + (SHORT(PictureOfLevel->width)/2)), currentMenu->y + imgheight, 0, PictureOfLevel);
 	}
@@ -10413,15 +10529,78 @@ static void M_HandleConnectIP(INT32 choice)
 			break;
 
 		case KEY_DEL:
-			if (setupm_ip[0])
+			if (setupm_ip[0] && !shiftdown) // Shift+Delete is used for something else.
 			{
 				S_StartSound(NULL,sfx_menu1); // Tails
 				setupm_ip[0] = 0;
 			}
-			break;
+			if (!shiftdown) // Shift+Delete is used for something else.
+				break;
 
+			/* FALLTHRU */
 		default:
 			l = strlen(setupm_ip);
+
+			if ( ctrldown ) {
+				switch (choice) {
+					case 'v':
+					case 'V': // ctrl+v, pasting
+					{
+						const char *paste = I_ClipboardPaste();
+
+						if (paste != NULL) {
+							strncat(setupm_ip, paste, 28-1 - l); // Concat the ip field with clipboard
+							if (strlen(paste) != 0) // Don't play sound if nothing was pasted
+								S_StartSound(NULL,sfx_menu1); // Tails
+						}
+
+						break;
+					}
+					case KEY_INS:
+					case 'c':
+					case 'C': // ctrl+c, ctrl+insert, copying
+						I_ClipboardCopy(setupm_ip, l);
+						S_StartSound(NULL,sfx_menu1); // Tails
+						break;
+
+					case 'x':
+					case 'X': // ctrl+x, cutting
+						I_ClipboardCopy(setupm_ip, l);
+						S_StartSound(NULL,sfx_menu1); // Tails
+						setupm_ip[0] = 0;
+						break;
+
+					default: // otherwise do nothing
+						break;
+				}
+				break; // don't check for typed keys
+			}
+
+			if ( shiftdown ) {
+				switch (choice) {
+					case KEY_INS: // shift+insert, pasting
+						{
+							const char *paste = I_ClipboardPaste();
+
+							if (paste != NULL) {
+								strncat(setupm_ip, paste, 28-1 - l); // Concat the ip field with clipboard
+								if (strlen(paste) != 0) // Don't play sound if nothing was pasted
+									S_StartSound(NULL,sfx_menu1); // Tails
+							}
+
+							break;
+						}
+					case KEY_DEL: // shift+delete, cutting
+						I_ClipboardCopy(setupm_ip, l);
+						S_StartSound(NULL,sfx_menu1); // Tails
+						setupm_ip[0] = 0;
+						break;
+					default: // otherwise do nothing.
+						break;
+				}
+				break; // don't check for typed keys
+			}
+
 			if (l >= 28-1)
 				break;
 
@@ -10551,7 +10730,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
 		multi_frame = 0;
 
 	sprframe = &sprdef->spriteframes[multi_frame];
-	patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
+	patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 	if (sprframe->flip & 1) // Only for first sprite
 		flags |= V_FLIP; // This sprite is left/right flipped!
 
@@ -10572,7 +10751,7 @@ faildraw:
 		return; // Can't render!
 
 	sprframe = &sprdef->spriteframes[0];
-	patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
+	patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 	if (sprframe->flip & 1) // Only for first sprite
 		flags |= V_FLIP; // This sprite is left/right flipped!
 
@@ -10643,7 +10822,7 @@ colordraw:
 		cursory = y;
 
 	V_DrawScaledPatch(x - 17, cursory, 0,
-		W_CachePatchName("M_CURSOR", PU_CACHE));
+		W_CachePatchName("M_CURSOR", PU_PATCH));
 }
 
 // Handle 1P/2P MP Setup
@@ -10969,7 +11148,7 @@ static void M_DrawJoystick(void)
 		if (i == itemOn)
 		{
 			V_DrawScaledPatch(currentMenu->x - 24, OP_JoystickSetDef.y+LINEHEIGHT*i-4, 0,
-				W_CachePatchName("M_CURSOR", PU_CACHE));
+				W_CachePatchName("M_CURSOR", PU_PATCH));
 		}
 	}
 }
@@ -11287,7 +11466,7 @@ static void M_DrawControl(void)
 	}
 
 	V_DrawScaledPatch(currentMenu->x - 20, cursory, 0,
-		W_CachePatchName("M_CURSOR", PU_CACHE));
+		W_CachePatchName("M_CURSOR", PU_PATCH));
 }
 
 #undef controlbuffer
@@ -11579,7 +11758,7 @@ static void M_DrawVideoMode(void)
 	j = OP_VideoModeDef.y + 14 + ((vidm_selected % vidm_column_size)*8);
 
 	V_DrawScaledPatch(i - 8, j, 0,
-		W_CachePatchName("M_CURSOR", PU_CACHE));
+		W_CachePatchName("M_CURSOR", PU_PATCH));
 }
 
 // Just M_DrawGenericScrollMenu but showing a backing behind the headers.
@@ -11707,7 +11886,7 @@ static void M_DrawColorMenu(void)
 
 	// DRAW THE SKULL CURSOR
 	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0,
-		W_CachePatchName("M_CURSOR", PU_CACHE));
+		W_CachePatchName("M_CURSOR", PU_PATCH));
 }
 
 // special menuitem key handler for video mode list
@@ -11785,17 +11964,7 @@ static void M_HandleVideoMode(INT32 ch)
 
 static void M_DrawScreenshotMenu(void)
 {
-
 	M_DrawGenericScrollMenu();
-#ifdef HWRENDER
-	if ((rendermode == render_opengl) && (itemOn < 7)) // where it starts to go offscreen; change this number if you change the layout of the screenshot menu
-	{
-		INT32 y = currentMenu->y+currentMenu->menuitems[op_screenshot_colorprofile].alphaKey*2;
-		if (itemOn == 6)
-			y -= 10;
-		V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, y, V_REDMAP, "ON");
-	}
-#endif
 }
 
 // ===============
@@ -11880,7 +12049,7 @@ void M_QuitResponse(INT32 ch)
 		ptime = I_GetTime() + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds Tails 03-26-2001
 		while (ptime > I_GetTime())
 		{
-			V_DrawScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_CACHE)); // Demo 3 Quit Screen Tails 06-16-2001
+			V_DrawScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_PATCH)); // Demo 3 Quit Screen Tails 06-16-2001
 			I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001
 			I_Sleep();
 		}
@@ -11920,20 +12089,6 @@ static void M_OGL_DrawFogMenu(void)
 			my + currentMenu->menuitems[FOG_COLOR_ITEM].alphaKey, '_' | 0x80,false);
 }
 
-// =====================
-// M_OGL_DrawColorMenu()
-// =====================
-static void M_OGL_DrawColorMenu(void)
-{
-	INT32 mx, my;
-
-	mx = currentMenu->x;
-	my = currentMenu->y;
-	M_DrawGenericMenu(); // use generic drawer for cursor, items and title
-	V_DrawString(mx, my + currentMenu->menuitems[0].alphaKey - 10,
-		V_YELLOWMAP, "Gamma correction");
-}
-
 //===================
 // M_HandleFogColor()
 //===================
diff --git a/src/m_menu.h b/src/m_menu.h
index ce7198d7545e2462f894e76e1fe30aeced128269..48a4ba6f0c8346f530f0b4efa0c27cb6f21093bc 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2011-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -222,13 +222,14 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
 
 // flags for items in the menu
 // menu handle (what we do when key is pressed
-#define IT_TYPE             14     // (2+4+8)
+#define IT_TYPE             15     // (1+2+4+8)
 #define IT_CALL              0     // call the function
+#define IT_SPACE             1     // no handling
 #define IT_ARROWS            2     // call function with 0 for left arrow and 1 for right arrow in param
 #define IT_KEYHANDLER        4     // call with the key in param
 #define IT_SUBMENU           6     // go to sub menu
 #define IT_CVAR              8     // handle as a cvar
-#define IT_SPACE            10     // no handling
+#define IT_PAIR             11     // no handling, define both sides of text
 #define IT_MSGHANDLER       12     // same as key but with event and sometime can handle y/n key (special for message)
 
 #define IT_DISPLAY   (48+64+128)    // 16+32+64+128
@@ -255,8 +256,8 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
 #define IT_CV_NOPRINT     1536
 #define IT_CV_NOMOD       2048
 #define IT_CV_INVISSLIDER 2560
-#define IT_CV_INTEGERSTEP 4096			// if IT_CV_NORMAL and cvar is CV_FLOAT, modify it by 1 instead of 0.0625
-#define IT_CV_FLOATSLIDER	4608			// IT_CV_SLIDER, value modified by 0.0625 instead of 1 (for CV_FLOAT cvars)
+#define IT_CV_INTEGERSTEP 4096      // if IT_CV_NORMAL and cvar is CV_FLOAT, modify it by 1 instead of 0.0625
+#define IT_CV_FLOATSLIDER 4608      // IT_CV_SLIDER, value modified by 0.0625 instead of 1 (for CV_FLOAT cvars)
 
 //call/submenu specific
 // There used to be a lot more here but ...
@@ -371,6 +372,7 @@ typedef struct
 	UINT8 col[2];
 	char notes[441];
 } gtdesc_t;
+extern gtdesc_t gametypedesc[NUMGAMETYPES];
 
 // mode descriptions for video mode menu
 typedef struct
diff --git a/src/m_misc.c b/src/m_misc.c
index 5cfb20f0fed98578f370d365d4e62854a9c7d155..edb24ab1ec9ce3996f34eafad1bd1909663a2a28 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -789,7 +789,7 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png
 	if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl[0] != '\0')
 		snprintf(lvlttltext, 48, "%s%s%s",
 			mapheaderinfo[gamemap-1]->lvlttl,
-			(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE",
+			(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone",
 			(mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : "");
 	else
 		snprintf(lvlttltext, 48, "Unknown");
@@ -1161,12 +1161,8 @@ void M_StartMovie(void)
 	switch (cv_moviemode.value)
 	{
 		case MM_GIF:
-			if (rendermode == render_soft)
-			{
-				moviemode = M_StartMovieGIF(pathname);
-				break;
-			}
-			/* FALLTHRU */
+			moviemode = M_StartMovieGIF(pathname);
+			break;
 		case MM_APNG:
 			moviemode = M_StartMovieAPNG(pathname);
 			break;
@@ -1783,7 +1779,7 @@ char *M_GetToken(const char *inputString)
 			|| stringToUse[startPos] == '\r'
 			|| stringToUse[startPos] == '\n'
 			|| stringToUse[startPos] == '\0'
-			|| stringToUse[startPos] == '"' // we're treating this as whitespace because SLADE likes adding it for no good reason
+			|| stringToUse[startPos] == '=' || stringToUse[startPos] == ';' // UDMF TEXTMAP.
 			|| inComment != 0)
 			&& startPos < stringLength)
 	{
@@ -1841,6 +1837,23 @@ char *M_GetToken(const char *inputString)
 		texturesToken[1] = '\0';
 		return texturesToken;
 	}
+	// Return entire string within quotes, except without the quotes.
+	else if (stringToUse[startPos] == '"')
+	{
+		endPos = ++startPos;
+		while (stringToUse[endPos] != '"' && endPos < stringLength)
+			endPos++;
+
+		texturesTokenLength = endPos++ - startPos;
+		// Assign the memory. Don't forget an extra byte for the end of the string!
+		texturesToken = (char *)Z_Malloc((texturesTokenLength+1)*sizeof(char),PU_STATIC,NULL);
+		// Copy the string.
+		M_Memcpy(texturesToken, stringToUse+startPos, (size_t)texturesTokenLength);
+		// Make the final character NUL.
+		texturesToken[texturesTokenLength] = '\0';
+
+		return texturesToken;
+	}
 
 	// Now find the end of the token. This includes several additional characters that are okay to capture as one character, but not trailing at the end of another token.
 	endPos = startPos + 1;
@@ -1851,7 +1864,7 @@ char *M_GetToken(const char *inputString)
 			&& stringToUse[endPos] != ','
 			&& stringToUse[endPos] != '{'
 			&& stringToUse[endPos] != '}'
-			&& stringToUse[endPos] != '"' // see above
+			&& stringToUse[endPos] != '=' && stringToUse[endPos] != ';' // UDMF TEXTMAP.
 			&& inComment == 0)
 			&& endPos < stringLength)
 	{
@@ -1895,6 +1908,20 @@ void M_UnGetToken(void)
 	endPos = oldendPos;
 }
 
+/** Returns the current token's position.
+ */
+UINT32 M_GetTokenPos(void)
+{
+	return endPos;
+}
+
+/** Sets the current token's position.
+ */
+void M_SetTokenPos(UINT32 newPos)
+{
+	endPos = newPos;
+}
+
 /** Count bits in a number.
   */
 UINT8 M_CountBits(UINT32 num, UINT8 size)
@@ -2453,3 +2480,94 @@ const char *M_FileError(FILE *fp)
 	else
 		return "end-of-file";
 }
+
+/** Return the number of parts of this path.
+*/
+int M_PathParts(const char *path)
+{
+	int n;
+	const char *p;
+	const char *t;
+	if (path == NULL)
+		return 0;
+	for (n = 0, p = path ;; ++n)
+	{
+		t = p;
+		if (( p = strchr(p, PATHSEP[0]) ))
+			p += strspn(p, PATHSEP);
+		else
+		{
+			if (*t)/* there is something after the final delimiter */
+				n++;
+			break;
+		}
+	}
+	return n;
+}
+
+/** Check whether a path is an absolute path.
+*/
+boolean M_IsPathAbsolute(const char *path)
+{
+#ifdef _WIN32
+	return ( strncmp(&path[1], ":\\", 2) == 0 );
+#else
+	return ( path[0] == '/' );
+#endif
+}
+
+/** I_mkdir for each part of the path.
+*/
+void M_MkdirEachUntil(const char *cpath, int start, int end, int mode)
+{
+	char path[MAX_WADPATH];
+	char *p;
+	char *t;
+
+	if (end > 0 && end <= start)
+		return;
+
+	strlcpy(path, cpath, sizeof path);
+#ifdef _WIN32
+	if (strncmp(&path[1], ":\\", 2) == 0)
+		p = &path[3];
+	else
+#endif
+		p = path;
+
+	if (end > 0)
+		end -= start;
+
+	for (; start > 0; --start)
+	{
+		p += strspn(p, PATHSEP);
+		if (!( p = strchr(p, PATHSEP[0]) ))
+			return;
+	}
+	p += strspn(p, PATHSEP);
+	for (;;)
+	{
+		if (end > 0 && !--end)
+			break;
+
+		t = p;
+		if (( p = strchr(p, PATHSEP[0]) ))
+		{
+			*p = '\0';
+			I_mkdir(path, mode);
+			*p = PATHSEP[0];
+			p += strspn(p, PATHSEP);
+		}
+		else
+		{
+			if (*t)
+				I_mkdir(path, mode);
+			break;
+		}
+	}
+}
+
+void M_MkdirEach(const char *path, int start, int mode)
+{
+	M_MkdirEachUntil(path, start, -1, mode);
+}
diff --git a/src/m_misc.h b/src/m_misc.h
index c37865ff3e6eafec831e8c16f850694d576ec865..e0a73e0b7f9d30789931b671c2fe487bdf1910e1 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -96,6 +96,11 @@ void M_SetupMemcpy(void);
 
 const char *M_FileError(FILE *handle);
 
+int     M_PathParts      (const char *path);
+boolean M_IsPathAbsolute (const char *path);
+void    M_MkdirEach      (const char *path, int start, int mode);
+void    M_MkdirEachUntil (const char *path, int start, int end, int mode);
+
 // counting bits, for weapon ammo code, usually
 FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);
 
diff --git a/src/m_queue.c b/src/m_queue.c
index a3aa04871de0c444569ac237d78dc5b02eced8f7..d8cb97d07b16cb9f38609f72132fb253d337895f 100644
--- a/src/m_queue.c
+++ b/src/m_queue.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2003      by James Haley
-// Copyright (C) 2003-2018 by Sonic Team Junior.
+// Copyright (C) 2003-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_queue.h b/src/m_queue.h
index eebb21468e6651cc25338c357d3771eb451ec124..096b0e1bb98319b6a91eeba34f522d38b38cf985 100644
--- a/src/m_queue.h
+++ b/src/m_queue.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2003      by James Haley
-// Copyright (C) 2003-2018 by Sonic Team Junior.
+// Copyright (C) 2003-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_random.c b/src/m_random.c
index 3d46f76b725e8d10be5a34e3661aba8a32595e96..8a7b62b196ce8f944c66adf59232138692dbd0c1 100644
--- a/src/m_random.c
+++ b/src/m_random.c
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_random.h b/src/m_random.h
index 6187bcf1ce789dde3dbf7d66c8d3442c956df81a..fb14249bca4266cc42b4f8e4fa3db8c82b9d14f6 100644
--- a/src/m_random.h
+++ b/src/m_random.h
@@ -3,7 +3,7 @@
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
 // Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/m_swap.h b/src/m_swap.h
index 3b50dc623737435bda8bd04eac6b50511e9cb626..d7233cba574a6f08cb353662c0fdbffeab913078 100644
--- a/src/m_swap.h
+++ b/src/m_swap.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/mserv.c b/src/mserv.c
index cf636dfdfb74082fc219d312819a686fc8d6bb0f..ddabdb6c020f88fe9cc44116b426cbb04ed15567 100644
--- a/src/mserv.c
+++ b/src/mserv.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/mserv.h b/src/mserv.h
index 09cd4f0895f313afb9fa68549f4f52e001810e69..b851aed30fc4eadba1268c08bf4572e1e2133785 100644
--- a/src/mserv.h
+++ b/src/mserv.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_ceilng.c b/src/p_ceilng.c
index 5395f618c5e6dd8361b168bb9bd1c58101f650b3..7031c287d9b099752d90710a6f36896420a55425 100644
--- a/src/p_ceilng.c
+++ b/src/p_ceilng.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 26d079cb418c5eb5c4f59376fafc517872dff45f..ce201844ff74c9b093b591d52b2a052686c4e586 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -181,11 +181,9 @@ void A_SpawnObjectAbsolute(mobj_t *actor);
 void A_SpawnObjectRelative(mobj_t *actor);
 void A_ChangeAngleRelative(mobj_t *actor);
 void A_ChangeAngleAbsolute(mobj_t *actor);
-#ifdef ROTSPRITE
 void A_RollAngle(mobj_t *actor);
 void A_ChangeRollAngleRelative(mobj_t *actor);
 void A_ChangeRollAngleAbsolute(mobj_t *actor);
-#endif // ROTSPRITE
 void A_PlaySound(mobj_t *actor);
 void A_FindTarget(mobj_t *actor);
 void A_FindTracer(mobj_t *actor);
@@ -1731,6 +1729,7 @@ void A_HoodThink(mobj_t *actor)
 	// Target dangerously close to robohood, retreat then.
 	if ((dm < 256<<FRACBITS) && (abs(dz) < 128<<FRACBITS))
 	{
+		S_StartSound(actor, actor->info->attacksound);
 		P_SetMobjState(actor, actor->info->raisestate);
 		return;
 	}
@@ -2262,7 +2261,7 @@ void A_CrushclawLaunch(mobj_t *actor)
 		while (chain)
 		{
 			P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz);
-			chain->watertop = chain->z;
+			chain->movefactor = chain->z;
 			idx += dx;
 			idy += dy;
 			idz += dz;
@@ -2476,12 +2475,8 @@ void A_VultureBlast(mobj_t *actor)
 void A_VultureFly(mobj_t *actor)
 {
 	fixed_t speedmax = 18*FRACUNIT;
-	angle_t angledif = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
-	fixed_t dx = actor->target->x - actor->x;
-	fixed_t dy = actor->target->y - actor->y;
-	fixed_t dz = actor->target->z - actor->z;
-	fixed_t dxy = FixedHypot(dx, dy);
-	fixed_t dm;
+	angle_t angledif;
+	fixed_t dx, dy, dz, dxy, dm;
 	mobj_t *dust;
 	fixed_t momm;
 
@@ -2490,6 +2485,18 @@ void A_VultureFly(mobj_t *actor)
 		return;
 #endif
 
+	if (!actor->target || P_MobjWasRemoved(actor->target))
+	{
+		P_SetMobjState(actor, actor->info->spawnstate);
+		return;
+	}
+
+	angledif = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
+	dx = actor->target->x - actor->x;
+	dy = actor->target->y - actor->y;
+	dz = actor->target->z - actor->z;
+	dxy = FixedHypot(dx, dy);
+
 	if (leveltime % 4 == 0)
 		S_StartSound(actor, actor->info->activesound);
 
@@ -3333,7 +3340,7 @@ void A_SkullAttack(mobj_t *actor)
 		INT32 i, j;
 		static INT32 k;/* static for (at least) GCC 9.1 weirdness */
 		boolean allow;
-		angle_t testang;
+		angle_t testang = 0;
 
 		mobjinfo[MT_NULL].spawnstate = S_INVISIBLE;
 		mobjinfo[MT_NULL].flags = MF_NOGRAVITY|MF_NOTHINK|MF_NOCLIPTHING|MF_NOBLOCKMAP;
@@ -3449,8 +3456,8 @@ void A_BossZoom(mobj_t *actor)
 // Description: Spawns explosions and plays appropriate sounds around the defeated boss.
 //
 // var1:
-//		0 - Use movecount to spawn explosions evenly
-//		1 - Use P_Random to spawn explosions at complete random
+//		& 1 - Use P_Random to spawn explosions at complete random
+//		& 2 - Use entire vertical range of object to spawn
 // var2 = Object to spawn. Default is MT_SONIC3KBOSSEXPLODE.
 //
 void A_BossScream(mobj_t *actor)
@@ -3466,17 +3473,13 @@ void A_BossScream(mobj_t *actor)
 	if (LUA_CallAction("A_BossScream", actor))
 		return;
 #endif
-	switch (locvar1)
+	if (locvar1 & 1)
+		fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
+	else
 	{
-	default:
-	case 0:
 		actor->movecount += 4*16;
 		actor->movecount %= 360;
 		fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
-		break;
-	case 1:
-		fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
-		break;
 	}
 	x = actor->x + FixedMul(FINECOSINE(fa),actor->radius);
 	y = actor->y + FixedMul(FINESINE(fa),actor->radius);
@@ -3487,7 +3490,9 @@ void A_BossScream(mobj_t *actor)
 	else
 		explodetype = (mobjtype_t)locvar2;
 
-	if (actor->eflags & MFE_VERTICALFLIP)
+	if (locvar1 & 2)
+		z = actor->z + (P_RandomKey((actor->height - mobjinfo[explodetype].height)>>FRACBITS)<<FRACBITS);
+	else if (actor->eflags & MFE_VERTICALFLIP)
 		z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
 	else
 		z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
@@ -4040,7 +4045,6 @@ bossjustdie:
 	switch (mo->type)
 	{
 		case MT_BLACKEGGMAN:
-		case MT_CYBRAKDEMON:
 		{
 			mo->flags |= MF_NOCLIP;
 			mo->flags &= ~MF_SPECIAL;
@@ -4048,6 +4052,20 @@ bossjustdie:
 			S_StartSound(NULL, sfx_befall);
 			break;
 		}
+		case MT_CYBRAKDEMON:
+		{
+			mo->flags |= MF_NOCLIP;
+			mo->flags &= ~(MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
+
+			S_StartSound(NULL, sfx_bedie2);
+			P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_CYBRAKDEMON_VILE_EXPLOSION);
+			mo->z += P_MobjFlip(mo);
+			P_SetObjectMomZ(mo, 12*FRACUNIT, false);
+			S_StartSound(mo, sfx_bgxpld);
+			if (mo->spawnpoint && !(mo->spawnpoint->options & MTF_EXTRA))
+				P_InstaThrust(mo, R_PointToAngle2(0, 0, mo->x, mo->y), 14*FRACUNIT);
+			break;
+		}
 		case MT_KOOPA:
 		{
 			junk.tag = LE_KOOPA;
@@ -4965,7 +4983,7 @@ void A_ThrownRing(mobj_t *actor)
 				continue;
 
 			// Don't home in on teammates.
-			if (gametype == GT_CTF
+			if ((gametyperules & GTR_TEAMFLAGS)
 				&& actor->target->player->ctfteam == player->ctfteam)
 				continue;
 		}
@@ -5664,10 +5682,10 @@ void A_MinusPopup(mobj_t *actor)
 	S_StartSound(actor, sfx_s3k82);
 	for (i = 1; i <= num; i++)
 	{
-		mobj_t *rock = P_SpawnMobj(actor->x, actor->y, actor->z + actor->height/4, MT_ROCKCRUMBLE1);
+		mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1);
 		P_Thrust(rock, ani*i, FRACUNIT);
-		rock->momz = 3*FRACUNIT;
-		P_SetScale(rock, FRACUNIT/3);
+		P_SetObjectMomZ(rock, 3*FRACUNIT, false);
+		P_SetScale(rock, rock->scale/3);
 	}
 	P_RadiusAttack(actor, actor, 2*actor->radius, 0);
 	if (actor->tracer)
@@ -5681,11 +5699,12 @@ void A_MinusPopup(mobj_t *actor)
 // Description: If the minus hits the floor, dig back into the ground.
 //
 // var1 = State to switch to (if 0, use seestate).
-// var2 = unused
+// var2 = If not 0, spawn debris when hitting the floor.
 //
 void A_MinusCheck(mobj_t *actor)
 {
 	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
 
 #ifdef HAVE_BLUA
 	if (LUA_CallAction("A_MinusCheck", actor))
@@ -5696,6 +5715,18 @@ void A_MinusCheck(mobj_t *actor)
 	{
 		P_SetMobjState(actor, locvar1 ? (statenum_t)locvar1 : actor->info->seestate);
 		actor->flags = actor->info->flags;
+		if (locvar2)
+		{
+			INT32 i, num = 6;
+			angle_t ani = FixedAngle(FRACUNIT*360/num);
+			for (i = 1; i <= num; i++)
+			{
+				mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1);
+				P_Thrust(rock, ani*i, FRACUNIT);
+				P_SetObjectMomZ(rock, 3*FRACUNIT, false);
+				P_SetScale(rock, rock->scale/3);
+			}
+		}
 	}
 }
 
@@ -6558,7 +6589,7 @@ void A_OldRingExplode(mobj_t *actor) {
 
 		if (changecolor)
 		{
-			if (gametype != GT_CTF)
+			if (!(gametyperules & GTR_TEAMFLAGS))
 				mo->color = actor->target->color; //copy color
 			else if (actor->target->player->ctfteam == 2)
 				mo->color = skincolor_bluering;
@@ -6574,7 +6605,7 @@ void A_OldRingExplode(mobj_t *actor) {
 
 	if (changecolor)
 	{
-		if (gametype != GT_CTF)
+		if (!(gametyperules & GTR_TEAMFLAGS))
 			mo->color = actor->target->color; //copy color
 		else if (actor->target->player->ctfteam == 2)
 			mo->color = skincolor_bluering;
@@ -6589,7 +6620,7 @@ void A_OldRingExplode(mobj_t *actor) {
 
 	if (changecolor)
 	{
-		if (gametype != GT_CTF)
+		if (!(gametyperules & GTR_TEAMFLAGS))
 			mo->color = actor->target->color; //copy color
 		else if (actor->target->player->ctfteam == 2)
 			mo->color = skincolor_bluering;
@@ -8594,7 +8625,6 @@ void A_ChangeAngleAbsolute(mobj_t *actor)
 	actor->angle = FixedAngle(P_RandomRange(amin, amax));
 }
 
-#ifdef ROTSPRITE
 // Function: A_RollAngle
 //
 // Description: Changes the roll angle.
@@ -8630,16 +8660,10 @@ void A_RollAngle(mobj_t *actor)
 //
 void A_ChangeRollAngleRelative(mobj_t *actor)
 {
-	// Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of
-	//  getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result
-	//  rather than the ranges, so <0 and >360 work as possible values. -Red
 	INT32 locvar1 = var1;
 	INT32 locvar2 = var2;
-	//angle_t angle = (P_RandomByte()+1)<<24;
 	const fixed_t amin = locvar1*FRACUNIT;
 	const fixed_t amax = locvar2*FRACUNIT;
-	//const angle_t amin = FixedAngle(locvar1*FRACUNIT);
-	//const angle_t amax = FixedAngle(locvar2*FRACUNIT);
 #ifdef HAVE_BLUA
 	if (LUA_CallAction("A_ChangeRollAngleRelative", actor))
 		return;
@@ -8649,11 +8673,6 @@ void A_ChangeRollAngleRelative(mobj_t *actor)
 	if (amin > amax)
 		I_Error("A_ChangeRollAngleRelative: var1 is greater than var2");
 #endif
-/*
-	if (angle < amin)
-		angle = amin;
-	if (angle > amax)
-		angle = amax;*/
 
 	actor->rollangle += FixedAngle(P_RandomRange(amin, amax));
 }
@@ -8669,11 +8688,8 @@ void A_ChangeRollAngleAbsolute(mobj_t *actor)
 {
 	INT32 locvar1 = var1;
 	INT32 locvar2 = var2;
-	//angle_t angle = (P_RandomByte()+1)<<24;
 	const fixed_t amin = locvar1*FRACUNIT;
 	const fixed_t amax = locvar2*FRACUNIT;
-	//const angle_t amin = FixedAngle(locvar1*FRACUNIT);
-	//const angle_t amax = FixedAngle(locvar2*FRACUNIT);
 #ifdef HAVE_BLUA
 	if (LUA_CallAction("A_ChangeRollAngleAbsolute", actor))
 		return;
@@ -8683,15 +8699,9 @@ void A_ChangeRollAngleAbsolute(mobj_t *actor)
 	if (amin > amax)
 		I_Error("A_ChangeRollAngleAbsolute: var1 is greater than var2");
 #endif
-/*
-	if (angle < amin)
-		angle = amin;
-	if (angle > amax)
-		angle = amax;*/
 
 	actor->rollangle = FixedAngle(P_RandomRange(amin, amax));
 }
-#endif // ROTSPRITE
 
 // Function: A_PlaySound
 //
@@ -13346,7 +13356,7 @@ void A_Boss5MakeJunk(mobj_t *actor)
 {
 	INT32 locvar1 = var1;
 	INT32 locvar2 = var2;
-	mobj_t *broked;
+	mobj_t *broked = NULL;
 	angle_t ang;
 	INT32 i = ((locvar2 & 1) ? 8 : 1);
 #ifdef HAVE_BLUA
@@ -13581,12 +13591,12 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing)
 		}
 		else
 		{ //Player on the top of the tornado.
+			P_ResetPlayer(player);
 			thing->z = dustdevil->z + dustdevil->height;
 			thrust = 20 * FRACUNIT;
 			player->powers[pw_nocontrol] = 0;
 			S_StartSound(thing, sfx_wdjump);
 			P_SetPlayerMobjState(thing, S_PLAY_FALL);
-			player->pflags &= ~PF_JUMPED;
 		}
 
 		thing->momz = thrust;
@@ -14215,7 +14225,7 @@ void A_SaloonDoorSpawn(mobj_t *actor)
 	fixed_t c = FINECOSINE(fa)*locvar2;
 	fixed_t s = FINESINE(fa)*locvar2;
 	mobj_t *door;
-	mobjflag2_t ambush = (actor->flags & MF2_AMBUSH);
+	mobjflag2_t ambush = (actor->flags2 & MF2_AMBUSH);
 
 #ifdef HAVE_BLUA
 	if (LUA_CallAction("A_SaloonDoorSpawn", actor))
@@ -14590,6 +14600,9 @@ void A_RolloutRock(mobj_t *actor)
 
 	actor->frame = actor->reactiontime % maxframes; // set frame
 
+	if (!actor->tracer || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health)
+		actor->flags |= MF_PUSHABLE;
+
 	if (!(actor->flags & MF_PUSHABLE)) // if being ridden, don't disappear
 		actor->fuse = 0;
 	else if (!actor->fuse && actor->movecount == 1) // otherwise if rock has moved, set its fuse
@@ -14676,20 +14689,35 @@ void A_DragonWing(mobj_t *actor)
 void A_DragonSegment(mobj_t *actor)
 {
 	mobj_t *target = actor->target;
-	fixed_t dist = P_AproxDistance(P_AproxDistance(actor->x - target->x, actor->y - target->y), actor->z - target->z);
-	fixed_t radius = actor->radius + target->radius;
-	angle_t hangle = R_PointToAngle2(target->x, target->y, actor->x, actor->y);
-	angle_t zangle = R_PointToAngle2(0, target->z, dist, actor->z);
-	fixed_t hdist = P_ReturnThrustX(target, zangle, radius);
-	fixed_t xdist = P_ReturnThrustX(target, hangle, hdist);
-	fixed_t ydist = P_ReturnThrustY(target, hangle, hdist);
-	fixed_t zdist = P_ReturnThrustY(target, zangle, radius);
+	fixed_t dist;
+	fixed_t radius;
+	angle_t hangle;
+	angle_t zangle;
+	fixed_t hdist;
+	fixed_t xdist;
+	fixed_t ydist;
+	fixed_t zdist;
 
 	#ifdef HAVE_BLUA
 		if (LUA_CallAction("A_DragonSegment", actor))
 			return;
 	#endif
 
+	if (target == NULL || !target->health)
+	{
+		P_RemoveMobj(actor);
+		return;
+	}
+
+	dist = P_AproxDistance(P_AproxDistance(actor->x - target->x, actor->y - target->y), actor->z - target->z);
+	radius = actor->radius + target->radius;
+	hangle = R_PointToAngle2(target->x, target->y, actor->x, actor->y);
+	zangle = R_PointToAngle2(0, target->z, dist, actor->z);
+	hdist = P_ReturnThrustX(target, zangle, radius);
+	xdist = P_ReturnThrustX(target, hangle, hdist);
+	ydist = P_ReturnThrustY(target, hangle, hdist);
+	zdist = P_ReturnThrustY(target, zangle, radius);
+
 	actor->angle = hangle;
 	P_TeleportMove(actor, target->x + xdist, target->y + ydist, target->z + zdist);
 }
diff --git a/src/p_floor.c b/src/p_floor.c
index 423fc1b70a8e7da8fd63f5a98ca65a54c509d3f3..c23d469bc4fe5c8655b295886ab4f6dfa20b4ac3 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_inter.c b/src/p_inter.c
index a51a4e8e6c2e2aa2ceb03c28f753c9828b3caeab..71740822e6a7ed1b02903490298be939b448a47f 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -148,13 +148,17 @@ void P_ResetStarposts(void)
 //
 boolean P_CanPickupItem(player_t *player, boolean weapon)
 {
-	if (player->bot && weapon)
+	if (!player->mo || player->mo->health <= 0)
 		return false;
 
-	if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] < UINT16_MAX)
-		return false;
+	if (player->bot)
+	{
+		if (weapon)
+			return false;
+		return P_CanPickupItem(&players[consoleplayer], true); // weapon is true to prevent infinite recursion if p1 is bot - doesn't occur in vanilla, but may be relevant for mods
+	}
 
-	if (player->mo && player->mo->health <= 0)
+	if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] < UINT16_MAX)
 		return false;
 
 	return true;
@@ -621,7 +625,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 			P_AddPlayerScore(player, 1000);
 
-			if (gametype != GT_COOP || modeattacking) // score only?
+			if (!(gametyperules & GTR_SPECIALSTAGES) || modeattacking) // score only?
 			{
 				S_StartSound(toucher, sfx_chchng);
 				break;
@@ -1447,7 +1451,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if (player->starpostnum >= special->health)
 				return; // Already hit this post
 
-			if (cv_coopstarposts.value && gametype == GT_COOP && (netgame || multiplayer))
+			if (cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (netgame || multiplayer))
 			{
 				for (i = 0; i < MAXPLAYERS; i++)
 				{
@@ -1803,7 +1807,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			return;
 
 		case MT_MINECARTSPAWNER:
-			if (!player->bot && (special->fuse < TICRATE || player->powers[pw_carry] != CR_MINECART))
+			if (!player->bot && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART)
 			{
 				mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART);
 				P_SetTarget(&mcart->target, toucher);
@@ -1884,7 +1888,7 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 	char targetname[MAXPLAYERNAME+4];
 	char sourcename[MAXPLAYERNAME+4];
 
-	if (G_PlatformGametype())
+	if (!(gametyperules & (GTR_RINGSLINGER|GTR_HURTMESSAGES)))
 		return; // Not in coop, etc.
 
 	if (!player)
@@ -2089,7 +2093,7 @@ void P_CheckTimeLimit(void)
 	if (!(multiplayer || netgame))
 		return;
 
-	if (G_PlatformGametype())
+	if (!(gametyperules & GTR_TIMELIMIT))
 		return;
 
 	if (leveltime < timelimitintics)
@@ -2120,7 +2124,7 @@ void P_CheckTimeLimit(void)
 	}
 
 	//Optional tie-breaker for Match/CTF
-	else if (cv_overtime.value)
+	else if ((cv_overtime.value) && (gametyperules & GTR_OVERTIME))
 	{
 		INT32 playerarray[MAXPLAYERS];
 		INT32 tempplayer = 0;
@@ -2202,7 +2206,7 @@ void P_CheckPointLimit(void)
 	if (!(multiplayer || netgame))
 		return;
 
-	if (G_PlatformGametype())
+	if (!(gametyperules & GTR_POINTLIMIT))
 		return;
 
 	// pointlimit is nonzero, check if it's been reached by this player
@@ -2385,7 +2389,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 	{
 		if (metalrecording) // Ack! Metal Sonic shouldn't die! Cut the tape, end recording!
 			G_StopMetalRecording(true);
-		if (gametype == GT_MATCH // note, no team match suicide penalty
+		if ((gametyperules & GTR_DEATHPENALTY) // note, no team match suicide penalty
 			&& ((target == source) || (source == NULL && inflictor == NULL) || (source && !source->player)))
 		{ // Suicide penalty
 			if (target->player->score >= 50)
@@ -2477,9 +2481,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 
 					P_SetMobjState(scoremobj, scorestate);
 
-					// On ground? No chain starts.
-					if (source->player->powers[pw_invulnerability] || !P_IsObjectOnGround(source))
-						source->player->scoreadd = locscoreadd;
+					source->player->scoreadd = locscoreadd;
 				}
 			}
 
@@ -2519,9 +2521,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 		target->colorized = false;
 		G_GhostAddColor(GHC_NORMAL);
 
-		if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0))
+		if ((target->player->lives <= 1) && (netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value == 0))
 			;
-		else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != INFLIVES)
+		else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES)
 		 && G_GametypeUsesLives())
 		{
 			target->player->lives -= 1; // Lose a life Tails 03-11-2000
@@ -2529,7 +2531,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			if (target->player->lives <= 0) // Tails 03-14-2000
 			{
 				boolean gameovermus = false;
-				if ((netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value != 1))
+				if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value != 1))
 				{
 					INT32 i;
 					for (i = 0; i < MAXPLAYERS; i++)
@@ -2639,6 +2641,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 				mo = P_SpawnMobj(target->x, target->y, target->z, MT_EXTRALARGEBUBBLE);
 			mo->destscale = target->scale;
 			P_SetScale(mo, mo->destscale);
+			P_SetMobjState(mo, mo->info->raisestate);
 			break;
 
 		case MT_YELLOWSHELL:
@@ -2814,13 +2817,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 			if (flip)
 				momz *= -1;
 #define makechunk(angtweak, xmov, ymov) \
-			chunk = P_SpawnMobj(target->x, target->y, target->z, MT_SPIKE);\
-			chunk->eflags |= flip;\
+			chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE);\
 			P_SetMobjState(chunk, target->info->xdeathstate);\
 			chunk->health = 0;\
 			chunk->angle = angtweak;\
-			chunk->destscale = scale;\
-			P_SetScale(chunk, scale);\
 			P_UnsetThingPosition(chunk);\
 			chunk->flags = MF_NOCLIP;\
 			chunk->x += xmov;\
@@ -2839,14 +2839,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 		if (flip)
 			momz *= -1;
 
-		chunk = P_SpawnMobj(target->x, target->y, target->z, MT_SPIKE);
-		chunk->eflags |= flip;
-
+		chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE);
 		P_SetMobjState(chunk, target->info->deathstate);
 		chunk->health = 0;
 		chunk->angle = ang + ANGLE_180;
-		chunk->destscale = scale;
-		P_SetScale(chunk, scale);
 		P_UnsetThingPosition(chunk);
 		chunk->flags = MF_NOCLIP;
 		chunk->x -= xoffs;
@@ -2889,13 +2885,10 @@ 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_SpawnMobj(target->x, target->y, target->z, MT_WALLSPIKE);\
-			chunk->eflags |= flip;\
+			chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);\
 			P_SetMobjState(chunk, target->info->xdeathstate);\
 			chunk->health = 0;\
 			chunk->angle = target->angle;\
-			chunk->destscale = scale;\
-			P_SetScale(chunk, scale);\
 			P_UnsetThingPosition(chunk);\
 			chunk->flags = MF_NOCLIP;\
 			chunk->x += xmov - forwardxoffs;\
@@ -2917,14 +2910,11 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 
 		sprflip = P_RandomChance(FRACUNIT/2);
 
-		chunk = P_SpawnMobj(target->x, target->y, target->z, MT_WALLSPIKE);
-		chunk->eflags |= flip;
+		chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);
 
 		P_SetMobjState(chunk, target->info->deathstate);
 		chunk->health = 0;
 		chunk->angle = target->angle;
-		chunk->destscale = scale;
-		P_SetScale(chunk, scale);
 		P_UnsetThingPosition(chunk);
 		chunk->flags = MF_NOCLIP;
 		chunk->x += forwardxoffs - xoffs;
@@ -2970,7 +2960,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 	   Graue 12-22-2003 */
 }
 
-static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
+static void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 {
 	player_t *player = target->player;
 	tic_t oldnightstime = player->nightstime;
@@ -2986,7 +2976,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 		player->flyangle += 180; // Shuffle's BETTERNIGHTSMOVEMENT?
 		player->flyangle %= 360;
 
-		if (gametype == GT_RACE || gametype == GT_COMPETITION)
+		if (gametyperules & GTR_RACE)
 			player->drillmeter -= 5*20;
 		else
 		{
@@ -3013,9 +3003,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 		P_SetPlayerMobjState(target, S_PLAY_NIGHTS_STUN);
 		S_StartSound(target, sfx_nghurt);
 
-#ifdef ROTSPRITE
 		player->mo->rollangle = 0;
-#endif
 
 		if (oldnightstime > 10*TICRATE
 			&& player->nightstime < 10*TICRATE)
@@ -3036,7 +3024,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 	}
 }
 
-static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
+static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
 	player_t *player = target->player;
 	(void)damage; //unused parm
@@ -3050,7 +3038,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 		return false;
 
 	// Ignore IT players shooting each other, unless friendlyfire is on.
-	if ((player->pflags & PF_TAGIT && !((cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) &&
+	if ((player->pflags & PF_TAGIT && !((cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE) || (damagetype & DMG_CANHURTSELF)) &&
 		source && source->player && source->player->pflags & PF_TAGIT)))
 	{
 		if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
@@ -3066,7 +3054,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 
 	// Don't allow players on the same team to hurt one another,
 	// unless cv_friendlyfire is on.
-	if (!(cv_friendlyfire.value || (damagetype & DMG_CANHURTSELF)) && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT))
+	if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE) || (damagetype & DMG_CANHURTSELF)) && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT))
 	{
 		if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
 		{
@@ -3140,7 +3128,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 	return true;
 }
 
-static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
+static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
 	player_t *player = target->player;
 
@@ -3151,7 +3139,7 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj
 			return false;
 
 		// In COOP/RACE, you can't hurt other players unless cv_friendlyfire is on
-		if (!cv_friendlyfire.value && (G_PlatformGametype()))
+		if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) && (G_PlatformGametype()))
 		{
 			if (gametype == GT_COOP && inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) // co-op only
 			{
@@ -3174,7 +3162,7 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj
 	{
 		// Don't allow players on the same team to hurt one another,
 		// unless cv_friendlyfire is on.
-		if (!cv_friendlyfire.value && target->player->ctfteam == source->player->ctfteam)
+		if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) && target->player->ctfteam == source->player->ctfteam)
 		{
 			if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
 			{
@@ -3211,10 +3199,12 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
 	player->powers[pw_carry] = CR_NONE;
 
 	// Burst weapons and emeralds in Match/CTF only
-	if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF))
+	if (source)
 	{
-		P_PlayerRingBurst(player, player->rings);
-		P_PlayerEmeraldBurst(player, false);
+		if ((gametyperules & GTR_RINGSLINGER) && !(gametyperules & GTR_TAG))
+			P_PlayerRingBurst(player, player->rings);
+		if (gametyperules & GTR_POWERSTONES)
+			P_PlayerEmeraldBurst(player, false);
 	}
 
 	// Get rid of shield
@@ -3232,7 +3222,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
 		player->mo->flags2 &= ~MF2_DONTDRAW;
 
 	P_SetPlayerMobjState(player->mo, player->mo->info->deathstate);
-	if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
+	if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
 	{
 		P_PlayerFlagBurst(player, false);
 		if (source && source->player)
@@ -3357,7 +3347,7 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
 	else
 		S_StartSound (player->mo, sfx_shldls); // Ba-Dum! Shield loss.
 
-	if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
+	if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
 	{
 		P_PlayerFlagBurst(player, false);
 		if (source && source->player)
@@ -3391,7 +3381,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
 			P_AddPlayerScore(source->player, 50);
 	}
 
-	if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
+	if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
 	{
 		P_PlayerFlagBurst(player, false);
 		if (source && source->player)
@@ -3434,6 +3424,24 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
 	if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super])
 		return;
 
+	if (!cv_friendlyfire.value)
+	{
+		if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
+		{
+			if (player->revitem != MT_LHRT && player->spinitem != MT_LHRT && player->thokitem != MT_LHRT) // Healers do not get to heal other healers.
+			{
+				P_SwitchShield(player, SH_PINK);
+				S_StartSound(player->mo, mobjinfo[MT_PITY_ICON].seesound);
+			}
+		}
+
+		if (source->player->ctfteam == player->ctfteam)
+			return;
+	}
+
+	if (inflictor->type == MT_LHRT)
+		return;
+
 	if (player->powers[pw_shield] || player->bot)  //If One-Hit Shield
 	{
 		P_RemoveShield(player);
@@ -3450,7 +3458,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
 
 	P_DoPlayerPain(player, inflictor, source);
 
-	if (gametype == GT_CTF && player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))
+	if ((gametyperules & GTR_TEAMFLAGS) && player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))
 		P_PlayerFlagBurst(player, false);
 
 	if (oldnightstime > 10*TICRATE
@@ -3601,7 +3609,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			{
 				if (source == target)
 					return false; // Don't hit yourself with your own paraloop, baka
-				if (source && source->player && !cv_friendlyfire.value
+				if (source && source->player && !(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE))
 				&& (gametype == GT_COOP
 				|| (G_GametypeHasTeams() && player->ctfteam == source->player->ctfteam)))
 					return false; // Don't run eachother over in special stages and team games and such
@@ -3696,7 +3704,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 		// by friendly fire. Spilling their rings and other items is enough.
 		else if (!force && G_GametypeHasTeams()
 			&& source && source->player && (source->player->ctfteam == player->ctfteam)
-			&& cv_friendlyfire.value)
+			&& (cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)))
 		{
 			damage = 0;
 			P_ShieldDamage(player, inflictor, source, damage, damagetype);
diff --git a/src/p_lights.c b/src/p_lights.c
index 8459d9ea044d066e5ba02c37b2717d549849b9ce..7de6a4dc5570c8677e9dc262b25e3186fcf51a0b 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_local.h b/src/p_local.h
index b718c43f194ab9d1bc5850a7a8f2f5ea1b9c2487..88deb0942ffa0a49b6244873edc59e7c87f8c030 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -115,10 +115,10 @@ typedef struct camera_s
 
 extern camera_t camera, camera2;
 extern consvar_t cv_cam_dist, cv_cam_still, cv_cam_height;
-extern consvar_t cv_cam_speed, cv_cam_rotate, cv_cam_rotspeed, cv_cam_orbit, cv_cam_adjust;
+extern consvar_t cv_cam_speed, cv_cam_rotate, cv_cam_rotspeed, cv_cam_turnmultiplier, cv_cam_orbit, cv_cam_adjust;
 
 extern consvar_t cv_cam2_dist, cv_cam2_still, cv_cam2_height;
-extern consvar_t cv_cam2_speed, cv_cam2_rotate, cv_cam2_rotspeed, cv_cam2_orbit, cv_cam2_adjust;
+extern consvar_t cv_cam2_speed, cv_cam2_rotate, cv_cam2_rotspeed, cv_cam2_turnmultiplier, cv_cam2_orbit, cv_cam2_adjust;
 
 extern fixed_t t_cam_dist, t_cam_height, t_cam_rotate;
 extern fixed_t t_cam2_dist, t_cam2_height, t_cam2_rotate;
@@ -323,6 +323,7 @@ SINT8 P_MobjFlip(mobj_t *mobj);
 fixed_t P_GetMobjGravity(mobj_t *mo);
 FUNCMATH boolean P_WeaponOrPanel(mobjtype_t type);
 
+void P_CalcChasePostImg(player_t *player, camera_t *thiscam);
 boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled);
 
 void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab);
diff --git a/src/p_map.c b/src/p_map.c
index 753ce9eb5f299f823740c928b34d9405e07a052f..c468e5b3c330f8ac885e61c36420749b1062ec27 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -426,7 +426,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 				object->player->pflags |= pflags;
 				object->player->secondjump = secondjump;
 			}
-			else if (object->player->dashmode >= 3*TICRATE)
+			else if (object->player->dashmode >= DASHMODE_THRESHOLD)
 				P_SetPlayerMobjState(object, S_PLAY_DASH);
 			else if (P_IsObjectOnGround(object) && horizspeed >= FixedMul(object->player->runspeed, object->scale))
 				P_SetPlayerMobjState(object, S_PLAY_RUN);
@@ -615,7 +615,7 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
 	// Why block opposing teams from tailsflying each other?
 	// Sneaking into the hands of a flying tails player in Race might be a viable strategy, who knows.
 	/*
-	if (gametype == GT_RACE || gametype == GT_COMPETITION
+	if ((gametyperules & GTR_RACE)
 		|| (netgame && (tails->spectator || sonic->spectator))
 		|| (G_TagGametype() && (!(tails->pflags & PF_TAGIT) != !(sonic->pflags & PF_TAGIT)))
 		|| (gametype == GT_MATCH)
@@ -745,9 +745,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
   // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
 	if (tmthing->type == MT_NAMECHECK)
 	{
-	  // Ignore things that aren't players, ignore spectators, ignore yourself.
-		// (also don't bother to check that tmthing->target->player is non-NULL because we're not actually using it here.)
-		if (!thing->player || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
+		// Ignore things that aren't players, ignore spectators, ignore yourself.
+		if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
 			return true;
 
 		// Now check that you actually hit them.
@@ -760,6 +759,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (tmthing->z + tmthing->height < thing->z)
 			return true; // underneath
 
+#ifdef HAVE_BLUA
+		// REX HAS SEEN YOU
+		if (!LUAh_SeenPlayer(tmthing->target->player, thing->player))
+			return false;
+#endif
+
 		seenplayer = thing->player;
 		return false;
 	}
@@ -784,12 +789,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (thing->type == MT_SPIKE
 		|| thing->type == MT_WALLSPIKE)
 		{
-			mobjtype_t type = thing->type;
+			mobj_t *iter;
 			if (thing->flags & MF_SOLID)
 				S_StartSound(tmthing, thing->info->deathsound);
-			for (thing = thing->subsector->sector->thinglist; thing; thing = thing->snext)
-				if (thing->type == type && thing->health > 0 && thing->flags & MF_SOLID && P_AproxDistance(P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y), thing->z - tmthing->z) < 56*thing->scale)//FixedMul(56*FRACUNIT, thing->scale))
-					P_KillMobj(thing, tmthing, tmthing, 0);
+			for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
+				if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
+					P_KillMobj(iter, tmthing, tmthing, 0);
 		}
 		else
 		{
@@ -801,7 +806,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 	// SF_DASHMODE users destroy spikes and monitors, CA_TWINSPIN users and CA2_MELEE users destroy spikes.
 	if ((tmthing->player)
-		&& (((tmthing->player->charflags & SF_DASHMODE) && (tmthing->player->dashmode >= 3*TICRATE)
+		&& ((((tmthing->player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE)) && (tmthing->player->dashmode >= DASHMODE_THRESHOLD)
 		&& (thing->flags & (MF_MONITOR)
 		|| (thing->type == MT_SPIKE
 		|| thing->type == MT_WALLSPIKE)))
@@ -823,19 +828,19 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (thing->type == MT_SPIKE
 		|| thing->type == MT_WALLSPIKE)
 		{
-			mobjtype_t type = thing->type;
+			mobj_t *iter;
 			if (thing->flags & MF_SOLID)
 				S_StartSound(tmthing, thing->info->deathsound);
-			for (thing = thing->subsector->sector->thinglist; thing; thing = thing->snext)
-				if (thing->type == type && thing->health > 0 && thing->flags & MF_SOLID && P_AproxDistance(P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y), thing->z - tmthing->z) < 56*thing->scale)//FixedMul(56*FRACUNIT, thing->scale))
-					P_KillMobj(thing, tmthing, tmthing, 0);
+			for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
+				if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
+					P_KillMobj(iter, tmthing, tmthing, 0);
+			return true;
 		}
 		else
 		{
-			thing->health = 0;
-			P_KillMobj(thing, tmthing, tmthing, 0);
+			if (P_DamageMobj(thing, tmthing, tmthing, 1, 0))
+				return true;
 		}
-		return true;
 	}
 
 	// vectorise metal - done in a special case as at this point neither has the right flags for touching
@@ -995,7 +1000,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (thing->type == MT_SALOONDOOR && tmthing->player)
 	{
 		mobj_t *ref = (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)) ? tmthing->tracer : tmthing;
-		if ((thing->flags & MF2_AMBUSH) || ref != tmthing)
+		if ((thing->flags2 & MF2_AMBUSH) || ref != tmthing)
 		{
 			fixed_t dm = min(FixedHypot(ref->momx, ref->momy), 16*FRACUNIT);
 			angle_t ang = R_PointToAngle2(0, 0, ref->momx, ref->momy) - thing->angle;
@@ -1008,7 +1013,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 	if (thing->type == MT_SALOONDOORCENTER && tmthing->player)
 	{
-		if ((thing->flags & MF2_AMBUSH) || (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)))
+		if ((thing->flags2 & MF2_AMBUSH) || (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)))
 			return true;
 	}
 
@@ -1303,11 +1308,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			return false;
 		}
 
-		// Fireball touched an enemy
-		// Don't bounce though, just despawn right there
-		if ((tmthing->type == MT_FIREBALL) && (thing->flags & MF_ENEMY))
-			P_KillMobj(tmthing, NULL, NULL, 0);
-
 		// damage / explode
 		if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example)
 			P_DamageMobj(thing, tmthing, tmthing, 1, 0);
@@ -1356,6 +1356,11 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			P_DamageMobj(thing, tmthing, tmthing->target, 1, damagetype);
 		}
 
+		// Fireball touched an enemy
+		// Don't bounce though, just despawn right there
+		if ((tmthing->type == MT_FIREBALL) && (thing->flags & MF_ENEMY))
+			P_KillMobj(tmthing, NULL, NULL, 0);
+
 		// don't traverse any more
 
 		if (tmthing->type == MT_SHELL)
@@ -1719,8 +1724,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		}
 	}
 
-	if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM) && (thing->player))
-		; // springs and gas jets should never be able to step up onto a player
+	if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
+		; // springs, gas jets and springs should never be able to step up onto a player
 	// z checking at last
 	// Treat noclip things as non-solid!
 	else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
@@ -1943,6 +1948,19 @@ static boolean PIT_CheckLine(line_t *ld)
 
 	// this line is out of the if so upper and lower textures can be hit by a splat
 	blockingline = ld;
+
+#ifdef HAVE_BLUA
+	{
+		UINT8 shouldCollide = LUAh_MobjLineCollide(tmthing, blockingline); // checks hook for thing's type
+		if (P_MobjWasRemoved(tmthing))
+			return true; // one of them was removed???
+		if (shouldCollide == 1)
+			return false; // force collide
+		else if (shouldCollide == 2)
+			return true; // force no collide
+	}
+#endif
+
 	if (!ld->backsector) // one sided line
 	{
 		if (P_PointOnLineSide(tmthing->x, tmthing->y, ld))
@@ -1987,7 +2005,7 @@ static boolean PIT_CheckLine(line_t *ld)
 
 	if (lowfloor < tmdropoffz)
 		tmdropoffz = lowfloor;
-
+	
 	return true;
 }
 
@@ -3758,6 +3776,33 @@ void P_SlideMove(mobj_t *mo)
 			v2.x = tmhitthing->x + cosradius;
 			v2.y = tmhitthing->y + sinradius;
 
+			// Can we box collision our way into smooth movement..?
+			if (sinradius && mo->y + mo->radius <= min(v1.y, v2.y))
+			{
+				mo->momy = 0;
+				P_TryMove(mo, mo->x + mo->momx, min(v1.y, v2.y) - mo->radius, true);
+				return;
+			}
+			else if (sinradius && mo->y - mo->radius >= max(v1.y, v2.y))
+			{
+				mo->momy = 0;
+				P_TryMove(mo, mo->x + mo->momx, max(v1.y, v2.y) + mo->radius, true);
+				return;
+			}
+			else if (cosradius && mo->x + mo->radius <= min(v1.x, v2.x))
+			{
+				mo->momx = 0;
+				P_TryMove(mo, min(v1.x, v2.x) - mo->radius, mo->y + mo->momy, true);
+				return;
+			}
+			else if (cosradius && mo->x - mo->radius >= max(v1.x, v2.x))
+			{
+				mo->momx = 0;
+				P_TryMove(mo, max(v1.x, v2.x) + mo->radius, mo->y + mo->momy, true);
+				return;
+			}
+
+			// nope, gotta fuck around with a fake linedef!
 			junk.v1 = &v1;
 			junk.v2 = &v2;
 			junk.dx = 2*cosradius; // v2.x - v1.x;
diff --git a/src/p_maputl.c b/src/p_maputl.c
index 111103294c659d38b8286a36597fa9c97d76f951..afa0205042a5d6b9394660b48a24cbc2ee2aa805 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -78,68 +78,37 @@ void P_ClosestPointOnLine(fixed_t x, fixed_t y, line_t *line, vertex_t *result)
 	return;
 }
 
-//
-// P_ClosestPointOnLine3D
-// Finds the closest point on a given line to the supplied point IN 3D!!!
-//
-void P_ClosestPointOnLine3D(fixed_t x, fixed_t y, fixed_t z, line_t *line, vertex_t *result)
+/// Similar to FV3_ClosestPointOnLine() except it actually works.
+void P_ClosestPointOnLine3D(const vector3_t *p, const vector3_t *Line, vector3_t *result)
 {
-	fixed_t startx = line->v1->x;
-	fixed_t starty = line->v1->y;
-	fixed_t startz = line->v1->z;
-	fixed_t dx = line->dx;
-	fixed_t dy = line->dy;
-	fixed_t dz = line->v2->z - line->v1->z;
+	const vector3_t* v1 = &Line[0];
+	const vector3_t* v2 = &Line[1];
+	vector3_t c, V, n;
+	fixed_t t, d;
+	FV3_SubEx(v2, v1, &V);
+	FV3_SubEx(p, v1, &c);
 
-	// Determine t (the length of the vector from �Line[0]� to �p�)
-	fixed_t cx, cy, cz;
-	fixed_t vx, vy, vz;
-	fixed_t magnitude;
-	fixed_t t;
+	d = R_PointToDist2(0, v2->z, R_PointToDist2(v2->x, v2->y, v1->x, v1->y), v1->z);
+	FV3_Copy(&n, &V);
+	FV3_Divide(&n, d);
 
-	//Sub (p, &Line[0], &c);
-	cx = x - startx;
-	cy = y - starty;
-	cz = z - startz;
-
-	//Sub (&Line[1], &Line[0], &V);
-	vx = dx;
-	vy = dy;
-	vz = dz;
-
-	//Normalize (&V, &V);
-	magnitude = R_PointToDist2(0, line->v2->z, R_PointToDist2(line->v2->x, line->v2->y, startx, starty), startz);
-	vx = FixedDiv(vx, magnitude);
-	vy = FixedDiv(vy, magnitude);
-	vz = FixedDiv(vz, magnitude);
-
-	t = (FixedMul(vx, cx) + FixedMul(vy, cy) + FixedMul(vz, cz));
+	t = FV3_Dot(&n, &c);
 
 	// Set closest point to the end if it extends past -Red
 	if (t <= 0)
 	{
-		result->x = line->v1->x;
-		result->y = line->v1->y;
-		result->z = line->v1->z;
+		FV3_Copy(result, v1);
 		return;
 	}
-	else if (t >= magnitude)
+	else if (t >= d)
 	{
-		result->x = line->v2->x;
-		result->y = line->v2->y;
-		result->z = line->v2->z;
+		FV3_Copy(result, v2);
 		return;
 	}
 
-	// Return the point between �Line[0]� and �Line[1]�
-	vx = FixedMul(vx, t);
-	vy = FixedMul(vy, t);
-	vz = FixedMul(vz, t);
+	FV3_Mul(&n, t);
 
-	//Add (&Line[0], &V, out);
-	result->x = startx + vx;
-	result->y = starty + vy;
-	result->z = startz + vz;
+	FV3_AddEx(v1, &n, result);
 	return;
 }
 
diff --git a/src/p_maputl.h b/src/p_maputl.h
index 5042817c54ec8a13112626a75ea330ab3c612549..16cfc834e0e0341f5e93fb0dd090828700ef6c14 100644
--- a/src/p_maputl.h
+++ b/src/p_maputl.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -43,7 +43,7 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
 
 FUNCMATH fixed_t P_AproxDistance(fixed_t dx, fixed_t dy);
 void P_ClosestPointOnLine(fixed_t x, fixed_t y, line_t *line, vertex_t *result);
-void P_ClosestPointOnLine3D(fixed_t x, fixed_t y, fixed_t z, line_t *line, vertex_t *result);
+void P_ClosestPointOnLine3D(const vector3_t *p, const vector3_t *line, vector3_t *result);
 INT32 P_PointOnLineSide(fixed_t x, fixed_t y, line_t *line);
 void P_MakeDivline(line_t *li, divline_t *dl);
 void P_CameraLineOpening(line_t *plinedef);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index f2dd1a734f07a77b0180a2c0e2d610d49798d9a7..b771103a9c4c4030ba117559eb14f6fb6880de55 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -255,7 +255,6 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 	case S_PLAY_WALK:
 	case S_PLAY_SKID:
 	case S_PLAY_FLOAT:
-	case S_PLAY_NIGHTS_FLOAT:
 		player->panim = PA_WALK;
 		break;
 	case S_PLAY_RUN:
@@ -281,6 +280,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 		player->panim = PA_SPRING;
 		break;
 	case S_PLAY_FALL:
+	case S_PLAY_NIGHTS_FLOAT:
 		player->panim = PA_FALL;
 		break;
 	case S_PLAY_FLY:
@@ -1572,6 +1572,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
 					}
 					break;
 				case MT_WATERDROP:
+				case MT_CYBRAKDEMON:
 					gravityadd >>= 1;
 				default:
 					break;
@@ -1996,7 +1997,7 @@ void P_XYMovement(mobj_t *mo)
 				{
 					mo->momz = transfermomz;
 					mo->standingslope = NULL;
-					if (player->pflags & PF_SPINNING)
+					if (player && (player->pflags & PF_SPINNING))
 						player->pflags |= PF_THOKKED;
 				}
 			}
@@ -3297,7 +3298,7 @@ boolean P_CanRunOnWater(player_t *player, ffloor_t *rover)
 		*rover->topheight;
 
 	if (!player->powers[pw_carry] && !player->homing
-		&& ((player->powers[pw_super] || player->charflags & SF_RUNONWATER || player->dashmode >= 3*TICRATE) && player->mo->ceilingz-topheight >= player->mo->height)
+		&& ((player->powers[pw_super] || player->charflags & SF_RUNONWATER || player->dashmode >= DASHMODE_THRESHOLD) && player->mo->ceilingz-topheight >= player->mo->height)
 		&& (rover->flags & FF_SWIMMABLE) && !(player->pflags & PF_SPINNING) && player->speed > FixedMul(player->runspeed, player->mo->scale)
 		&& !(player->pflags & PF_SLIDING)
 		&& abs(player->mo->z - topheight) < FixedMul(30*FRACUNIT, player->mo->scale))
@@ -3408,7 +3409,7 @@ void P_MobjCheckWater(mobj_t *mobj)
 
 		// Drown timer setting
 		if ((p->powers[pw_shield] & SH_PROTECTWATER) // Has water protection
-		 || (p->exiting) // Or exiting
+		 || (p->exiting) || (p->pflags & PF_FINISHED) // Or finished/exiting
 		 || (maptol & TOL_NIGHTS) // Or in NiGHTS mode
 		 || (mariomode)) // Or in Mario mode...
 		{
@@ -3724,17 +3725,10 @@ void P_DestroyRobots(void)
 	}
 }
 
-// P_CameraThinker
-//
-// Process the mobj-ish required functions of the camera
-boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled)
+// the below is chasecam only, if you're curious. check out P_CalcPostImg in p_user.c for first person
+void P_CalcChasePostImg(player_t *player, camera_t *thiscam)
 {
-	boolean itsatwodlevel = false;
 	postimg_t postimg = postimg_none;
-	if (twodlevel
-		|| (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
-		|| (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
-		itsatwodlevel = true;
 
 	if (player->pflags & PF_FLIPCAM && !(player->powers[pw_carry] == CR_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP)
 		postimg = postimg_flip;
@@ -3762,13 +3756,27 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 			postimg = postimg_heat;
 	}
 
-	if (postimg != postimg_none)
-	{
-		if (splitscreen && player == &players[secondarydisplayplayer])
-			postimgtype2 = postimg;
-		else
-			postimgtype = postimg;
-	}
+	if (postimg == postimg_none)
+		return;
+
+	if (splitscreen && player == &players[secondarydisplayplayer])
+		postimgtype2 = postimg;
+	else
+		postimgtype = postimg;
+}
+
+// P_CameraThinker
+//
+// Process the mobj-ish required functions of the camera
+boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled)
+{
+	boolean itsatwodlevel = false;
+	if (twodlevel
+		|| (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
+		|| (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
+		itsatwodlevel = true;
+
+	P_CalcChasePostImg(player, thiscam);
 
 	if (thiscam->momx || thiscam->momy)
 	{
@@ -3782,10 +3790,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 			dummy.z = thiscam->z;
 			dummy.height = thiscam->height;
 			if (!resetcalled && !(player->pflags & PF_NOCLIP) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead.
-			{
 				P_ResetCamera(player, thiscam);
-				resetcalled = true;
-			}
 			else
 			{
 				fixed_t camspeed = P_AproxDistance(thiscam->momx, thiscam->momy);
@@ -3793,10 +3798,10 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
 				P_SlideCameraMove(thiscam);
 
 				if (!resetcalled && P_AproxDistance(thiscam->momx, thiscam->momy) == camspeed)
-					{
-						P_ResetCamera(player, thiscam);
-						resetcalled = true;
-					}
+				{
+					P_ResetCamera(player, thiscam);
+					resetcalled = true;
+				}
 			}
 			if (resetcalled) // Okay this means the camera is fully reset.
 				return true;
@@ -4644,7 +4649,7 @@ static void P_Boss3Thinker(mobj_t *mobj)
 			{
 				UINT8 i, numtospawn = 24;
 				angle_t ang = 0, interval = FixedAngle((360 << FRACBITS) / numtospawn);
-				mobj_t *shock, *sfirst, *sprev = NULL;
+				mobj_t *shock = NULL, *sfirst = NULL, *sprev = NULL;
 
 				mobj->movecount = mobj->health+1;
 				mobj->movefactor = -512*FRACUNIT;
@@ -7082,7 +7087,7 @@ static void P_SpawnMinecartSegments(mobj_t *mobj, boolean mode)
 		seg = P_SpawnMobj(x, y, z, MT_MINECARTSEG);
 		P_SetMobjState(seg, (statenum_t)(S_MINECARTSEG_FRONT + i));
 		if (i >= 2)
-			seg->extravalue1 = (i == 2) ? -18 : 18;
+			seg->extravalue1 = (i == 2) ? -18 : 18; // make -20/20 when papersprite projection fixed
 		else
 		{
 			seg->extravalue2 = (i == 0) ? 24 : -24;
@@ -7138,2873 +7143,3051 @@ static void P_PyreFlyBurn(mobj_t *mobj, fixed_t hoffs, INT16 vrange, mobjtype_t
 	particle->momz = momz;
 }
 
-//
-// P_MobjThinker
-//
-void P_MobjThinker(mobj_t *mobj)
+static void P_MobjScaleThink(mobj_t *mobj)
 {
-	I_Assert(mobj != NULL);
-	I_Assert(!P_MobjWasRemoved(mobj));
-
-	if (mobj->flags & MF_NOTHINK)
-		return;
-
-	if ((mobj->flags & MF_BOSS) && mobj->spawnpoint && (bossdisabled & (1<<mobj->spawnpoint->extrainfo)))
-		return;
+	fixed_t oldheight = mobj->height;
+	UINT8 correctionType = 0; // Don't correct Z position, just gain height
 
-	// Remove dead target/tracer.
-	if (mobj->target && P_MobjWasRemoved(mobj->target))
-		P_SetTarget(&mobj->target, NULL);
-	if (mobj->tracer && P_MobjWasRemoved(mobj->tracer))
-		P_SetTarget(&mobj->tracer, NULL);
-	if (mobj->hnext && P_MobjWasRemoved(mobj->hnext))
-		P_SetTarget(&mobj->hnext, NULL);
-	if (mobj->hprev && P_MobjWasRemoved(mobj->hprev))
-		P_SetTarget(&mobj->hprev, NULL);
+	if ((mobj->flags & MF_NOCLIPHEIGHT || (mobj->z > mobj->floorz && mobj->z + mobj->height < mobj->ceilingz))
+		&& mobj->type != MT_EGGMOBILE_FIRE)
+		correctionType = 1; // Correct Z position by centering
+	else if (mobj->eflags & MFE_VERTICALFLIP)
+		correctionType = 2; // Correct Z position by moving down
+
+	if (abs(mobj->scale - mobj->destscale) < mobj->scalespeed)
+		P_SetScale(mobj, mobj->destscale);
+	else if (mobj->scale < mobj->destscale)
+		P_SetScale(mobj, mobj->scale + mobj->scalespeed);
+	else if (mobj->scale > mobj->destscale)
+		P_SetScale(mobj, mobj->scale - mobj->scalespeed);
+
+	if (correctionType == 1)
+		mobj->z -= (mobj->height - oldheight)/2;
+	else if (correctionType == 2)
+		mobj->z -= mobj->height - oldheight;
+
+	if (mobj->scale == mobj->destscale)
+		/// \todo Lua hook for "reached destscale"?
+		switch (mobj->type)
+		{
+		case MT_EGGMOBILE_FIRE:
+			mobj->destscale = FRACUNIT;
+			mobj->scalespeed = FRACUNIT>>4;
+			break;
+		default:
+			break;
+		}
+}
 
-	mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG);
+static void P_MaceSceneryThink(mobj_t *mobj)
+{
+	angle_t oldmovedir = mobj->movedir;
 
-	tmfloorthing = tmhitthing = NULL;
+	// Always update movedir to prevent desyncing (in the traditional sense, not the netplay sense).
+	mobj->movedir = (mobj->movedir + mobj->lastlook) & FINEMASK;
 
-	// 970 allows ANY mobj to trigger a linedef exec
-	if (mobj->subsector && GETSECSPECIAL(mobj->subsector->sector->special, 2) == 8)
+	// If too far away and not deliberately spitting in the face of optimisation, don't think!
+	if (!(mobj->flags2 & MF2_BOSSNOTRAP))
 	{
-		sector_t *sec2;
-
-		sec2 = P_ThingOnSpecial3DFloor(mobj);
-		if (sec2 && GETSECSPECIAL(sec2->special, 2) == 1)
-			P_LinedefExecute(sec2->tag, mobj, sec2);
-	}
+		UINT8 i;
+		// Quick! Look through players! Don't move unless a player is relatively close by.
+		// The below is selected based on CEZ2's first room. I promise you it is a coincidence that it looks like the weed number.
+		for (i = 0; i < MAXPLAYERS; ++i)
+			if (playeringame[i] && players[i].mo
+				&& P_AproxDistance(P_AproxDistance(mobj->x - players[i].mo->x, mobj->y - players[i].mo->y), mobj->z - players[i].mo->z) < (4200 << FRACBITS))
+				break; // Stop looking.
+		if (i == MAXPLAYERS)
+		{
+			if (!(mobj->flags2 & MF2_BEYONDTHEGRAVE))
+			{
+				mobj_t *ref = mobj;
 
-	// Slowly scale up/down to reach your destscale.
-	if (mobj->scale != mobj->destscale)
-	{
-		fixed_t oldheight = mobj->height;
-		UINT8 correctionType = 0; // Don't correct Z position, just gain height
+				// stop/hide all your babies
+				while ((ref = ref->hnext))
+				{
+					ref->eflags = (((ref->flags & MF_NOTHINK) ? 0 : 1)
+						| ((ref->flags & MF_NOCLIPTHING) ? 0 : 2)
+						| ((ref->flags2 & MF2_DONTDRAW) ? 0 : 4)); // oh my god this is nasty.
+					ref->flags |= MF_NOTHINK|MF_NOCLIPTHING;
+					ref->flags2 |= MF2_DONTDRAW;
+					ref->momx = ref->momy = ref->momz = 0;
+				}
 
-		if ((mobj->flags & MF_NOCLIPHEIGHT || (mobj->z > mobj->floorz && mobj->z + mobj->height < mobj->ceilingz))
-		&& mobj->type != MT_EGGMOBILE_FIRE)
-			correctionType = 1; // Correct Z position by centering
-		else if (mobj->eflags & MFE_VERTICALFLIP)
-			correctionType = 2; // Correct Z position by moving down
-
-		if (abs(mobj->scale - mobj->destscale) < mobj->scalespeed)
-			P_SetScale(mobj, mobj->destscale);
-		else if (mobj->scale < mobj->destscale)
-			P_SetScale(mobj, mobj->scale + mobj->scalespeed);
-		else if (mobj->scale > mobj->destscale)
-			P_SetScale(mobj, mobj->scale - mobj->scalespeed);
-
-		if (correctionType == 1)
-			mobj->z -= (mobj->height - oldheight)/2;
-		else if (correctionType == 2)
-			mobj->z -= mobj->height - oldheight;
+				mobj->flags2 |= MF2_BEYONDTHEGRAVE;
+			}
+			return; // don't make bubble!
+		}
+		else if (mobj->flags2 & MF2_BEYONDTHEGRAVE)
+		{
+			mobj_t *ref = mobj;
 
-		if (mobj->scale == mobj->destscale)
-			/// \todo Lua hook for "reached destscale"?
-			switch(mobj->type)
+			// start/show all your babies
+			while ((ref = ref->hnext))
 			{
-			case MT_EGGMOBILE_FIRE:
-				mobj->destscale = FRACUNIT;
-				mobj->scalespeed = FRACUNIT>>4;
-				break;
-			default:
-				break;
+				if (ref->eflags & 1)
+					ref->flags &= ~MF_NOTHINK;
+				if (ref->eflags & 2)
+					ref->flags &= ~MF_NOCLIPTHING;
+				if (ref->eflags & 4)
+					ref->flags2 &= ~MF2_DONTDRAW;
+				ref->eflags = 0; // le sign
 			}
+
+			mobj->flags2 &= ~MF2_BEYONDTHEGRAVE;
+		}
 	}
 
-	if ((mobj->type == MT_GHOST || mobj->type == MT_THOK) && mobj->fuse > 0) // Not guaranteed to be MF_SCENERY or not MF_SCENERY!
+	// Okay, time to MOVE
+	P_MaceRotate(mobj, mobj->movedir, oldmovedir);
+}
+
+static boolean P_DrownNumbersSceneryThink(mobj_t *mobj)
+{
+	if (!mobj->target)
 	{
-		if (mobj->flags2 & MF2_BOSSNOTRAP) // "fast" flag
-		{
-			if ((signed)((mobj->frame & FF_TRANSMASK) >> FF_TRANSSHIFT) < (NUMTRANSMAPS-1) - (2*mobj->fuse)/3)
-				// fade out when nearing the end of fuse...
-				mobj->frame = (mobj->frame & ~FF_TRANSMASK) | (((NUMTRANSMAPS-1) - (2*mobj->fuse)/3) << FF_TRANSSHIFT);
-		}
-		else
-		{
-			if ((signed)((mobj->frame & FF_TRANSMASK) >> FF_TRANSSHIFT) < (NUMTRANSMAPS-1) - mobj->fuse / 2)
-				// fade out when nearing the end of fuse...
-				mobj->frame = (mobj->frame & ~FF_TRANSMASK) | (((NUMTRANSMAPS-1) - mobj->fuse / 2) << FF_TRANSSHIFT);
-		}
+		P_RemoveMobj(mobj);
+		return false;
+	}
+	if (!mobj->target->player || !(mobj->target->player->powers[pw_underwater] || mobj->target->player->powers[pw_spacetime]))
+	{
+		P_RemoveMobj(mobj);
+		return false;
 	}
+	mobj->x = mobj->target->x;
+	mobj->y = mobj->target->y;
 
-	// Special thinker for scenery objects
-	if (mobj->flags & MF_SCENERY)
+	mobj->destscale = mobj->target->destscale;
+	P_SetScale(mobj, mobj->target->scale);
+
+	if (mobj->target->eflags & MFE_VERTICALFLIP)
 	{
-#ifdef HAVE_BLUA
-		if (LUAh_MobjThinker(mobj))
-			return;
-		if (P_MobjWasRemoved(mobj))
-			return;
-#endif
+		mobj->z = mobj->target->z - FixedMul(16*FRACUNIT, mobj->target->scale) - mobj->height;
+		if (mobj->target->player->pflags & PF_FLIPCAM)
+			mobj->eflags |= MFE_VERTICALFLIP;
+	}
+	else
+		mobj->z = mobj->target->z + (mobj->target->height) + FixedMul(8*FRACUNIT, mobj->target->scale); // Adjust height for height changes
 
-		if (mobj->flags2 & MF2_SHIELD)
-			if (!P_AddShield(mobj))
-				return;
+	if (mobj->threshold <= 35)
+		mobj->flags2 |= MF2_DONTDRAW;
+	else
+		mobj->flags2 &= ~MF2_DONTDRAW;
+	if (mobj->threshold <= 30)
+		mobj->threshold = 40;
+	mobj->threshold--;
+	return true;
+}
 
-		switch (mobj->type)
-		{
-			case MT_BOSSJUNK:
-				mobj->flags2 ^= MF2_DONTDRAW;
-				break;
-			case MT_MACEPOINT:
-			case MT_CHAINMACEPOINT:
-			case MT_SPRINGBALLPOINT:
-			case MT_CHAINPOINT:
-			case MT_FIREBARPOINT:
-			case MT_CUSTOMMACEPOINT:
-			case MT_HIDDEN_SLING:
-				{
-					angle_t oldmovedir = mobj->movedir;
+static void P_FlameJetSceneryThink(mobj_t *mobj)
+{
+	mobj_t *flame;
+	fixed_t strength;
 
-					// Always update movedir to prevent desyncing (in the traditional sense, not the netplay sense).
-					mobj->movedir = (mobj->movedir + mobj->lastlook) & FINEMASK;
+	if (!(mobj->flags2 & MF2_FIRING))
+		return;
 
-					// If too far away and not deliberately spitting in the face of optimisation, don't think!
-					if (!(mobj->flags2 & MF2_BOSSNOTRAP))
-					{
-						UINT8 i;
-						// Quick! Look through players! Don't move unless a player is relatively close by.
-						// The below is selected based on CEZ2's first room. I promise you it is a coincidence that it looks like the weed number.
-						for (i = 0; i < MAXPLAYERS; ++i)
-							if (playeringame[i] && players[i].mo
-							 && P_AproxDistance(P_AproxDistance(mobj->x - players[i].mo->x, mobj->y - players[i].mo->y), mobj->z - players[i].mo->z) < (4200<<FRACBITS))
-								break; // Stop looking.
-						if (i == MAXPLAYERS)
-						{
-							if (!(mobj->flags2 & MF2_BEYONDTHEGRAVE))
-							{
-								mobj_t *ref = mobj;
+	if ((leveltime & 3) != 0)
+		return;
 
-								// stop/hide all your babies
-								while ((ref = ref->hnext))
-								{
-									ref->eflags = (((ref->flags & MF_NOTHINK) ? 0 : 1)
-										| ((ref->flags & MF_NOCLIPTHING) ? 0 : 2)
-										| ((ref->flags2 & MF2_DONTDRAW) ? 0 : 4)); // oh my god this is nasty.
-									ref->flags |= MF_NOTHINK|MF_NOCLIPTHING;
-									ref->flags2 |= MF2_DONTDRAW;
-									ref->momx = ref->momy = ref->momz = 0;
-								}
+	// Wave the flames back and forth. Reactiontime determines which direction it's going.
+	if (mobj->fuse <= -16)
+		mobj->reactiontime = 1;
+	else if (mobj->fuse >= 16)
+		mobj->reactiontime = 0;
 
-								mobj->flags2 |= MF2_BEYONDTHEGRAVE;
-							}
+	if (mobj->reactiontime)
+		mobj->fuse += 2;
+	else
+		mobj->fuse -= 2;
 
-							break; // don't make bubble!
-						}
-						else if (mobj->flags2 & MF2_BEYONDTHEGRAVE)
-						{
-							mobj_t *ref = mobj;
+	flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME);
+	P_SetMobjState(flame, S_FLAMEJETFLAME4);
 
-							// start/show all your babies
-							while ((ref = ref->hnext))
-							{
-								if (ref->eflags & 1)
-									ref->flags &= ~MF_NOTHINK;
-								if (ref->eflags & 2)
-									ref->flags &= ~MF_NOCLIPTHING;
-								if (ref->eflags & 4)
-									ref->flags2 &= ~MF2_DONTDRAW;
-								ref->eflags = 0; // le sign
-							}
+	flame->angle = mobj->angle;
 
-							mobj->flags2 &= ~MF2_BEYONDTHEGRAVE;
-						}
-					}
+	if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side
+		flame->momz = mobj->fuse << (FRACBITS - 2);
+	else
+		flame->angle += FixedAngle(mobj->fuse<<FRACBITS);
 
-					// Okay, time to MOVE
-					P_MaceRotate(mobj, mobj->movedir, oldmovedir);
-				}
-				break;
-			case MT_HOOP:
-				if (mobj->fuse > 1)
-					P_MoveHoop(mobj);
-				else if (mobj->fuse == 1)
-					mobj->movecount = 1;
+	strength = 20*FRACUNIT;
+	strength -= ((20*FRACUNIT)/16)*mobj->movedir;
 
-				if (mobj->movecount)
-				{
-					mobj->fuse++;
+	P_InstaThrust(flame, flame->angle, strength);
+	S_StartSound(flame, sfx_fire);
+}
 
-					if (mobj->fuse > 32)
-					{
-						// Don't kill the hoop center. For the sake of respawning.
-						//if (mobj->target)
-						//	P_RemoveMobj(mobj->target);
+static void P_VerticalFlameJetSceneryThink(mobj_t *mobj)
+{
+	mobj_t *flame;
+	fixed_t strength;
 
-						P_RemoveMobj(mobj);
-					}
-				}
-				else
-					mobj->fuse--;
-				return;
-			case MT_NIGHTSPARKLE:
-				if (mobj->tics != -1)
-				{
-					mobj->tics--;
+	if (!(mobj->flags2 & MF2_FIRING))
+		return;
 
-					// you can cycle through multiple states in a tic
-					if (!mobj->tics)
-						if (!P_SetMobjState(mobj, mobj->state->nextstate))
-							return; // freed itself
-				}
+	if ((leveltime & 3) != 0)
+		return;
 
-				P_UnsetThingPosition(mobj);
-				mobj->x += mobj->momx;
-				mobj->y += mobj->momy;
-				mobj->z += mobj->momz;
-				P_SetThingPosition(mobj);
-				return;
-			case MT_NIGHTSLOOPHELPER:
-				if (--mobj->tics <= 0)
-					P_RemoveMobj(mobj);
+	// Wave the flames back and forth. Reactiontime determines which direction it's going.
+	if (mobj->fuse <= -16)
+		mobj->reactiontime = 1;
+	else if (mobj->fuse >= 16)
+		mobj->reactiontime = 0;
 
-				// Don't touch my fuse!
-				return;
-			case MT_OVERLAY:
-				if (!mobj->target)
-				{
-					P_RemoveMobj(mobj);
-					return;
-				}
-				else
-					P_AddOverlay(mobj);
-				break;
-			case MT_PITY_ORB:
-			case MT_WHIRLWIND_ORB:
-			case MT_ARMAGEDDON_ORB:
-				if (!(mobj->flags2 & MF2_SHIELD))
-					return;
-				break;
-			case MT_ATTRACT_ORB:
-				if (!(mobj->flags2 & MF2_SHIELD))
-					return;
-				if (/*(mobj->target) -- the following is implicit by P_AddShield
-				&& (mobj->target->player)
-				&& */ (mobj->target->player->homing) && (mobj->target->player->pflags & PF_SHIELDABILITY))
-				{
-					P_SetMobjState(mobj, mobj->info->painstate);
-					mobj->tics++;
-				}
-				break;
-			case MT_ELEMENTAL_ORB:
-				if (!(mobj->flags2 & MF2_SHIELD))
-					return;
-				if (mobj->tracer
-				/* && mobj->target -- the following is implicit by P_AddShield
-				&& mobj->target->player
-				&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL */
-				&& mobj->target->player->pflags & PF_SHIELDABILITY
-				&& ((statenum_t)(mobj->tracer->state-states) < mobj->info->raisestate
-					|| (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
-				{
-					P_SetMobjState(mobj, mobj->info->painstate);
-					mobj->tics++;
-					P_SetMobjState(mobj->tracer, mobj->info->raisestate);
-					mobj->tracer->tics++;
-				}
-				break;
-			case MT_FORCE_ORB:
-				if (!(mobj->flags2 & MF2_SHIELD))
-					return;
-				if (/*
-				&& mobj->target -- the following is implicit by P_AddShield
-				&& mobj->target->player
-				&& (mobj->target->player->powers[pw_shield] & SH_FORCE)
-				&& */ (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
-					mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal whoosh
-				}
-				/* FALLTHRU */
-			case MT_FLAMEAURA_ORB:
-				if (!(mobj->flags2 & MF2_SHIELD))
-					return;
-				if ((statenum_t)(mobj->state-states) < mobj->info->painstate)
-					mobj->angle = mobj->target->angle; // implicitly okay because of P_AddShield
-				if (mobj->tracer
-				/* && mobj->target -- the following is implicit by P_AddShield
-				&& mobj->target->player
-				&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA */
-				&& mobj->target->player->pflags & PF_SHIELDABILITY
-				&& ((statenum_t)(mobj->tracer->state-states) < mobj->info->raisestate
-					|| (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
-				{
-					P_SetMobjState(mobj, mobj->info->painstate);
-					mobj->tics++;
-					P_SetMobjState(mobj->tracer, mobj->info->raisestate);
-					mobj->tracer->tics++;
-				}
-				break;
-			case MT_BUBBLEWRAP_ORB:
-				if (!(mobj->flags2 & MF2_SHIELD))
-					return;
-				if (mobj->tracer
-				/* && mobj->target -- the following is implicit by P_AddShield
-				&& mobj->target->player
-				&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP */
-				)
-				{
-					if (mobj->target->player->pflags & PF_SHIELDABILITY
-					&& ((statenum_t)(mobj->state-states) < mobj->info->painstate
-						|| (mobj->state->nextstate < mobj->info->painstate && mobj->tics == 1)))
-					{
-						P_SetMobjState(mobj, mobj->info->painstate);
-						mobj->tics++;
-						P_SetMobjState(mobj->tracer, mobj->info->raisestate);
-						mobj->tracer->tics++;
-					}
-					else if (mobj->target->eflags & MFE_JUSTHITFLOOR
-					&& (statenum_t)(mobj->state-states) == mobj->info->painstate)
-					{
-						P_SetMobjState(mobj, mobj->info->painstate+1);
-						mobj->tics++;
-						P_SetMobjState(mobj->tracer, mobj->info->raisestate+1);
-						mobj->tracer->tics++;
-					}
-				}
-				break;
-			case MT_THUNDERCOIN_ORB:
-				if (!(mobj->flags2 & MF2_SHIELD))
-					return;
-				if (mobj->tracer
-				/* && mobj->target -- the following is implicit by P_AddShield
-				&& mobj->target->player
-				&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN */
-				&& (mobj->target->player->pflags & PF_SHIELDABILITY))
-				{
-					P_SetMobjState(mobj, mobj->info->painstate);
-					mobj->tics++;
-					P_SetMobjState(mobj->tracer, mobj->info->raisestate);
-					mobj->tracer->tics++;
-					mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal spark
-				}
-				break;
-			case MT_WATERDROP:
-				P_SceneryCheckWater(mobj);
-				if ((mobj->z <= mobj->floorz || mobj->z <= mobj->watertop)
-					&& mobj->health > 0)
-				{
-					mobj->health = 0;
-					P_SetMobjState(mobj, mobj->info->deathstate);
-					S_StartSound(mobj, mobj->info->deathsound+P_RandomKey(mobj->info->mass));
-					return;
-				}
-				break;
-			case MT_BUBBLES:
-				P_SceneryCheckWater(mobj);
-				break;
-			case MT_SMALLBUBBLE:
-			case MT_MEDIUMBUBBLE:
-			case MT_EXTRALARGEBUBBLE:	// start bubble dissipate
-				P_SceneryCheckWater(mobj);
-				if (P_MobjWasRemoved(mobj)) // bubble was removed by not being in water
-					return;
-				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
-					P_RemoveMobj(mobj);
-					return;
-				}
-				break;
-			case MT_LOCKON:
-				if (!mobj->target)
-				{
-					P_RemoveMobj(mobj);
-					return;
-				}
+	if (mobj->reactiontime)
+		mobj->fuse++;
+	else
+		mobj->fuse--;
 
-				mobj->flags2 &= ~MF2_DONTDRAW;
+	flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME);
 
-				mobj->x = mobj->target->x;
-				mobj->y = mobj->target->y;
+	strength = 20*FRACUNIT;
+	strength -= ((20*FRACUNIT)/16)*mobj->movedir;
 
-				mobj->eflags |= (mobj->target->eflags & MFE_VERTICALFLIP);
+	// If deaf'd, the object spawns on the ceiling.
+	if (mobj->flags2 & MF2_AMBUSH)
+	{
+		mobj->z = mobj->ceilingz - mobj->height;
+		flame->momz = -strength;
+	}
+	else
+	{
+		flame->momz = strength;
+		P_SetMobjState(flame, S_FLAMEJETFLAME7);
+	}
+	P_InstaThrust(flame, mobj->angle, FixedDiv(mobj->fuse*FRACUNIT, 3*FRACUNIT));
+	S_StartSound(flame, sfx_fire);
+}
 
-				mobj->destscale = mobj->target->destscale;
-				P_SetScale(mobj, mobj->target->scale);
+static boolean P_ParticleGenSceneryThink(mobj_t *mobj)
+{
+	if (!mobj->lastlook)
+		return false;
 
-				if (!(mobj->eflags & MFE_VERTICALFLIP))
-					mobj->z = mobj->target->z + mobj->target->height + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale);
-				else
-					mobj->z = mobj->target->z - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale) - mobj->height;
-				break;
-			case MT_LOCKONINF:
-				if (!(mobj->flags2 & MF2_STRONGBOX))
-				{
-					mobj->threshold = mobj->z;
-					mobj->flags2 |= MF2_STRONGBOX;
-				}
-				if (!(mobj->eflags & MFE_VERTICALFLIP))
-					mobj->z = mobj->threshold + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale);
-				else
-					mobj->z = mobj->threshold - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale);
-				break;
-			case MT_DROWNNUMBERS:
-				if (!mobj->target)
-				{
-					P_RemoveMobj(mobj);
-					return;
-				}
-				if (!mobj->target->player || !(mobj->target->player->powers[pw_underwater] || mobj->target->player->powers[pw_spacetime]))
-				{
-					P_RemoveMobj(mobj);
-					return;
-				}
-				mobj->x = mobj->target->x;
-				mobj->y = mobj->target->y;
+	if (!mobj->threshold)
+		return false;
 
-				mobj->destscale = mobj->target->destscale;
-				P_SetScale(mobj, mobj->target->scale);
+	if (--mobj->fuse <= 0)
+	{
+		INT32 i = 0;
+		mobj_t *spawn;
+		fixed_t bottomheight, topheight;
+		INT32 type = mobj->threshold, line = mobj->cvmem;
 
-				if (mobj->target->eflags & MFE_VERTICALFLIP)
-				{
-					mobj->z = mobj->target->z - FixedMul(16*FRACUNIT, mobj->target->scale) - mobj->height;
-					if (mobj->target->player->pflags & PF_FLIPCAM)
-						mobj->eflags |= MFE_VERTICALFLIP;
-				}
-				else
-					mobj->z = mobj->target->z + (mobj->target->height) + FixedMul(8*FRACUNIT, mobj->target->scale); // Adjust height for height changes
-
-				if (mobj->threshold <= 35)
-					mobj->flags2 |= MF2_DONTDRAW;
-				else
-					mobj->flags2 &= ~MF2_DONTDRAW;
-				if (mobj->threshold <= 30)
-					mobj->threshold = 40;
-				mobj->threshold--;
-				break;
-			case MT_FLAMEJET:
-				if ((mobj->flags2 & MF2_FIRING) && (leveltime & 3) == 0)
-				{
-					mobj_t *flame;
-					fixed_t strength;
-
-					// Wave the flames back and forth. Reactiontime determines which direction it's going.
-					if (mobj->fuse <= -16)
-						mobj->reactiontime = 1;
-					else if (mobj->fuse >= 16)
-						mobj->reactiontime = 0;
-
-					if (mobj->reactiontime)
-						mobj->fuse += 2;
-					else
-						mobj->fuse -= 2;
-
-					flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME);
-					P_SetMobjState(flame, S_FLAMEJETFLAME4);
-
-					flame->angle = mobj->angle;
-
-					if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side
-						flame->momz = mobj->fuse << (FRACBITS-2);
-					else
-						flame->angle += FixedAngle(mobj->fuse*FRACUNIT);
-
-					strength = 20*FRACUNIT;
-					strength -= ((20*FRACUNIT)/16)*mobj->movedir;
-
-					P_InstaThrust(flame, flame->angle, strength);
-					S_StartSound(flame, sfx_fire);
-				}
-				break;
-			case MT_VERTICALFLAMEJET:
-				if ((mobj->flags2 & MF2_FIRING) && (leveltime & 3) == 0)
-				{
-					mobj_t *flame;
-					fixed_t strength;
-
-					// Wave the flames back and forth. Reactiontime determines which direction it's going.
-					if (mobj->fuse <= -16)
-						mobj->reactiontime = 1;
-					else if (mobj->fuse >= 16)
-						mobj->reactiontime = 0;
-
-					if (mobj->reactiontime)
-						mobj->fuse++;
-					else
-						mobj->fuse--;
-
-					flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME);
-
-					strength = 20*FRACUNIT;
-					strength -= ((20*FRACUNIT)/16)*mobj->movedir;
-
-					// If deaf'd, the object spawns on the ceiling.
-					if (mobj->flags2 & MF2_AMBUSH)
-					{
-						mobj->z = mobj->ceilingz-mobj->height;
-						flame->momz = -strength;
-					}
-					else
-					{
-						flame->momz = strength;
-						P_SetMobjState(flame, S_FLAMEJETFLAME7);
-					}
-					P_InstaThrust(flame, mobj->angle, FixedDiv(mobj->fuse*FRACUNIT,3*FRACUNIT));
-					S_StartSound(flame, sfx_fire);
-				}
-				break;
-			case MT_FLICKY_01_CENTER:
-			case MT_FLICKY_02_CENTER:
-			case MT_FLICKY_03_CENTER:
-			case MT_FLICKY_04_CENTER:
-			case MT_FLICKY_05_CENTER:
-			case MT_FLICKY_06_CENTER:
-			case MT_FLICKY_07_CENTER:
-			case MT_FLICKY_08_CENTER:
-			case MT_FLICKY_09_CENTER:
-			case MT_FLICKY_10_CENTER:
-			case MT_FLICKY_11_CENTER:
-			case MT_FLICKY_12_CENTER:
-			case MT_FLICKY_13_CENTER:
-			case MT_FLICKY_14_CENTER:
-			case MT_FLICKY_15_CENTER:
-			case MT_FLICKY_16_CENTER:
-			case MT_SECRETFLICKY_01_CENTER:
-			case MT_SECRETFLICKY_02_CENTER:
-				if (mobj->tracer && (mobj->flags & MF_NOCLIPTHING)
-					&& (mobj->flags & MF_GRENADEBOUNCE))
-					// for now: only do this bounce routine if flicky is in-place. \todo allow in all movements
-				{
-					if (!(mobj->tracer->flags2 & MF2_OBJECTFLIP) && mobj->tracer->z <= mobj->tracer->floorz)
-						mobj->tracer->momz = 7*FRACUNIT;
-					else if ((mobj->tracer->flags2 & MF2_OBJECTFLIP) && mobj->tracer->z >= mobj->tracer->ceilingz - mobj->tracer->height)
-						mobj->tracer->momz = -7*FRACUNIT;
-				}
-				break;
-			case MT_SEED:
-				if (P_MobjFlip(mobj)*mobj->momz < mobj->info->speed)
-					mobj->momz = P_MobjFlip(mobj)*mobj->info->speed;
-				break;
-			case MT_ROCKCRUMBLE1:
-			case MT_ROCKCRUMBLE2:
-			case MT_ROCKCRUMBLE3:
-			case MT_ROCKCRUMBLE4:
-			case MT_ROCKCRUMBLE5:
-			case MT_ROCKCRUMBLE6:
-			case MT_ROCKCRUMBLE7:
-			case MT_ROCKCRUMBLE8:
-			case MT_ROCKCRUMBLE9:
-			case MT_ROCKCRUMBLE10:
-			case MT_ROCKCRUMBLE11:
-			case MT_ROCKCRUMBLE12:
-			case MT_ROCKCRUMBLE13:
-			case MT_ROCKCRUMBLE14:
-			case MT_ROCKCRUMBLE15:
-			case MT_ROCKCRUMBLE16:
-			case MT_WOODDEBRIS:
-			case MT_BRICKDEBRIS:
-			case MT_BROKENROBOT:
-				if (mobj->z <= P_FloorzAtPos(mobj->x, mobj->y, mobj->z, mobj->height)
-					&& mobj->state != &states[mobj->info->deathstate])
-				{
-					P_SetMobjState(mobj, mobj->info->deathstate);
-					return;
-				}
-				break;
-			case MT_PARTICLEGEN:
-				if (!mobj->lastlook)
-					return;
-
-				if (!mobj->threshold)
-					return;
-
-				if (--mobj->fuse <= 0)
-				{
-					INT32 i = 0;
-					mobj_t *spawn;
-					fixed_t bottomheight, topheight;
-					INT32 type = mobj->threshold, line = mobj->cvmem;
-
-					mobj->fuse = (tic_t)mobj->reactiontime;
-
-					bottomheight = lines[line].frontsector->floorheight;
-					topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height;
-
-					if (mobj->waterbottom != bottomheight || mobj->watertop != topheight)
-					{
-						if (mobj->movefactor && (topheight > bottomheight))
-							mobj->health = (tic_t)(FixedDiv((topheight - bottomheight), abs(mobj->movefactor)) >> FRACBITS);
-						else
-							mobj->health = 0;
-
-						mobj->z = ((mobj->flags2 & MF2_OBJECTFLIP) ? topheight : bottomheight);
-					}
-
-					if (!mobj->health)
-						return;
-
-					for (i = 0; i < mobj->lastlook; i++)
-					{
-						spawn = P_SpawnMobj(
-							mobj->x + FixedMul(FixedMul(mobj->friction, mobj->scale), FINECOSINE(mobj->angle>>ANGLETOFINESHIFT)),
-							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
-
-						mobj->angle += mobj->movedir;
-					}
-
-					mobj->angle += (angle_t)mobj->movecount;
-				}
-				break;
-			case MT_FSGNA:
-				if (mobj->movedir)
-					mobj->angle += mobj->movedir;
-				break;
-			case MT_ROSY:
-				{
-					UINT8 i;
-					fixed_t pdist = 1700*mobj->scale, work, actualwork;
-					player_t *player = NULL;
-					statenum_t stat = (mobj->state-states);
-					for (i = 0; i < MAXPLAYERS; i++)
-					{
-						if (!playeringame[i])
-							continue;
-						if (!players[i].mo)
-							continue;
-						if (players[i].bot)
-							continue;
-						if (!players[i].mo->health)
-							continue;
-						actualwork = work = FixedHypot(mobj->x-players[i].mo->x, mobj->y-players[i].mo->y);
-						if (player)
-						{
-							if (players[i].skin == 0 || players[i].skin == 5)
-								work = (2*work)/3;
-							if (work >= pdist)
-								continue;
-						}
-						pdist = actualwork;
-						player = &players[i];
-					}
+		mobj->fuse = (tic_t)mobj->reactiontime;
 
-					if (stat == S_ROSY_JUMP || stat == S_ROSY_PAIN)
-					{
-						if (P_IsObjectOnGround(mobj))
-						{
-							mobj->momx = mobj->momy = 0;
-							if (player && mobj->cvmem < (-2*TICRATE))
-								stat = S_ROSY_UNHAPPY;
-							else
-								stat = S_ROSY_WALK;
-							P_SetMobjState(mobj, stat);
-						}
-						else if (P_MobjFlip(mobj)*mobj->momz < 0)
-							mobj->frame = mobj->state->frame+mobj->state->var1;
-					}
+		bottomheight = lines[line].frontsector->floorheight;
+		topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height;
 
-					if (!player)
-					{
-						if ((stat < S_ROSY_IDLE1 || stat > S_ROSY_IDLE4) && stat != S_ROSY_JUMP)
-						{
-							mobj->momx = mobj->momy = 0;
-							P_SetMobjState(mobj, S_ROSY_IDLE1);
-						}
-					}
-					else
-					{
-						boolean dojump = false, targonground, love, makeheart = false;
-						if (mobj->target != player->mo)
-							P_SetTarget(&mobj->target, player->mo);
-						targonground = (P_IsObjectOnGround(mobj->target) && (player->panim == PA_IDLE || player->panim == PA_WALK || player->panim == PA_RUN));
-						love = (player->skin == 0 || player->skin == 5);
+		if (mobj->waterbottom != bottomheight || mobj->watertop != topheight)
+		{
+			if (mobj->movefactor && (topheight > bottomheight))
+				mobj->health = (tic_t)(FixedDiv((topheight - bottomheight), abs(mobj->movefactor)) >> FRACBITS);
+			else
+				mobj->health = 0;
 
-						switch (stat)
-						{
-							case S_ROSY_IDLE1:
-							case S_ROSY_IDLE2:
-							case S_ROSY_IDLE3:
-							case S_ROSY_IDLE4:
-								dojump = true;
-								break;
-							case S_ROSY_JUMP:
-							case S_ROSY_PAIN:
-								// handled above
-								break;
-							case S_ROSY_WALK:
-								{
-									fixed_t x = mobj->x, y = mobj->y, z = mobj->z;
-									angle_t angletoplayer = R_PointToAngle2(x, y, mobj->target->x, mobj->target->y);
-									boolean allowed = P_TryMove(mobj, mobj->target->x, mobj->target->y, false);
-
-									P_UnsetThingPosition(mobj);
-									mobj->x = x;
-									mobj->y = y;
-									mobj->z = z;
-									P_SetThingPosition(mobj);
-
-									if (allowed)
-									{
-										fixed_t mom, max;
-										P_Thrust(mobj, angletoplayer, (3*FRACUNIT)>>1);
-										mom = FixedHypot(mobj->momx, mobj->momy);
-										max = pdist;
-										if ((--mobj->extravalue1) <= 0)
-										{
-											if (++mobj->frame > mobj->state->frame+mobj->state->var1)
-												mobj->frame = mobj->state->frame;
-											if (mom > 12*mobj->scale)
-												mobj->extravalue1 = 2;
-											else if (mom > 6*mobj->scale)
-												mobj->extravalue1 = 3;
-											else
-												mobj->extravalue1 = 4;
-										}
-										if (max < (mobj->radius + mobj->target->radius))
-										{
-											mobj->momx = mobj->target->player->cmomx;
-											mobj->momy = mobj->target->player->cmomy;
-											if ((mobj->cvmem > TICRATE && !player->exiting) || !targonground)
-												P_SetMobjState(mobj, (stat = S_ROSY_STND));
-											else
-											{
-												mobj->target->momx = mobj->momx;
-												mobj->target->momy = mobj->momy;
-												P_SetMobjState(mobj, (stat = S_ROSY_HUG));
-												S_StartSound(mobj, sfx_cdpcm6);
-												mobj->angle = angletoplayer;
-											}
-										}
-										else
-										{
-											max /= 3;
-											if (max > 30*mobj->scale)
-												max = 30*mobj->scale;
-											if (mom > max && max > mobj->scale)
-											{
-												max = FixedDiv(max, mom);
-												mobj->momx = FixedMul(mobj->momx, max);
-												mobj->momy = FixedMul(mobj->momy, max);
-											}
-											if (abs(mobj->momx) > mobj->scale || abs(mobj->momy) > mobj->scale)
-												mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy);
-										}
-									}
-									else
-										dojump = true;
-								}
-								break;
-							case S_ROSY_HUG:
-								if (targonground)
-								{
-									player->pflags |= PF_STASIS;
-									if (mobj->cvmem < 5*TICRATE)
-										mobj->cvmem++;
-									if (love && !(leveltime & 7))
-										makeheart = true;
-								}
-								else
-								{
-									if (mobj->cvmem < (love ? 5*TICRATE : 0))
-									{
-										P_SetMobjState(mobj, (stat = S_ROSY_PAIN));
-										S_StartSound(mobj, sfx_cdpcm7);
-									}
-									else
-										P_SetMobjState(mobj, (stat = S_ROSY_JUMP));
-									var1 = var2 = 0;
-									A_DoNPCPain(mobj);
-									mobj->cvmem -= TICRATE;
-								}
-								break;
-							case S_ROSY_STND:
-								if ((pdist > (mobj->radius + mobj->target->radius + 3*(mobj->scale + mobj->target->scale))))
-									P_SetMobjState(mobj, (stat = S_ROSY_WALK));
-								else if (!targonground)
-									;
-								else
-								{
-									if (love && !(leveltime & 15))
-										makeheart = true;
-									if (player->exiting || --mobj->cvmem < TICRATE)
-									{
-										P_SetMobjState(mobj, (stat = S_ROSY_HUG));
-										S_StartSound(mobj, sfx_cdpcm6);
-										mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
-										mobj->target->momx = mobj->momx;
-										mobj->target->momy = mobj->momy;
-									}
-								}
-								break;
-							case S_ROSY_UNHAPPY:
-							default:
-								break;
-						}
+			mobj->z = ((mobj->flags2 & MF2_OBJECTFLIP) ? topheight : bottomheight);
+		}
 
-						if (stat == S_ROSY_HUG)
-						{
-							if (player->panim != PA_IDLE)
-								P_SetPlayerMobjState(mobj->target, S_PLAY_STND);
-							player->pflags |= PF_STASIS;
-						}
+		if (!mobj->health)
+			return false;
 
-						if (dojump)
-						{
-							P_SetMobjState(mobj, S_ROSY_JUMP);
-							mobj->z += P_MobjFlip(mobj);
-							mobj->momx = mobj->momy = 0;
-							P_SetObjectMomZ(mobj, 6<<FRACBITS, false);
-							S_StartSound(mobj, sfx_cdfm02);
-						}
+		for (i = 0; i < mobj->lastlook; i++)
+		{
+			spawn = P_SpawnMobj(
+				mobj->x + FixedMul(FixedMul(mobj->friction, mobj->scale), FINECOSINE(mobj->angle >> ANGLETOFINESHIFT)),
+				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 (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;
-						}
-					}
-				}
-				break;
-			case MT_CDLHRT:
-				{
-					if (mobj->cvmem < 24)
-						mobj->cvmem++;
-					mobj->movedir += ANG10;
-					P_UnsetThingPosition(mobj);
-					mobj->x = mobj->extravalue1 + P_ReturnThrustX(mobj, mobj->movedir, mobj->cvmem*mobj->scale);
-					mobj->y = mobj->extravalue2 + P_ReturnThrustY(mobj, mobj->movedir, mobj->cvmem*mobj->scale);
-					P_SetThingPosition(mobj);
-					if ((--mobj->fuse) < 6)
-					{
-						if (!mobj->fuse)
-						{
-							P_RemoveMobj(mobj);
-							return;
-						}
-						mobj->frame = (mobj->frame & ~FF_TRANSMASK)|((10-(mobj->fuse*2))<<(FF_TRANSSHIFT));
-					}
-				}
-				break;
-			case MT_VWREF:
-			case MT_VWREB:
-				{
-					INT32 strength;
-					++mobj->movedir;
-					mobj->frame &= ~FF_TRANSMASK;
-					strength = min(mobj->fuse, (INT32)mobj->movedir)*3;
-					if (strength < 10)
-						mobj->frame |= ((10-strength)<<(FF_TRANSSHIFT));
-				}
-				/* FALLTHRU */
-			default:
-				if (mobj->fuse)
-				{ // Scenery object fuse! Very basic!
-					mobj->fuse--;
-					if (!mobj->fuse)
-					{
-#ifdef HAVE_BLUA
-						if (!LUAh_MobjFuse(mobj))
-#endif
-						P_RemoveMobj(mobj);
-						return;
-					}
-				}
-				break;
+			mobj->angle += mobj->movedir;
 		}
 
-		P_SceneryThinker(mobj);
-		return;
+		mobj->angle += (angle_t)mobj->movecount;
 	}
 
-#ifdef HAVE_BLUA
-	// Check for a Lua thinker first
-	if (!mobj->player)
+	return true;
+}
+
+static void P_RosySceneryThink(mobj_t *mobj)
+{
+	UINT8 i;
+	fixed_t pdist = 1700*mobj->scale, work, actualwork;
+	player_t *player = NULL;
+	statenum_t stat = (mobj->state - states);
+	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (LUAh_MobjThinker(mobj) || P_MobjWasRemoved(mobj))
-			return;
+		if (!playeringame[i])
+			continue;
+		if (!players[i].mo)
+			continue;
+		if (players[i].bot)
+			continue;
+		if (!players[i].mo->health)
+			continue;
+		actualwork = work = FixedHypot(mobj->x - players[i].mo->x, mobj->y - players[i].mo->y);
+		if (player)
+		{
+			if (players[i].skin == 0 || players[i].skin == 5)
+				work = (2*work)/3;
+			if (work >= pdist)
+				continue;
+		}
+		pdist = actualwork;
+		player = &players[i];
 	}
-	else if (!mobj->player->spectator)
+
+	if (stat == S_ROSY_JUMP || stat == S_ROSY_PAIN)
 	{
-		// You cannot short-circuit the player thinker like you can other thinkers.
-		LUAh_MobjThinker(mobj);
-		if (P_MobjWasRemoved(mobj))
-			return;
+		if (P_IsObjectOnGround(mobj))
+		{
+			mobj->momx = mobj->momy = 0;
+			if (player && mobj->cvmem < (-2*TICRATE))
+				stat = S_ROSY_UNHAPPY;
+			else
+				stat = S_ROSY_WALK;
+			P_SetMobjState(mobj, stat);
+		}
+		else if (P_MobjFlip(mobj)*mobj->momz < 0)
+			mobj->frame = mobj->state->frame + mobj->state->var1;
 	}
-#endif
-	// if it's pushable, or if it would be pushable other than temporary disablement, use the
-	// separate thinker
-	if (mobj->flags & MF_PUSHABLE || (mobj->info->flags & MF_PUSHABLE && mobj->fuse))
-	{
-		P_MobjCheckWater(mobj);
-		P_PushableThinker(mobj);
 
-		// Extinguish fire objects in water. (Yes, it's extraordinarily rare to have a pushable flame object, but Brak uses such a case.)
-		if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
-			&& (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
+	if (!player)
+	{
+		if ((stat < S_ROSY_IDLE1 || stat > S_ROSY_IDLE4) && stat != S_ROSY_JUMP)
 		{
-			P_KillMobj(mobj, NULL, NULL, 0);
-			return;
+			mobj->momx = mobj->momy = 0;
+			P_SetMobjState(mobj, S_ROSY_IDLE1);
 		}
 	}
-	else if (mobj->flags & MF_BOSS)
+	else
 	{
-#ifdef HAVE_BLUA
-		if (LUAh_BossThinker(mobj))
+		boolean dojump = false, targonground, love, makeheart = false;
+		if (mobj->target != player->mo)
+			P_SetTarget(&mobj->target, player->mo);
+		// Tatsuru: Don't try to hug them if they're above or below you!
+		targonground = (P_IsObjectOnGround(mobj->target) && (player->panim == PA_IDLE || player->panim == PA_WALK || player->panim == PA_RUN) && player->mo->z == mobj->z);
+		love = (player->skin == 0 || player->skin == 5);
+
+		switch (stat)
 		{
-			if (P_MobjWasRemoved(mobj))
-				return;
-		}
-		else if (P_MobjWasRemoved(mobj))
-			return;
-		else
-#endif
-		switch (mobj->type)
+		case S_ROSY_IDLE1:
+		case S_ROSY_IDLE2:
+		case S_ROSY_IDLE3:
+		case S_ROSY_IDLE4:
+			dojump = true;
+			break;
+		case S_ROSY_JUMP:
+		case S_ROSY_PAIN:
+			// handled above
+			break;
+		case S_ROSY_WALK:
 		{
-			case MT_EGGMOBILE:
-				if (mobj->health < mobj->info->damage+1 && leveltime & 2)
-				{
-					fixed_t rad = mobj->radius>>FRACBITS;
-					fixed_t hei = mobj->height>>FRACBITS;
-					mobj_t *particle = P_SpawnMobjFromMobj(mobj,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(hei/2, hei)<<FRACBITS,
-						MT_SMOKE);
-					P_SetObjectMomZ(particle, 2<<FRACBITS, false);
-					particle->momz += mobj->momz;
-				}
-				if (mobj->flags2 & MF2_SKULLFLY)
-#if 1
-					P_SpawnGhostMobj(mobj);
-#else // all the way back from final demo... MT_THOK isn't even the same size anymore!
-				{
-					mobj_t *spawnmobj;
-					spawnmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->info->painchance);
-					P_SetTarget(&spawnmobj->target, mobj);
-					spawnmobj->color = SKINCOLOR_GREY;
-				}
-#endif
-				P_Boss1Thinker(mobj);
-				break;
-			case MT_EGGMOBILE2:
-				if (mobj->health < mobj->info->damage+1 && leveltime & 2)
-				{
-					fixed_t rad = mobj->radius>>FRACBITS;
-					fixed_t hei = mobj->height>>FRACBITS;
-					mobj_t *particle = P_SpawnMobjFromMobj(mobj,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(hei/2, hei)<<FRACBITS,
-						MT_SMOKE);
-					P_SetObjectMomZ(particle, 2<<FRACBITS, false);
-					particle->momz += mobj->momz;
-				}
-				P_Boss2Thinker(mobj);
-				break;
-			case MT_EGGMOBILE3:
-				if (mobj->health < mobj->info->damage+1 && leveltime & 2)
+			fixed_t x = mobj->x, y = mobj->y, z = mobj->z;
+			angle_t angletoplayer = R_PointToAngle2(x, y, mobj->target->x, mobj->target->y);
+			boolean allowed = P_TryMove(mobj, mobj->target->x, mobj->target->y, false);
+
+			P_UnsetThingPosition(mobj);
+			mobj->x = x;
+			mobj->y = y;
+			mobj->z = z;
+			P_SetThingPosition(mobj);
+
+			if (allowed)
+			{
+				fixed_t mom, max;
+				P_Thrust(mobj, angletoplayer, (3*FRACUNIT) >> 1);
+				mom = FixedHypot(mobj->momx, mobj->momy);
+				max = pdist;
+				if ((--mobj->extravalue1) <= 0)
 				{
-					fixed_t rad = mobj->radius>>FRACBITS;
-					fixed_t hei = mobj->height>>FRACBITS;
-					mobj_t *particle = P_SpawnMobjFromMobj(mobj,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(hei/2, hei)<<FRACBITS,
-						MT_SMOKE);
-					P_SetObjectMomZ(particle, 2<<FRACBITS, false);
-					particle->momz += mobj->momz;
+					if (++mobj->frame > mobj->state->frame + mobj->state->var1)
+						mobj->frame = mobj->state->frame;
+					if (mom > 12*mobj->scale)
+						mobj->extravalue1 = 2;
+					else if (mom > 6*mobj->scale)
+						mobj->extravalue1 = 3;
+					else
+						mobj->extravalue1 = 4;
 				}
-				P_Boss3Thinker(mobj);
-				break;
-			case MT_EGGMOBILE4:
-				if (mobj->health < mobj->info->damage+1 && leveltime & 2)
+				if (max < (mobj->radius + mobj->target->radius))
 				{
-					fixed_t rad = mobj->radius>>FRACBITS;
-					fixed_t hei = mobj->height>>FRACBITS;
-					mobj_t *particle = P_SpawnMobjFromMobj(mobj,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(rad, -rad)<<FRACBITS,
-						P_RandomRange(hei/2, hei)<<FRACBITS,
-						MT_SMOKE);
-					P_SetObjectMomZ(particle, 2<<FRACBITS, false);
-					particle->momz += mobj->momz;
+					mobj->momx = mobj->target->player->cmomx;
+					mobj->momy = mobj->target->player->cmomy;
+					if ((mobj->cvmem > TICRATE && !player->exiting) || !targonground)
+						P_SetMobjState(mobj, (stat = S_ROSY_STND));
+					else
+					{
+						mobj->target->momx = mobj->momx;
+						mobj->target->momy = mobj->momy;
+						P_SetMobjState(mobj, (stat = S_ROSY_HUG));
+						S_StartSound(mobj, sfx_cdpcm6);
+						mobj->angle = angletoplayer;
+					}
 				}
-				P_Boss4Thinker(mobj);
-				break;
-			case MT_FANG:
-				P_Boss5Thinker(mobj);
-				break;
-			case MT_BLACKEGGMAN:
-				P_Boss7Thinker(mobj);
-				break;
-			case MT_METALSONIC_BATTLE:
-				P_Boss9Thinker(mobj);
-				break;
-			default: // Generic SOC-made boss
-				if (mobj->flags2 & MF2_SKULLFLY)
-					P_SpawnGhostMobj(mobj);
-				P_GenericBossThinker(mobj);
-				break;
-		}
-		if (mobj->flags2 & MF2_BOSSFLEE)
-		{
-			if (mobj->extravalue1)
-			{
-				if (!(--mobj->extravalue1))
+				else
 				{
-					if (mobj->target)
+					max /= 3;
+					if (max > 30*mobj->scale)
+						max = 30*mobj->scale;
+					if (mom > max && max > mobj->scale)
 					{
-						mobj->momz = FixedMul(FixedDiv(mobj->target->z - mobj->z, P_AproxDistance(mobj->x-mobj->target->x,mobj->y-mobj->target->y)), mobj->scale<<1);
-						mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+						max = FixedDiv(max, mom);
+						mobj->momx = FixedMul(mobj->momx, max);
+						mobj->momy = FixedMul(mobj->momy, max);
 					}
-					else
-						mobj->momz = 8*mobj->scale;
+					if (abs(mobj->momx) > mobj->scale || abs(mobj->momy) > mobj->scale)
+						mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy);
 				}
-				else
-					mobj->angle += mobj->movedir;
 			}
-			else if (mobj->target)
-				P_InstaThrust(mobj, mobj->angle, FixedMul(12*FRACUNIT, mobj->scale));
+			else
+				dojump = true;
 		}
-	}
-	else if (mobj->health <= 0) // Dead things think differently than the living.
-		switch (mobj->type)
-		{
-		case MT_BLUESPHERE:
-			if ((mobj->tics>>2)+1 > 0 && (mobj->tics>>2)+1 <= tr_trans60) // tr_trans50 through tr_trans90, shifting once every second frame
-				mobj->frame = (NUMTRANSMAPS-((mobj->tics>>2)+1))<<FF_TRANSSHIFT;
-			else // tr_trans60 otherwise
-				mobj->frame = tr_trans60<<FF_TRANSSHIFT;
-			break;
-		case MT_EGGCAPSULE:
-			if (mobj->z <= mobj->floorz)
+		break;
+		case S_ROSY_HUG:
+			if (targonground)
 			{
-				P_RemoveMobj(mobj);
-				return;
+				player->pflags |= PF_STASIS;
+				if (mobj->cvmem < 5*TICRATE)
+					mobj->cvmem++;
+				if (love && !(leveltime & 7))
+					makeheart = true;
 			}
-			break;
-		case MT_FAKEMOBILE:
-			if (mobj->scale == mobj->destscale)
+			else
 			{
-				if (!mobj->fuse)
+				if (mobj->cvmem < (love ? 5*TICRATE : 0))
 				{
-					S_StartSound(mobj, sfx_s3k77);
-					mobj->flags2 |= MF2_DONTDRAW;
-					mobj->fuse = TICRATE;
+					P_SetMobjState(mobj, (stat = S_ROSY_PAIN));
+					S_StartSound(mobj, sfx_cdpcm7);
 				}
-				return;
-			}
-			if (!mobj->reactiontime)
-			{
-				if (P_RandomChance(FRACUNIT/2))
-					mobj->movefactor = FRACUNIT;
-				else
-					mobj->movefactor = -FRACUNIT;
-				if (P_RandomChance(FRACUNIT/2))
-					mobj->movedir = ANG20;
 				else
-					mobj->movedir = -ANG20;
-				mobj->reactiontime = 5;
+					P_SetMobjState(mobj, (stat = S_ROSY_JUMP));
+				var1 = var2 = 0;
+				A_DoNPCPain(mobj);
+				mobj->cvmem -= TICRATE;
 			}
-			mobj->momz += mobj->movefactor;
-			mobj->angle += mobj->movedir;
-			P_InstaThrust(mobj, mobj->angle, -mobj->info->speed);
-			mobj->reactiontime--;
 			break;
-		case MT_EGGSHIELD:
-			mobj->flags2 ^= MF2_DONTDRAW;
-			break;
-		case MT_EGGTRAP: // Egg Capsule animal release
-			if (mobj->fuse > 0)// && mobj->fuse < TICRATE-(TICRATE/7))
+		case S_ROSY_STND:
+			if ((pdist > (mobj->radius + mobj->target->radius + 3*(mobj->scale + mobj->target->scale))))
+				P_SetMobjState(mobj, (stat = S_ROSY_WALK));
+			else if (!targonground)
+				;
+			else
 			{
-				INT32 i;
-				fixed_t x,y,z;
-				fixed_t ns;
-				mobj_t *mo2;
-				mobj_t *flicky;
-
-				z = mobj->subsector->sector->floorheight + FRACUNIT + (P_RandomKey(64)<<FRACBITS);
-				for (i = 0; i < 3; i++)
+				if (love && !(leveltime & 15))
+					makeheart = true;
+				if (player->exiting || --mobj->cvmem < TICRATE)
 				{
-					const angle_t fa = P_RandomKey(FINEANGLES) & FINEMASK;
-					ns = 64 * FRACUNIT;
-					x = mobj->x + FixedMul(FINESINE(fa),ns);
-					y = mobj->y + FixedMul(FINECOSINE(fa),ns);
-
-					mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
-					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);
-					mo2->momy = FixedMul(FINECOSINE(fa),ns);
-					mo2->angle = fa << ANGLETOFINESHIFT;
-
-					if (!i && !(mobj->fuse & 2))
-						S_StartSound(mo2, mobj->info->deathsound);
-
-					flicky = P_InternalFlickySpawn(mo2, 0, 8*FRACUNIT, false, -1);
-					if (!flicky)
-						break;
-
-					P_SetTarget(&flicky->target, mo2);
-					flicky->momx = mo2->momx;
-					flicky->momy = mo2->momy;
+					P_SetMobjState(mobj, (stat = S_ROSY_HUG));
+					S_StartSound(mobj, sfx_cdpcm6);
+					mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+					mobj->target->momx = mobj->momx;
+					mobj->target->momy = mobj->momy;
 				}
-
-				mobj->fuse--;
 			}
 			break;
-		case MT_PLAYER:
-			/// \todo Have the player's dead body completely finish its animation even if they've already respawned.
-			if (!mobj->fuse)
-			{ // Go away.
-			  /// \todo Actually go ahead and remove mobj completely, and fix any bugs and crashes doing this creates. Chasecam should stop moving, and F12 should never return to it.
-				mobj->momz = 0;
-				if (mobj->player)
-					mobj->flags2 |= MF2_DONTDRAW;
-				else // safe to remove, nobody's going to complain!
-				{
-					P_RemoveMobj(mobj);
-					return;
-				}
-			}
-			else // Apply gravity to fall downwards.
-			{
-				if (mobj->player && !(mobj->fuse % 8) && (mobj->player->charflags & SF_MACHINE))
-				{
-					fixed_t r = mobj->radius >> FRACBITS;
-					mobj_t *explosion = P_SpawnMobj(
-						mobj->x + (P_RandomRange(r, -r) << FRACBITS),
-						mobj->y + (P_RandomRange(r, -r) << FRACBITS),
-						mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS),
-						MT_SONIC3KBOSSEXPLODE);
-					S_StartSound(explosion, sfx_s3kb4);
-				}
-				if (mobj->movedir == DMG_DROWNED)
-					P_SetObjectMomZ(mobj, -FRACUNIT / 2, true); // slower fall from drowning
-				else
-					P_SetObjectMomZ(mobj, -2 * FRACUNIT / 3, true);
-			}
-			break;
-		case MT_METALSONIC_RACE:
-			{
-				if (!(mobj->fuse % 8))
-				{
-					fixed_t r = mobj->radius >> FRACBITS;
-					mobj_t *explosion = P_SpawnMobj(
-						mobj->x + (P_RandomRange(r, -r) << FRACBITS),
-						mobj->y + (P_RandomRange(r, -r) << FRACBITS),
-						mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS),
-						MT_SONIC3KBOSSEXPLODE);
-					S_StartSound(explosion, sfx_s3kb4);
-				}
-				P_SetObjectMomZ(mobj, -2 * FRACUNIT / 3, true);
-			}
+		case S_ROSY_UNHAPPY:
 		default:
 			break;
 		}
-	else
-	{
-		if ((mobj->flags & MF_ENEMY) && (mobj->state->nextstate == mobj->info->spawnstate && mobj->tics == 1))
-			mobj->flags2 &= ~MF2_FRET;
 
-		// Angle-to-tracer to trigger a linedef exec
-		// See Linedef Exec 457 (Track mobj angle to point)
-		if ((mobj->eflags & MFE_TRACERANGLE) && mobj->tracer && mobj->extravalue2)
+		if (stat == S_ROSY_HUG)
+		{
+			if (player->panim != PA_IDLE)
+				P_SetPlayerMobjState(mobj->target, S_PLAY_STND);
+			player->pflags |= PF_STASIS;
+		}
+
+		if (dojump)
+		{
+			P_SetMobjState(mobj, S_ROSY_JUMP);
+			mobj->z += P_MobjFlip(mobj);
+			mobj->momx = mobj->momy = 0;
+			P_SetObjectMomZ(mobj, 6 << FRACBITS, false);
+			S_StartSound(mobj, sfx_cdfm02);
+		}
+
+		if (makeheart)
 		{
-			// mobj->lastlook - Don't disable behavior after first failure
-			// mobj->extravalue1 - Angle tolerance
-			// mobj->extravalue2 - Exec tag upon failure
-			// mobj->cvval - Allowable failure delay
-			// mobj->cvmem - Failure timer
+			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;
+		}
+	}
+}
+
+static void P_MobjSceneryThink(mobj_t *mobj)
+{
+#ifdef HAVE_BLUA
+	if (LUAh_MobjThinker(mobj))
+		return;
+	if (P_MobjWasRemoved(mobj))
+		return;
+#endif
+
+	if ((mobj->flags2 & MF2_SHIELD) && !P_AddShield(mobj))
+		return;
 
-			angle_t ang = mobj->angle - R_PointToAngle2(mobj->x, mobj->y, mobj->tracer->x, mobj->tracer->y);
+	switch (mobj->type)
+	{
+	case MT_BOSSJUNK:
+		mobj->flags2 ^= MF2_DONTDRAW;
+		break;
+	case MT_MACEPOINT:
+	case MT_CHAINMACEPOINT:
+	case MT_SPRINGBALLPOINT:
+	case MT_CHAINPOINT:
+	case MT_FIREBARPOINT:
+	case MT_CUSTOMMACEPOINT:
+	case MT_HIDDEN_SLING:
+		P_MaceSceneryThink(mobj);
+		break;
+	case MT_HOOP:
+		if (mobj->fuse > 1)
+			P_MoveHoop(mobj);
+		else if (mobj->fuse == 1)
+			mobj->movecount = 1;
 
-			// \todo account for distance between mobj and tracer
-			// Because closer mobjs can be facing beyond the angle tolerance
-			// yet tracer is still in the camera view
+		if (mobj->movecount)
+		{
+			mobj->fuse++;
 
-			// failure state: mobj is not facing tracer
-			// Reasaonable defaults: ANGLE_67h, ANGLE_292h
-			if (ang >= (angle_t)mobj->extravalue1 && ang <= ANGLE_MAX - (angle_t)mobj->extravalue1)
+			if (mobj->fuse > 32)
 			{
-				if (mobj->cvmem)
-					mobj->cvmem--;
-				else
-				{
-					INT32 exectag = mobj->extravalue2; // remember this before we erase the values
+				// Don't kill the hoop center. For the sake of respawning.
+				//if (mobj->target)
+				//	P_RemoveMobj(mobj->target);
 
-					if (mobj->lastlook)
-						mobj->cvmem = mobj->cusval; // reset timer for next failure
-					else
-					{
-						// disable after first failure
-						mobj->eflags &= ~MFE_TRACERANGLE;
-						mobj->lastlook = mobj->extravalue1 = mobj->extravalue2 = mobj->cvmem = mobj->cusval = 0;
-					}
+				P_RemoveMobj(mobj);
+			}
+		}
+		else
+			mobj->fuse--;
+		return;
+	case MT_NIGHTSPARKLE:
+		if (mobj->tics != -1)
+		{
+			mobj->tics--;
 
-					P_LinedefExecute(exectag, mobj, NULL);
-				}
+			// you can cycle through multiple states in a tic
+			if (!mobj->tics)
+				if (!P_SetMobjState(mobj, mobj->state->nextstate))
+					return; // freed itself
+		}
+
+		P_UnsetThingPosition(mobj);
+		mobj->x += mobj->momx;
+		mobj->y += mobj->momy;
+		mobj->z += mobj->momz;
+		P_SetThingPosition(mobj);
+		return;
+	case MT_NIGHTSLOOPHELPER:
+		if (--mobj->tics <= 0)
+			P_RemoveMobj(mobj);
+
+		// Don't touch my fuse!
+		return;
+	case MT_OVERLAY:
+		if (!mobj->target)
+		{
+			P_RemoveMobj(mobj);
+			return;
+		}
+		else
+			P_AddOverlay(mobj);
+		break;
+	case MT_PITY_ORB:
+	case MT_WHIRLWIND_ORB:
+	case MT_ARMAGEDDON_ORB:
+		if (!(mobj->flags2 & MF2_SHIELD))
+			return;
+		break;
+	case MT_ATTRACT_ORB:
+		if (!(mobj->flags2 & MF2_SHIELD))
+			return;
+		if (/*(mobj->target) -- the following is implicit by P_AddShield
+		&& (mobj->target->player)
+		&& */ (mobj->target->player->homing) && (mobj->target->player->pflags & PF_SHIELDABILITY))
+		{
+			P_SetMobjState(mobj, mobj->info->painstate);
+			mobj->tics++;
+		}
+		break;
+	case MT_ELEMENTAL_ORB:
+		if (!(mobj->flags2 & MF2_SHIELD))
+			return;
+		if (mobj->tracer
+			/* && mobj->target -- the following is implicit by P_AddShield
+			&& mobj->target->player
+			&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL */
+			&& mobj->target->player->pflags & PF_SHIELDABILITY
+			&& ((statenum_t)(mobj->tracer->state - states) < mobj->info->raisestate
+				|| (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
+		{
+			P_SetMobjState(mobj, mobj->info->painstate);
+			mobj->tics++;
+			P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+			mobj->tracer->tics++;
+		}
+		break;
+	case MT_FORCE_ORB:
+		if (!(mobj->flags2 & MF2_SHIELD))
+			return;
+		if (/*
+		&& mobj->target -- the following is implicit by P_AddShield
+		&& mobj->target->player
+		&& (mobj->target->player->powers[pw_shield] & SH_FORCE)
+		&& */ (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
+			mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal whoosh
+		}
+		/* FALLTHRU */
+	case MT_FLAMEAURA_ORB:
+		if (!(mobj->flags2 & MF2_SHIELD))
+			return;
+		if ((statenum_t)(mobj->state - states) < mobj->info->painstate)
+			mobj->angle = mobj->target->angle; // implicitly okay because of P_AddShield
+		if (mobj->tracer
+			/* && mobj->target -- the following is implicit by P_AddShield
+			&& mobj->target->player
+			&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA */
+			&& mobj->target->player->pflags & PF_SHIELDABILITY
+			&& ((statenum_t)(mobj->tracer->state - states) < mobj->info->raisestate
+				|| (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
+		{
+			P_SetMobjState(mobj, mobj->info->painstate);
+			mobj->tics++;
+			P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+			mobj->tracer->tics++;
+		}
+		break;
+	case MT_BUBBLEWRAP_ORB:
+		if (!(mobj->flags2 & MF2_SHIELD))
+			return;
+		if (mobj->tracer
+			/* && mobj->target -- the following is implicit by P_AddShield
+			&& mobj->target->player
+			&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP */
+			)
+		{
+			if (mobj->target->player->pflags & PF_SHIELDABILITY
+				&& ((statenum_t)(mobj->state - states) < mobj->info->painstate
+					|| (mobj->state->nextstate < mobj->info->painstate && mobj->tics == 1)))
+			{
+				P_SetMobjState(mobj, mobj->info->painstate);
+				mobj->tics++;
+				P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+				mobj->tracer->tics++;
 			}
-			else
-				mobj->cvmem = mobj->cusval; // reset failure timer
+			else if (mobj->target->eflags & MFE_JUSTHITFLOOR
+				&& (statenum_t)(mobj->state - states) == mobj->info->painstate)
+			{
+				P_SetMobjState(mobj, mobj->info->painstate + 1);
+				mobj->tics++;
+				P_SetMobjState(mobj->tracer, mobj->info->raisestate + 1);
+				mobj->tracer->tics++;
+			}
+		}
+		break;
+	case MT_THUNDERCOIN_ORB:
+		if (!(mobj->flags2 & MF2_SHIELD))
+			return;
+		if (mobj->tracer
+			/* && mobj->target -- the following is implicit by P_AddShield
+			&& mobj->target->player
+			&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN */
+			&& (mobj->target->player->pflags & PF_SHIELDABILITY))
+		{
+			P_SetMobjState(mobj, mobj->info->painstate);
+			mobj->tics++;
+			P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+			mobj->tracer->tics++;
+			mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal spark
+		}
+		break;
+	case MT_WATERDROP:
+		P_SceneryCheckWater(mobj);
+		if ((mobj->z <= mobj->floorz || mobj->z <= mobj->watertop)
+			&& mobj->health > 0)
+		{
+			mobj->health = 0;
+			P_SetMobjState(mobj, mobj->info->deathstate);
+			S_StartSound(mobj, mobj->info->deathsound + P_RandomKey(mobj->info->mass));
+			return;
+		}
+		break;
+	case MT_BUBBLES:
+		P_SceneryCheckWater(mobj);
+		break;
+	case MT_SMALLBUBBLE:
+	case MT_MEDIUMBUBBLE:
+	case MT_EXTRALARGEBUBBLE:	// start bubble dissipate
+		P_SceneryCheckWater(mobj);
+		if (P_MobjWasRemoved(mobj)) // bubble was removed by not being in water
+			return;
+		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
+			P_RemoveMobj(mobj);
+			return;
+		}
+		break;
+	case MT_LOCKON:
+		if (!mobj->target)
+		{
+			P_RemoveMobj(mobj);
+			return;
 		}
 
-		switch (mobj->type)
+		mobj->flags2 &= ~MF2_DONTDRAW;
+
+		mobj->x = mobj->target->x;
+		mobj->y = mobj->target->y;
+
+		mobj->eflags |= (mobj->target->eflags & MFE_VERTICALFLIP);
+
+		mobj->destscale = mobj->target->destscale;
+		P_SetScale(mobj, mobj->target->scale);
+
+		if (!(mobj->eflags & MFE_VERTICALFLIP))
+			mobj->z = mobj->target->z + mobj->target->height + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale);
+		else
+			mobj->z = mobj->target->z - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale) - mobj->height;
+		break;
+	case MT_LOCKONINF:
+		if (!(mobj->flags2 & MF2_STRONGBOX))
 		{
-			case MT_WALLSPIKEBASE:
-				if (!mobj->target) {
-					P_RemoveMobj(mobj);
-					return;
-				}
-				mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK);
-#if 0
-				if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle
-				{
-					mobj_t *target = mobj->target; // shortcut
-					const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale);
-					P_UnsetThingPosition(mobj);
-					mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius);
-					mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius);
-					P_SetThingPosition(mobj);
-					mobj->angle = target->angle + ANGLE_90;
-				}
+			mobj->threshold = mobj->z;
+			mobj->flags2 |= MF2_STRONGBOX;
+		}
+		if (!(mobj->eflags & MFE_VERTICALFLIP))
+			mobj->z = mobj->threshold + FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale);
+		else
+			mobj->z = mobj->threshold - FixedMul((16 + abs((signed)(leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->scale);
+		break;
+	case MT_DROWNNUMBERS:
+		if (!P_DrownNumbersSceneryThink(mobj))
+			return;
+		break;
+	case MT_FLAMEJET:
+		P_FlameJetSceneryThink(mobj);
+		break;
+	case MT_VERTICALFLAMEJET:
+		P_VerticalFlameJetSceneryThink(mobj);
+		break;
+	case MT_FLICKY_01_CENTER:
+	case MT_FLICKY_02_CENTER:
+	case MT_FLICKY_03_CENTER:
+	case MT_FLICKY_04_CENTER:
+	case MT_FLICKY_05_CENTER:
+	case MT_FLICKY_06_CENTER:
+	case MT_FLICKY_07_CENTER:
+	case MT_FLICKY_08_CENTER:
+	case MT_FLICKY_09_CENTER:
+	case MT_FLICKY_10_CENTER:
+	case MT_FLICKY_11_CENTER:
+	case MT_FLICKY_12_CENTER:
+	case MT_FLICKY_13_CENTER:
+	case MT_FLICKY_14_CENTER:
+	case MT_FLICKY_15_CENTER:
+	case MT_FLICKY_16_CENTER:
+	case MT_SECRETFLICKY_01_CENTER:
+	case MT_SECRETFLICKY_02_CENTER:
+		if (mobj->tracer && (mobj->flags & MF_NOCLIPTHING)
+			&& (mobj->flags & MF_GRENADEBOUNCE))
+			// for now: only do this bounce routine if flicky is in-place. \todo allow in all movements
+		{
+			if (!(mobj->tracer->flags2 & MF2_OBJECTFLIP) && mobj->tracer->z <= mobj->tracer->floorz)
+				mobj->tracer->momz = 7*FRACUNIT;
+			else if ((mobj->tracer->flags2 & MF2_OBJECTFLIP) && mobj->tracer->z >= mobj->tracer->ceilingz - mobj->tracer->height)
+				mobj->tracer->momz = -7*FRACUNIT;
+		}
+		break;
+	case MT_SEED:
+		if (P_MobjFlip(mobj)*mobj->momz < mobj->info->speed)
+			mobj->momz = P_MobjFlip(mobj)*mobj->info->speed;
+		break;
+	case MT_ROCKCRUMBLE1:
+	case MT_ROCKCRUMBLE2:
+	case MT_ROCKCRUMBLE3:
+	case MT_ROCKCRUMBLE4:
+	case MT_ROCKCRUMBLE5:
+	case MT_ROCKCRUMBLE6:
+	case MT_ROCKCRUMBLE7:
+	case MT_ROCKCRUMBLE8:
+	case MT_ROCKCRUMBLE9:
+	case MT_ROCKCRUMBLE10:
+	case MT_ROCKCRUMBLE11:
+	case MT_ROCKCRUMBLE12:
+	case MT_ROCKCRUMBLE13:
+	case MT_ROCKCRUMBLE14:
+	case MT_ROCKCRUMBLE15:
+	case MT_ROCKCRUMBLE16:
+	case MT_WOODDEBRIS:
+	case MT_BRICKDEBRIS:
+	case MT_BROKENROBOT:
+		if (mobj->z <= P_FloorzAtPos(mobj->x, mobj->y, mobj->z, mobj->height)
+			&& mobj->state != &states[mobj->info->deathstate])
+		{
+			P_SetMobjState(mobj, mobj->info->deathstate);
+			return;
+		}
+		break;
+	case MT_PARTICLEGEN:
+		if (!P_ParticleGenSceneryThink(mobj))
+			return;
+		break;
+	case MT_FSGNA:
+		if (mobj->movedir)
+			mobj->angle += mobj->movedir;
+		break;
+	case MT_ROSY:
+		P_RosySceneryThink(mobj);
+		break;
+	case MT_CDLHRT:
+	{
+		if (mobj->cvmem < 24)
+			mobj->cvmem++;
+		mobj->movedir += ANG10;
+		P_UnsetThingPosition(mobj);
+		mobj->x = mobj->extravalue1 + P_ReturnThrustX(mobj, mobj->movedir, mobj->cvmem*mobj->scale);
+		mobj->y = mobj->extravalue2 + P_ReturnThrustY(mobj, mobj->movedir, mobj->cvmem*mobj->scale);
+		P_SetThingPosition(mobj);
+		
+		if (!mobj->fuse)
+		{
+#ifdef HAVE_BLUA
+			if (!LUAh_MobjFuse(mobj))
+#endif
+				P_RemoveMobj(mobj);
+			return;
+		}
+		if (mobj->fuse < 0)
+			return;
+		if ((--mobj->fuse) < 6)
+			mobj->frame = (mobj->frame & ~FF_TRANSMASK) | ((10 - (mobj->fuse*2)) << (FF_TRANSSHIFT));
+	}
+	break;
+	case MT_VWREF:
+	case MT_VWREB:
+	{
+		INT32 strength;
+		++mobj->movedir;
+		mobj->frame &= ~FF_TRANSMASK;
+		strength = min(mobj->fuse, (INT32)mobj->movedir)*3;
+		if (strength < 10)
+			mobj->frame |= ((10 - strength) << (FF_TRANSSHIFT));
+	}
+	/* FALLTHRU */
+	default:
+		if (mobj->fuse)
+		{ // Scenery object fuse! Very basic!
+			mobj->fuse--;
+			if (!mobj->fuse)
+			{
+#ifdef HAVE_BLUA
+				if (!LUAh_MobjFuse(mobj))
 #endif
-				break;
-			case MT_FALLINGROCK:
-				// Despawn rocks here in case zmovement code can't do so (blame slopes)
-				if (!mobj->momx && !mobj->momy && !mobj->momz
-				&& ((mobj->eflags & MFE_VERTICALFLIP) ?
-					  mobj->z + mobj->height >= mobj->ceilingz
-					: mobj->z <= mobj->floorz))
-				{
 					P_RemoveMobj(mobj);
-					return;
-				}
-				P_MobjCheckWater(mobj);
-				break;
-			case MT_ARROW:
-				if (mobj->flags & MF_MISSILE)
-				{
-					// Calculate the angle of movement.
-					/*
-						   momz
-						 / |
-					   /   |
-					 /     |
-					0------dist(momx,momy)
-					*/
-
-					fixed_t dist = P_AproxDistance(mobj->momx, mobj->momy);
-					angle_t angle = R_PointToAngle2(0, 0, dist, mobj->momz);
-
-					if (angle > ANG20 && angle <= ANGLE_180)
-						mobj->frame = 2;
-					else if (angle < ANG340 && angle > ANGLE_180)
-						mobj->frame = 0;
-					else
-						mobj->frame = 1;
+				return;
+			}
+		}
+		break;
+	}
 
-					if (!(mobj->extravalue1) && (mobj->momz < 0))
-					{
-						mobj->extravalue1 = 1;
-						S_StartSound(mobj, mobj->info->activesound);
-					}
-					if (leveltime & 1)
-					{
-						mobj_t *dust = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_PARTICLE);
-						dust->tics = 18;
-						dust->scalespeed = 4096;
-						dust->destscale = FRACUNIT/32;
-					}
-				}
-				else
-					mobj->flags2 ^= MF2_DONTDRAW;
-				break;
-			case MT_EMERALDSPAWN:
-				if (mobj->threshold)
-				{
-					mobj->threshold--;
+	P_SceneryThinker(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);
-					}
-				}
-				break;
-			case MT_BUGGLE:
-				mobj->eflags |= MFE_UNDERWATER; //P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn
-				{
-					if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0
-						&& P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius * 16)
-					{
-						// Home in on the target.
-						P_HomingAttack(mobj, mobj->tracer);
+static boolean P_MobjPushableThink(mobj_t *mobj)
+{
+	P_MobjCheckWater(mobj);
+	P_PushableThinker(mobj);
 
-						if (mobj->z < mobj->floorz)
-							mobj->z = mobj->floorz;
+	// Extinguish fire objects in water. (Yes, it's extraordinarily rare to have a pushable flame object, but Brak uses such a case.)
+	if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
+		&& (mobj->eflags & (MFE_UNDERWATER | MFE_TOUCHWATER)))
+	{
+		P_KillMobj(mobj, NULL, NULL, 0);
+		return false;
+	}
 
-						if (leveltime % mobj->info->painchance == 0)
-							S_StartSound(mobj, mobj->info->activesound);
+	return true;
+}
 
-						if ((statenum_t)(mobj->state-states) != mobj->info->seestate)
-							P_SetMobjState(mobj, mobj->info->seestate);
-					}
-					else
-					{
-						// Try to find a player
-						P_LookForPlayers(mobj, true, true, mobj->radius * 16);
-						mobj->momx >>= 1;
-						mobj->momy >>= 1;
-						mobj->momz >>= 1;
-						if ((statenum_t)(mobj->state-states) != mobj->info->spawnstate)
-							P_SetMobjState(mobj, mobj->info->spawnstate);
-					}
-				}
-				break;
-			case MT_BUMBLEBORE:
+static boolean P_MobjBossThink(mobj_t *mobj)
+{
+#ifdef HAVE_BLUA
+	if (LUAh_BossThinker(mobj))
+	{
+		if (P_MobjWasRemoved(mobj))
+			return false;
+	}
+	else if (P_MobjWasRemoved(mobj))
+		return false;
+	else
+#endif
+		switch (mobj->type)
+		{
+		case MT_EGGMOBILE:
+			if (mobj->health < mobj->info->damage + 1 && leveltime & 2)
+			{
+				fixed_t rad = mobj->radius >> FRACBITS;
+				fixed_t hei = mobj->height >> FRACBITS;
+				mobj_t *particle = P_SpawnMobjFromMobj(mobj,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(hei / 2, hei) << FRACBITS,
+					MT_SMOKE);
+				P_SetObjectMomZ(particle, 2 << FRACBITS, false);
+				particle->momz += mobj->momz;
+			}
+			if (mobj->flags2 & MF2_SKULLFLY)
+#if 1
+				P_SpawnGhostMobj(mobj);
+#else // all the way back from final demo... MT_THOK isn't even the same size anymore!
+			{
+				mobj_t *spawnmobj;
+				spawnmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->info->painchance);
+				P_SetTarget(&spawnmobj->target, mobj);
+				spawnmobj->color = SKINCOLOR_GREY;
+			}
+#endif
+			P_Boss1Thinker(mobj);
+			break;
+		case MT_EGGMOBILE2:
+			if (mobj->health < mobj->info->damage + 1 && leveltime & 2)
+			{
+				fixed_t rad = mobj->radius >> FRACBITS;
+				fixed_t hei = mobj->height >> FRACBITS;
+				mobj_t *particle = P_SpawnMobjFromMobj(mobj,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(hei/2, hei) << FRACBITS,
+					MT_SMOKE);
+				P_SetObjectMomZ(particle, 2 << FRACBITS, false);
+				particle->momz += mobj->momz;
+			}
+			P_Boss2Thinker(mobj);
+			break;
+		case MT_EGGMOBILE3:
+			if (mobj->health < mobj->info->damage + 1 && leveltime & 2)
+			{
+				fixed_t rad = mobj->radius >> FRACBITS;
+				fixed_t hei = mobj->height >> FRACBITS;
+				mobj_t *particle = P_SpawnMobjFromMobj(mobj,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(hei/2, hei) << FRACBITS,
+					MT_SMOKE);
+				P_SetObjectMomZ(particle, 2 << FRACBITS, false);
+				particle->momz += mobj->momz;
+			}
+			P_Boss3Thinker(mobj);
+			break;
+		case MT_EGGMOBILE4:
+			if (mobj->health < mobj->info->damage + 1 && leveltime & 2)
+			{
+				fixed_t rad = mobj->radius >> FRACBITS;
+				fixed_t hei = mobj->height >> FRACBITS;
+				mobj_t* particle = P_SpawnMobjFromMobj(mobj,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(rad, -rad) << FRACBITS,
+					P_RandomRange(hei/2, hei) << FRACBITS,
+					MT_SMOKE);
+				P_SetObjectMomZ(particle, 2 << FRACBITS, false);
+				particle->momz += mobj->momz;
+			}
+			P_Boss4Thinker(mobj);
+			break;
+		case MT_FANG:
+			P_Boss5Thinker(mobj);
+			break;
+		case MT_BLACKEGGMAN:
+			P_Boss7Thinker(mobj);
+			break;
+		case MT_METALSONIC_BATTLE:
+			P_Boss9Thinker(mobj);
+			break;
+		default: // Generic SOC-made boss
+			if (mobj->flags2 & MF2_SKULLFLY)
+				P_SpawnGhostMobj(mobj);
+			P_GenericBossThinker(mobj);
+			break;
+		}
+	if (mobj->flags2 & MF2_BOSSFLEE)
+	{
+		if (mobj->extravalue1)
+		{
+			if (!(--mobj->extravalue1))
+			{
+				if (mobj->target)
 				{
-					statenum_t st = mobj->state-states;
-					if (st == S_BUMBLEBORE_FLY1 || st == S_BUMBLEBORE_FLY2)
-					{
-						if (!mobj->target)
-							P_SetMobjState(mobj, mobj->info->spawnstate);
-						else if (P_MobjFlip(mobj)*((mobj->z + (mobj->height>>1)) - (mobj->target->z + (mobj->target->height>>1))) > 0
-							&& R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) <= 32*FRACUNIT)
-						{
-							mobj->momx >>= 1;
-							mobj->momy >>= 1;
-							if (++mobj->movefactor == 4)
-							{
-								S_StartSound(mobj, mobj->info->seesound);
-								mobj->momx = mobj->momy = mobj->momz = 0;
-								mobj->flags = (mobj->flags|MF_PAIN) & ~MF_NOGRAVITY;
-								P_SetMobjState(mobj, mobj->info->meleestate);
-							}
-						}
-						else
-							mobj->movefactor = 0;
-					}
-					else if (st == S_BUMBLEBORE_RAISE || st == S_BUMBLEBORE_FALL2) // no _FALL1 because it's an 0-tic
-					{
-						if (P_IsObjectOnGround(mobj))
-						{
-							S_StopSound(mobj);
-							S_StartSound(mobj, mobj->info->attacksound);
-							mobj->flags = (mobj->flags|MF_NOGRAVITY) & ~MF_PAIN;
-							mobj->momx = mobj->momy = mobj->momz = 0;
-							P_SetMobjState(mobj, mobj->info->painstate);
-						}
-						else
-						{
-							mobj->angle += ANGLE_22h;
-							mobj->frame = mobj->state->frame + ((mobj->tics & 2)>>1);
-						}
-					}
-					else if (st == S_BUMBLEBORE_STUCK2 && mobj->tics < TICRATE)
-						mobj->frame = mobj->state->frame + ((mobj->tics & 2)>>1);
+					mobj->momz = FixedMul(FixedDiv(mobj->target->z - mobj->z, P_AproxDistance(mobj->x - mobj->target->x, mobj->y - mobj->target->y)), mobj->scale << 1);
+					mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
 				}
-				break;
-			case MT_BIGMINE:
-				mobj->extravalue1 += 3;
-				mobj->extravalue1 %= 360;
-				P_UnsetThingPosition(mobj);
-				mobj->z += FINESINE(mobj->extravalue1*(FINEMASK+1)/360);
-				P_SetThingPosition(mobj);
-				break;
-			case MT_FLAME:
-				if (mobj->flags2 & MF2_BOSSNOTRAP)
+				else
+					mobj->momz = 8*mobj->scale;
+			}
+			else
+				mobj->angle += mobj->movedir;
+		}
+		else if (mobj->target)
+			P_InstaThrust(mobj, mobj->angle, FixedMul(12*FRACUNIT, mobj->scale));
+	}
+	if (mobj->type == MT_CYBRAKDEMON && !mobj->health)
+	{
+		if (!(mobj->tics & 1))
+		{
+			var1 = 2;
+			var2 = 0;
+			A_BossScream(mobj);
+		}
+		if (P_CheckDeathPitCollide(mobj))
+		{
+			P_RemoveMobj(mobj);
+			return false;
+		}
+		if (mobj->momz && mobj->z + mobj->momz <= mobj->floorz)
+		{
+			S_StartSound(mobj, sfx_befall);
+			if (mobj->state != states + S_CYBRAKDEMON_DIE8)
+				P_SetMobjState(mobj, S_CYBRAKDEMON_DIE8);
+		}
+	}
+	return true;
+}
+
+static boolean P_MobjDeadThink(mobj_t *mobj)
+{
+	switch (mobj->type)
+	{
+	case MT_BLUESPHERE:
+		if ((mobj->tics >> 2) + 1 > 0 && (mobj->tics >> 2) + 1 <= tr_trans60) // tr_trans50 through tr_trans90, shifting once every second frame
+			mobj->frame = (NUMTRANSMAPS - ((mobj->tics >> 2) + 1)) << FF_TRANSSHIFT;
+		else // tr_trans60 otherwise
+			mobj->frame = tr_trans60 << FF_TRANSSHIFT;
+		break;
+	case MT_EGGCAPSULE:
+		if (mobj->z <= mobj->floorz)
+		{
+			P_RemoveMobj(mobj);
+			return false;
+		}
+		break;
+	case MT_FAKEMOBILE:
+		if (mobj->scale == mobj->destscale)
+		{
+			if (!mobj->fuse)
+			{
+				S_StartSound(mobj, sfx_s3k77);
+				mobj->flags2 |= MF2_DONTDRAW;
+				mobj->fuse = TICRATE;
+			}
+			return false;
+		}
+		if (!mobj->reactiontime)
+		{
+			if (P_RandomChance(FRACUNIT/2))
+				mobj->movefactor = FRACUNIT;
+			else
+				mobj->movefactor = -FRACUNIT;
+			if (P_RandomChance(FRACUNIT/2))
+				mobj->movedir = ANG20;
+			else
+				mobj->movedir = -ANG20;
+			mobj->reactiontime = 5;
+		}
+		mobj->momz += mobj->movefactor;
+		mobj->angle += mobj->movedir;
+		P_InstaThrust(mobj, mobj->angle, -mobj->info->speed);
+		mobj->reactiontime--;
+		break;
+	case MT_EGGSHIELD:
+		mobj->flags2 ^= MF2_DONTDRAW;
+		break;
+	case MT_EGGTRAP: // Egg Capsule animal release
+		if (mobj->fuse > 0)// && mobj->fuse < TICRATE-(TICRATE/7))
+		{
+			INT32 i;
+			fixed_t x, y, z;
+			fixed_t ns;
+			mobj_t* mo2;
+			mobj_t* flicky;
+
+			z = mobj->subsector->sector->floorheight + FRACUNIT + (P_RandomKey(64) << FRACBITS);
+			for (i = 0; i < 3; i++)
+			{
+				const angle_t fa = P_RandomKey(FINEANGLES) & FINEMASK;
+				ns = 64*FRACUNIT;
+				x = mobj->x + FixedMul(FINESINE(fa), ns);
+				y = mobj->y + FixedMul(FINECOSINE(fa), ns);
+
+				mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
+				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);
+				mo2->momy = FixedMul(FINECOSINE(fa), ns);
+				mo2->angle = fa << ANGLETOFINESHIFT;
+
+				if (!i && !(mobj->fuse & 2))
+					S_StartSound(mo2, mobj->info->deathsound);
+
+				flicky = P_InternalFlickySpawn(mo2, 0, 8*FRACUNIT, false, -1);
+				if (!flicky)
+					break;
+
+				P_SetTarget(&flicky->target, mo2);
+				flicky->momx = mo2->momx;
+				flicky->momy = mo2->momy;
+			}
+
+			mobj->fuse--;
+		}
+		break;
+	case MT_PLAYER:
+		/// \todo Have the player's dead body completely finish its animation even if they've already respawned.
+		if (!mobj->fuse)
+		{ // Go away.
+		  /// \todo Actually go ahead and remove mobj completely, and fix any bugs and crashes doing this creates. Chasecam should stop moving, and F12 should never return to it.
+			mobj->momz = 0;
+			if (mobj->player)
+				mobj->flags2 |= MF2_DONTDRAW;
+			else // safe to remove, nobody's going to complain!
+			{
+				P_RemoveMobj(mobj);
+				return false;
+			}
+		}
+		else // Apply gravity to fall downwards.
+		{
+			if (mobj->player && !(mobj->fuse % 8) && (mobj->player->charflags & SF_MACHINE))
+			{
+				fixed_t r = mobj->radius >> FRACBITS;
+				mobj_t *explosion = P_SpawnMobj(
+					mobj->x + (P_RandomRange(r, -r) << FRACBITS),
+					mobj->y + (P_RandomRange(r, -r) << FRACBITS),
+					mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS),
+					MT_SONIC3KBOSSEXPLODE);
+				S_StartSound(explosion, sfx_s3kb4);
+			}
+			if (mobj->movedir == DMG_DROWNED)
+				P_SetObjectMomZ(mobj, -FRACUNIT/2, true); // slower fall from drowning
+			else
+				P_SetObjectMomZ(mobj, -2*FRACUNIT/3, true);
+		}
+		break;
+	case MT_METALSONIC_RACE:
+	{
+		if (!(mobj->fuse % 8))
+		{
+			fixed_t r = mobj->radius >> FRACBITS;
+			mobj_t *explosion = P_SpawnMobj(
+				mobj->x + (P_RandomRange(r, -r) << FRACBITS),
+				mobj->y + (P_RandomRange(r, -r) << FRACBITS),
+				mobj->z + (P_RandomKey(mobj->height >> FRACBITS) << FRACBITS),
+				MT_SONIC3KBOSSEXPLODE);
+			S_StartSound(explosion, sfx_s3kb4);
+		}
+		P_SetObjectMomZ(mobj, -2*FRACUNIT/3, true);
+	}
+	break;
+	default:
+		break;
+	}
+	return true;
+}
+
+// Angle-to-tracer to trigger a linedef exec
+// See Linedef Exec 457 (Track mobj angle to point)
+static void P_TracerAngleThink(mobj_t *mobj)
+{
+	angle_t ang;
+
+	if (!mobj->tracer)
+		return;
+
+	if (!mobj->extravalue2)
+		return;
+
+	// mobj->lastlook - Don't disable behavior after first failure
+	// mobj->extravalue1 - Angle tolerance
+	// mobj->extravalue2 - Exec tag upon failure
+	// mobj->cvval - Allowable failure delay
+	// mobj->cvmem - Failure timer
+
+	ang = mobj->angle - R_PointToAngle2(mobj->x, mobj->y, mobj->tracer->x, mobj->tracer->y);
+
+	// \todo account for distance between mobj and tracer
+	// Because closer mobjs can be facing beyond the angle tolerance
+	// yet tracer is still in the camera view
+
+	// failure state: mobj is not facing tracer
+	// Reasaonable defaults: ANGLE_67h, ANGLE_292h
+	if (ang >= (angle_t)mobj->extravalue1 && ang <= ANGLE_MAX - (angle_t)mobj->extravalue1)
+	{
+		if (mobj->cvmem)
+			mobj->cvmem--;
+		else
+		{
+			INT32 exectag = mobj->extravalue2; // remember this before we erase the values
+
+			if (mobj->lastlook)
+				mobj->cvmem = mobj->cusval; // reset timer for next failure
+			else
+			{
+				// disable after first failure
+				mobj->eflags &= ~MFE_TRACERANGLE;
+				mobj->lastlook = mobj->extravalue1 = mobj->extravalue2 = mobj->cvmem = mobj->cusval = 0;
+			}
+
+			P_LinedefExecute(exectag, mobj, NULL);
+		}
+	}
+	else
+		mobj->cvmem = mobj->cusval; // reset failure timer
+}
+
+static void P_ArrowThink(mobj_t *mobj)
+{
+	if (mobj->flags & MF_MISSILE)
+	{
+		// Calculate the angle of movement.
+		/*
+			   momz
+			 / |
+		   /   |
+		 /     |
+		0------dist(momx,momy)
+		*/
+
+		fixed_t dist = P_AproxDistance(mobj->momx, mobj->momy);
+		angle_t angle = R_PointToAngle2(0, 0, dist, mobj->momz);
+
+		if (angle > ANG20 && angle <= ANGLE_180)
+			mobj->frame = 2;
+		else if (angle < ANG340 && angle > ANGLE_180)
+			mobj->frame = 0;
+		else
+			mobj->frame = 1;
+
+		if (!(mobj->extravalue1) && (mobj->momz < 0))
+		{
+			mobj->extravalue1 = 1;
+			S_StartSound(mobj, mobj->info->activesound);
+		}
+		if (leveltime & 1)
+		{
+			mobj_t *dust = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_PARTICLE);
+			dust->tics = 18;
+			dust->scalespeed = 4096;
+			dust->destscale = FRACUNIT/32;
+		}
+	}
+	else
+		mobj->flags2 ^= MF2_DONTDRAW;
+}
+
+static void P_BumbleboreThink(mobj_t *mobj)
+{
+	statenum_t st = mobj->state - states;
+	if (st == S_BUMBLEBORE_FLY1 || st == S_BUMBLEBORE_FLY2)
+	{
+		if (!mobj->target)
+			P_SetMobjState(mobj, mobj->info->spawnstate);
+		else if (P_MobjFlip(mobj)*((mobj->z + (mobj->height >> 1)) - (mobj->target->z + (mobj->target->height >> 1))) > 0
+			&& R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) <= 32*FRACUNIT)
+		{
+			mobj->momx >>= 1;
+			mobj->momy >>= 1;
+			if (++mobj->movefactor == 4)
+			{
+				S_StartSound(mobj, mobj->info->seesound);
+				mobj->momx = mobj->momy = mobj->momz = 0;
+				mobj->flags = (mobj->flags|MF_PAIN) & ~MF_NOGRAVITY;
+				P_SetMobjState(mobj, mobj->info->meleestate);
+			}
+		}
+		else
+			mobj->movefactor = 0;
+	}
+	else if (st == S_BUMBLEBORE_RAISE || st == S_BUMBLEBORE_FALL2) // no _FALL1 because it's an 0-tic
+	{
+		if (P_IsObjectOnGround(mobj))
+		{
+			S_StopSound(mobj);
+			S_StartSound(mobj, mobj->info->attacksound);
+			mobj->flags = (mobj->flags | MF_NOGRAVITY) & ~MF_PAIN;
+			mobj->momx = mobj->momy = mobj->momz = 0;
+			P_SetMobjState(mobj, mobj->info->painstate);
+		}
+		else
+		{
+			mobj->angle += ANGLE_22h;
+			mobj->frame = mobj->state->frame + ((mobj->tics & 2) >> 1);
+		}
+	}
+	else if (st == S_BUMBLEBORE_STUCK2 && mobj->tics < TICRATE)
+		mobj->frame = mobj->state->frame + ((mobj->tics & 2) >> 1);
+}
+
+static boolean P_HangsterThink(mobj_t *mobj)
+{
+	statenum_t st = mobj->state - states;
+	//ghost image trail when flying down
+	if (st == S_HANGSTER_SWOOP1 || st == S_HANGSTER_SWOOP2)
+	{
+		P_SpawnGhostMobj(mobj);
+		//curve when in line with target, otherwise curve to avoid crashing into floor
+		if ((mobj->z - mobj->floorz <= 80*FRACUNIT) || (mobj->target && (mobj->z - mobj->target->z <= 80*FRACUNIT)))
+			P_SetMobjState(mobj, (st = S_HANGSTER_ARC1));
+	}
+
+	//swoop arc movement stuff
+	if (st == S_HANGSTER_ARC1)
+	{
+		A_FaceTarget(mobj);
+		P_Thrust(mobj, mobj->angle, 1*FRACUNIT);
+	}
+	else if (st == S_HANGSTER_ARC2)
+		P_Thrust(mobj, mobj->angle, 2*FRACUNIT);
+	else if (st == S_HANGSTER_ARC3)
+		P_Thrust(mobj, mobj->angle, 4*FRACUNIT);
+	//if movement has stopped while flying (like hitting a wall), fly up immediately
+	else if (st == S_HANGSTER_FLY1 && !mobj->momx && !mobj->momy)
+	{
+		mobj->extravalue1 = 0;
+		P_SetMobjState(mobj, S_HANGSTER_ARCUP1);
+	}
+	//after swooping back up, check for ceiling
+	else if ((st == S_HANGSTER_RETURN1 || st == S_HANGSTER_RETURN2) && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height))
+		P_SetMobjState(mobj, (st = S_HANGSTER_RETURN3));
+
+	//should you roost on a ceiling with F_SKY1 as its flat, disappear forever
+	if (st == S_HANGSTER_RETURN3 && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height)
+		&& mobj->subsector->sector->ceilingpic == skyflatnum
+		&& mobj->subsector->sector->ceilingheight == mobj->ceilingz)
+	{
+		P_RemoveMobj(mobj);
+		return false;
+	}
+
+	return true;
+}
+
+static boolean P_JetFume1Think(mobj_t *mobj)
+{
+	fixed_t jetx, jety;
+
+	if (!mobj->target // if you have no target
+		|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
+	{ // then remove yourself as well!
+		P_RemoveMobj(mobj);
+		return false;
+	}
+
+	jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
+	jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
+
+	if (mobj->fuse == 56) // First one
+	{
+		P_UnsetThingPosition(mobj);
+		mobj->x = jetx;
+		mobj->y = jety;
+		if (mobj->target->eflags & MFE_VERTICALFLIP)
+			mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(38*FRACUNIT, mobj->target->scale);
+		else
+			mobj->z = mobj->target->z + FixedMul(38*FRACUNIT, mobj->target->scale);
+		mobj->floorz = mobj->z;
+		mobj->ceilingz = mobj->z + mobj->height;
+		P_SetThingPosition(mobj);
+	}
+	else if (mobj->fuse == 57)
+	{
+		P_UnsetThingPosition(mobj);
+		mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle - ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+		mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle - ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+		if (mobj->target->eflags & MFE_VERTICALFLIP)
+			mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
+		else
+			mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
+		mobj->floorz = mobj->z;
+		mobj->ceilingz = mobj->z + mobj->height;
+		P_SetThingPosition(mobj);
+	}
+	else if (mobj->fuse == 58)
+	{
+		P_UnsetThingPosition(mobj);
+		mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle + ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+		mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle + ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
+		if (mobj->target->eflags & MFE_VERTICALFLIP)
+			mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
+		else
+			mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
+		mobj->floorz = mobj->z;
+		mobj->ceilingz = mobj->z + mobj->height;
+		P_SetThingPosition(mobj);
+	}
+	else if (mobj->fuse == 59)
+	{
+		boolean dashmod = ((mobj->target->flags & MF_PAIN) && (mobj->target->health <= mobj->target->info->damage));
+		jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, -mobj->target->radius);
+		jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, -mobj->target->radius);
+		P_UnsetThingPosition(mobj);
+		mobj->x = jetx;
+		mobj->y = jety;
+		mobj->destscale = mobj->target->scale;
+		if (!(dashmod && mobj->target->state == states + S_METALSONIC_BOUNCE))
+		{
+			mobj->destscale = (mobj->destscale + FixedDiv(R_PointToDist2(0, 0, mobj->target->momx, mobj->target->momy), 36*mobj->target->scale))/3;
+		}
+		if (mobj->target->eflags & MFE_VERTICALFLIP)
+			mobj->z = mobj->target->z + mobj->target->height/2 + mobj->height/2;
+		else
+			mobj->z = mobj->target->z + mobj->target->height/2 - mobj->height/2;
+		mobj->floorz = mobj->z;
+		mobj->ceilingz = mobj->z + mobj->height;
+		P_SetThingPosition(mobj);
+		if (dashmod)
+		{
+			mobj->color = SKINCOLOR_SUNSET;
+			if (mobj->target->movecount == 3 && !mobj->target->reactiontime && (mobj->target->movedir == 0 || mobj->target->movedir == 2))
+				P_SpawnGhostMobj(mobj);
+		}
+		else
+			mobj->color = SKINCOLOR_ICY;
+	}
+	mobj->fuse++;
+	return true;
+}
+
+static boolean P_EggRobo1Think(mobj_t *mobj)
+{
+#define SPECTATORRADIUS (96*mobj->scale)
+	if (!(mobj->flags2 & MF2_STRONGBOX))
+	{
+		mobj->cusval = mobj->x; // eat my SOCs, p_mobj.h warning, we have lua now
+		mobj->cvmem = mobj->y; // ditto
+		mobj->movedir = mobj->angle;
+		mobj->threshold = P_MobjFlip(mobj)*10*mobj->scale;
+		if (mobj->threshold < 0)
+			mobj->threshold += (mobj->ceilingz - mobj->height);
+		else
+			mobj->threshold += mobj->floorz;
+		var1 = 4;
+		A_BossJetFume(mobj);
+		mobj->flags2 |= MF2_STRONGBOX;
+	}
+
+	if (mobj->state == &states[mobj->info->deathstate]) // todo: make map actually set health to 0 for these
+	{
+		if (mobj->movecount)
+		{
+			if (!(--mobj->movecount))
+				S_StartSound(mobj, mobj->info->deathsound);
+		}
+		else
+		{
+			mobj->momz += P_MobjFlip(mobj)*mobj->scale;
+			if (mobj->momz > 0)
+			{
+				if (mobj->z + mobj->momz > mobj->ceilingz + (1000 << FRACBITS))
 				{
-					if (!mobj->target || P_MobjWasRemoved(mobj->target))
-					{
-						if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
-							P_RemoveMobj(mobj->tracer);
-						P_RemoveMobj(mobj);
-						return;
-					}
-					mobj->z = mobj->target->z + mobj->target->momz;
-					if (!(mobj->eflags & MFE_VERTICALFLIP))
-						mobj->z += mobj->target->height;
+					P_RemoveMobj(mobj);
+					return false;
 				}
-				if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
+			}
+			else if (mobj->z + mobj->height + mobj->momz < mobj->floorz - (1000 << FRACBITS))
+			{
+				P_RemoveMobj(mobj);
+				return false;
+			}
+		}
+	}
+	else
+	{
+		fixed_t basex = mobj->cusval, basey = mobj->cvmem;
+
+		if (mobj->spawnpoint && mobj->spawnpoint->options & (MTF_AMBUSH|MTF_OBJECTSPECIAL))
+		{
+			angle_t sideang = mobj->movedir + ((mobj->spawnpoint->options & MTF_AMBUSH) ? ANGLE_90 : -ANGLE_90);
+			fixed_t oscillate = FixedMul(FINESINE(((leveltime * ANG1) >> (ANGLETOFINESHIFT + 2)) & FINEMASK), 250*mobj->scale);
+			basex += P_ReturnThrustX(mobj, sideang, oscillate);
+			basey += P_ReturnThrustY(mobj, sideang, oscillate);
+		}
+
+		mobj->z = mobj->threshold + FixedMul(FINESINE(((leveltime + mobj->movecount)*ANG2 >> (ANGLETOFINESHIFT - 2)) & FINEMASK), 8*mobj->scale);
+		if (mobj->state != &states[mobj->info->meleestate])
+		{
+			boolean didmove = false;
+
+			if (mobj->state == &states[mobj->info->spawnstate])
+			{
+				UINT8 i;
+				fixed_t dist = INT32_MAX;
+
+				for (i = 0; i < MAXPLAYERS; i++)
 				{
-					mobj->tracer->z = mobj->z + P_MobjFlip(mobj)*20*mobj->scale;
-					if (mobj->eflags & MFE_VERTICALFLIP)
-						mobj->tracer->z += mobj->height;
+					fixed_t compdist;
+					if (!playeringame[i])
+						continue;
+					if (players[i].spectator)
+						continue;
+					if (!players[i].mo)
+						continue;
+					if (!players[i].mo->health)
+						continue;
+					if (P_PlayerInPain(&players[i]))
+						continue;
+					if (players[i].mo->z > mobj->z + mobj->height + 8*mobj->scale)
+						continue;
+					if (players[i].mo->z + players[i].mo->height < mobj->z - 8*mobj->scale)
+						continue;
+					compdist = P_AproxDistance(
+						players[i].mo->x + players[i].mo->momx - basex,
+						players[i].mo->y + players[i].mo->momy - basey);
+					if (compdist >= dist)
+						continue;
+					dist = compdist;
+					P_SetTarget(&mobj->target, players[i].mo);
 				}
-				break;
-			case MT_WAVINGFLAG1:
-			case MT_WAVINGFLAG2:
+
+				if (dist < (SPECTATORRADIUS << 1))
 				{
-					fixed_t base = (leveltime<<(FRACBITS+1));
-					mobj_t *seg = mobj->tracer, *prev = mobj;
-					mobj->movedir = mobj->angle
-						+ ((((FINESINE((FixedAngle(base<<1)>>ANGLETOFINESHIFT) & FINEMASK)
-							+ FINESINE((FixedAngle(base<<4)>>ANGLETOFINESHIFT) & FINEMASK))>>1)
-						+ FINESINE((FixedAngle(base*9)>>ANGLETOFINESHIFT) & FINEMASK)
-						+ FINECOSINE(((FixedAngle(base*9))>>ANGLETOFINESHIFT) & FINEMASK))<<12); //*2^12
-					while (seg)
-					{
-						seg->movedir = seg->angle;
-						seg->angle = prev->movedir;
-						P_UnsetThingPosition(seg);
-						seg->x = prev->x + P_ReturnThrustX(prev, prev->angle, prev->radius);
-						seg->y = prev->y + P_ReturnThrustY(prev, prev->angle, prev->radius);
-						seg->z = prev->z + prev->height - (seg->scale>>1);
-						P_SetThingPosition(seg);
-						prev = seg;
-						seg = seg->tracer;
-					}
-				}
-				break;
-			case MT_SPINCUSHION:
-				if (mobj->target && mobj->state-states >= S_SPINCUSHION_AIM1 && mobj->state-states <= S_SPINCUSHION_AIM5)
+					didmove = true;
+					mobj->frame = 3 + ((leveltime & 2) >> 1);
 					mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
-				break;
-			case MT_CRUSHCLAW:
-				if (mobj->state-states == S_CRUSHCLAW_STAY && mobj->target)
-				{
-					mobj_t *chain = mobj->target->target;
-					SINT8 sign = ((mobj->tics & 1) ? mobj->tics : -(SINT8)(mobj->tics));
-					while (chain)
-					{
-						chain->z = chain->watertop + sign*mobj->scale;
-						sign = -sign;
-						chain = chain->target;
-					}
+
+					if (P_AproxDistance(
+						mobj->x - basex,
+						mobj->y - basey)
+						< mobj->scale)
+						S_StartSound(mobj, mobj->info->seesound);
+
+					P_TeleportMove(mobj,
+						(15*(mobj->x >> 4)) + (basex >> 4) + P_ReturnThrustX(mobj, mobj->angle, SPECTATORRADIUS >> 4),
+						(15*(mobj->y >> 4)) + (basey >> 4) + P_ReturnThrustY(mobj, mobj->angle, SPECTATORRADIUS >> 4),
+						mobj->z);
 				}
-				break;
-			case MT_SMASHINGSPIKEBALL:
-				mobj->momx = mobj->momy = 0;
-				if (mobj->state-states == S_SMASHSPIKE_FALL && P_IsObjectOnGround(mobj))
+				else
 				{
-					P_SetMobjState(mobj, S_SMASHSPIKE_STOMP1);
-					S_StartSound(mobj, sfx_spsmsh);
+					angle_t diff = (mobj->movedir - mobj->angle);
+					if (diff > ANGLE_180)
+						diff = InvAngle(InvAngle(diff)/8);
+					else
+						diff /= 8;
+					mobj->angle += diff;
+
+					dist = FINECOSINE(((leveltime + mobj->movecount)*ANG2 >> (ANGLETOFINESHIFT - 2)) & FINEMASK);
+
+					if (abs(dist) < FRACUNIT/2)
+						mobj->frame = 0;
+					else
+						mobj->frame = (dist > 0) ? 1 : 2;
 				}
-				else if (mobj->state-states == S_SMASHSPIKE_RISE2 && P_MobjFlip(mobj)*(mobj->z - mobj->movecount) >= 0)
+			}
+
+			if (!didmove)
+			{
+				if (P_AproxDistance(mobj->x - basex, mobj->y - basey) < mobj->scale)
+					P_TeleportMove(mobj, basex, basey, mobj->z);
+				else
+					P_TeleportMove(mobj,
+					(15*(mobj->x >> 4)) + (basex >> 4),
+						(15*(mobj->y >> 4)) + (basey >> 4),
+						mobj->z);
+			}
+		}
+	}
+	return true;
+#undef SPECTATORRADIUS
+}
+
+static void P_NiGHTSDroneThink(mobj_t *mobj)
+{
+	{
+		// variable setup
+		mobj_t *goalpost = NULL;
+		mobj_t *sparkle = NULL;
+		mobj_t *droneman = NULL;
+
+		boolean flip = mobj->flags2 & MF2_OBJECTFLIP;
+		boolean topaligned = (mobj->flags & MF_SLIDEME) && !(mobj->flags & MF_GRENADEBOUNCE);
+		boolean middlealigned = (mobj->flags & MF_GRENADEBOUNCE) && !(mobj->flags & MF_SLIDEME);
+		boolean bottomoffsetted = !(mobj->flags & MF_SLIDEME) && !(mobj->flags & MF_GRENADEBOUNCE);
+		boolean flipchanged = false;
+
+		fixed_t dronemanoffset, goaloffset, sparkleoffset, droneboxmandiff, dronemangoaldiff;
+
+		if (mobj->target && mobj->target->type == MT_NIGHTSDRONE_GOAL)
+		{
+			goalpost = mobj->target;
+			if (goalpost->target && goalpost->target->type == MT_NIGHTSDRONE_SPARKLING)
+				sparkle = goalpost->target;
+			if (goalpost->tracer && goalpost->tracer->type == MT_NIGHTSDRONE_MAN)
+				droneman = goalpost->tracer;
+		}
+
+		if (!goalpost || !sparkle || !droneman)
+			return;
+
+		// did NIGHTSDRONE position, scale, flip, or flags change? all elements need to be synced
+		droneboxmandiff = max(mobj->height - droneman->height, 0);
+		dronemangoaldiff = max(droneman->height - goalpost->height, 0);
+
+		if (!(goalpost->flags2 & MF2_OBJECTFLIP) && (mobj->flags2 & MF2_OBJECTFLIP))
+		{
+			goalpost->eflags |= MFE_VERTICALFLIP;
+			goalpost->flags2 |= MF2_OBJECTFLIP;
+			sparkle->eflags |= MFE_VERTICALFLIP;
+			sparkle->flags2 |= MF2_OBJECTFLIP;
+			droneman->eflags |= MFE_VERTICALFLIP;
+			droneman->flags2 |= MF2_OBJECTFLIP;
+			flipchanged = true;
+		}
+		else if ((goalpost->flags2 & MF2_OBJECTFLIP) && !(mobj->flags2 & MF2_OBJECTFLIP))
+		{
+			goalpost->eflags &= ~MFE_VERTICALFLIP;
+			goalpost->flags2 &= ~MF2_OBJECTFLIP;
+			sparkle->eflags &= ~MFE_VERTICALFLIP;
+			sparkle->flags2 &= ~MF2_OBJECTFLIP;
+			droneman->eflags &= ~MFE_VERTICALFLIP;
+			droneman->flags2 &= ~MF2_OBJECTFLIP;
+			flipchanged = true;
+		}
+
+		if (goalpost->destscale != mobj->destscale
+			|| goalpost->movefactor != mobj->z
+			|| goalpost->friction != mobj->height
+			|| flipchanged
+			|| goalpost->threshold != (INT32)(mobj->flags & (MF_SLIDEME|MF_GRENADEBOUNCE)))
+		{
+			goalpost->destscale = sparkle->destscale = droneman->destscale = mobj->destscale;
+
+			// straight copy-pasta from P_SpawnMapThing, case MT_NIGHTSDRONE
+			if (!flip)
+			{
+				if (topaligned) // Align droneman to top of hitbox
 				{
-					mobj->momz = 0;
-					P_SetMobjState(mobj, S_SMASHSPIKE_FLOAT);
+					dronemanoffset = droneboxmandiff;
+					goaloffset = dronemangoaldiff/2 + dronemanoffset;
 				}
-				break;
-			case MT_HANGSTER:
+				else if (middlealigned) // Align droneman to center of hitbox
 				{
-					statenum_t st =  mobj->state-states;
-					//ghost image trail when flying down
-					if (st == S_HANGSTER_SWOOP1 || st == S_HANGSTER_SWOOP2)
-					{
-						P_SpawnGhostMobj(mobj);
-						//curve when in line with target, otherwise curve to avoid crashing into floor
-						if ((mobj->z - mobj->floorz <= 80*FRACUNIT) || (mobj->target && (mobj->z - mobj->target->z <= 80*FRACUNIT)))
-							P_SetMobjState(mobj, (st = S_HANGSTER_ARC1));
-					}
-
-					//swoop arc movement stuff
-					if (st == S_HANGSTER_ARC1)
-					{
-						A_FaceTarget(mobj);
-						P_Thrust(mobj, mobj->angle, 1*FRACUNIT);
-					}
-					else if (st == S_HANGSTER_ARC2)
-						P_Thrust(mobj, mobj->angle, 2*FRACUNIT);
-					else if (st == S_HANGSTER_ARC3)
-						P_Thrust(mobj, mobj->angle, 4*FRACUNIT);
-					//if movement has stopped while flying (like hitting a wall), fly up immediately
-					else if (st == S_HANGSTER_FLY1 && !mobj->momx && !mobj->momy)
-					{
-						mobj->extravalue1 = 0;
-						P_SetMobjState(mobj, S_HANGSTER_ARCUP1);
-					}
-					//after swooping back up, check for ceiling
-					else if ((st == S_HANGSTER_RETURN1 || st == S_HANGSTER_RETURN2) && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height))
-						P_SetMobjState(mobj, (st = S_HANGSTER_RETURN3));
-
-					//should you roost on a ceiling with F_SKY1 as its flat, disappear forever
-					if (st == S_HANGSTER_RETURN3 && mobj->momz == 0 && mobj->ceilingz == (mobj->z + mobj->height)
-					&& mobj->subsector->sector->ceilingpic == skyflatnum
-					&& mobj->subsector->sector->ceilingheight == mobj->ceilingz)
-					{
-						P_RemoveMobj(mobj);
-						return;
-					}
+					dronemanoffset = droneboxmandiff/2;
+					goaloffset = dronemangoaldiff/2 + dronemanoffset;
 				}
-				break;
-			case MT_LHRT:
-				mobj->momx = FixedMul(mobj->momx, mobj->extravalue2);
-				mobj->momy = FixedMul(mobj->momy, mobj->extravalue2);
-				break;
-			case MT_EGGCAPSULE:
-				if (!mobj->reactiontime)
+				else if (bottomoffsetted)
 				{
-					// Target nearest player on your mare.
-					// (You can make it float up/down by adding MF_FLOAT,
-					//  but beware level design pitfalls.)
-					fixed_t shortest = 1024*FRACUNIT;
-					INT32 i;
-					P_SetTarget(&mobj->target, NULL);
-					for (i = 0; i < MAXPLAYERS; i++)
-						if (playeringame[i] && players[i].mo
-						&& players[i].mare == mobj->threshold && players[i].spheres > 0)
-						{
-							fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y);
-							if (dist < shortest)
-							{
-								P_SetTarget(&mobj->target, players[i].mo);
-								shortest = dist;
-							}
-						}
+					dronemanoffset = 24*FRACUNIT;
+					goaloffset = dronemangoaldiff + dronemanoffset;
 				}
-				break;
-			case MT_EGGMOBILE2_POGO:
-				if (!mobj->target
-				|| !mobj->target->health
-				|| mobj->target->state == &states[mobj->target->info->spawnstate]
-				|| mobj->target->state == &states[mobj->target->info->raisestate])
+				else
 				{
-					P_RemoveMobj(mobj);
-					return;
+					dronemanoffset = 0;
+					goaloffset = dronemangoaldiff/2 + dronemanoffset;
 				}
-				P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height);
-				break;
-			case MT_HAMMER:
-				if (mobj->z <= mobj->floorz)
+
+				sparkleoffset = goaloffset - FixedMul(15*FRACUNIT, mobj->scale);
+			}
+			else
+			{
+				if (topaligned) // Align droneman to top of hitbox
 				{
-					P_RemoveMobj(mobj);
-					return;
+					dronemanoffset = 0;
+					goaloffset = dronemangoaldiff/2 + dronemanoffset;
 				}
-				break;
-			case MT_KOOPA:
-				P_KoopaThinker(mobj);
-				break;
-			case MT_FIREBALL:
-				if (P_AproxDistance(mobj->momx, mobj->momy) <= 16*FRACUNIT) // Once fireballs lose enough speed, kill them
+				else if (middlealigned) // Align droneman to center of hitbox
 				{
-					P_KillMobj(mobj, NULL, NULL, 0);
-					return;
+					dronemanoffset = droneboxmandiff/2;
+					goaloffset = dronemangoaldiff/2 + dronemanoffset;
 				}
-				break;
-			case MT_REDRING:
-				if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz))
-					&& mobj->flags & MF_MISSILE)
+				else if (bottomoffsetted)
 				{
-					P_ExplodeMissile(mobj);
-					return;
+					dronemanoffset = droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale);
+					goaloffset = dronemangoaldiff + dronemanoffset;
 				}
-				break;
-			case MT_BOSSFLYPOINT:
-				return;
-			case MT_NIGHTSCORE:
-				mobj->color = (UINT8)(leveltime % SKINCOLOR_WHITE);
-				break;
-			case MT_JETFUME1:
+				else
 				{
-					fixed_t jetx, jety;
-
-					if (!mobj->target // if you have no target
-					|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
-					{ // then remove yourself as well!
-						P_RemoveMobj(mobj);
-						return;
-					}
-
-					jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
-					jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, FixedMul(-64*FRACUNIT, mobj->target->scale));
-
-					if (mobj->fuse == 56) // First one
-					{
-						P_UnsetThingPosition(mobj);
-						mobj->x = jetx;
-						mobj->y = jety;
-						if (mobj->target->eflags & MFE_VERTICALFLIP)
-							mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(38*FRACUNIT, mobj->target->scale);
-						else
-							mobj->z = mobj->target->z + FixedMul(38*FRACUNIT, mobj->target->scale);
-						mobj->floorz = mobj->z;
-						mobj->ceilingz = mobj->z+mobj->height;
-						P_SetThingPosition(mobj);
-					}
-					else if (mobj->fuse == 57)
-					{
-						P_UnsetThingPosition(mobj);
-						mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-						mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle-ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-						if (mobj->target->eflags & MFE_VERTICALFLIP)
-							mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
-						else
-							mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
-						mobj->floorz = mobj->z;
-						mobj->ceilingz = mobj->z+mobj->height;
-						P_SetThingPosition(mobj);
-					}
-					else if (mobj->fuse == 58)
-					{
-						P_UnsetThingPosition(mobj);
-						mobj->x = jetx + P_ReturnThrustX(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-						mobj->y = jety + P_ReturnThrustY(mobj->target, mobj->target->angle+ANGLE_90, FixedMul(24*FRACUNIT, mobj->target->scale));
-						if (mobj->target->eflags & MFE_VERTICALFLIP)
-							mobj->z = mobj->target->z + mobj->target->height - mobj->height - FixedMul(12*FRACUNIT, mobj->target->scale);
-						else
-							mobj->z = mobj->target->z + FixedMul(12*FRACUNIT, mobj->target->scale);
-						mobj->floorz = mobj->z;
-						mobj->ceilingz = mobj->z+mobj->height;
-						P_SetThingPosition(mobj);
-					}
-					else if (mobj->fuse == 59)
-					{
-						boolean dashmod = ((mobj->target->flags & MF_PAIN) && (mobj->target->health <= mobj->target->info->damage));
-						jetx = mobj->target->x + P_ReturnThrustX(mobj->target, mobj->target->angle, -mobj->target->radius);
-						jety = mobj->target->y + P_ReturnThrustY(mobj->target, mobj->target->angle, -mobj->target->radius);
-						P_UnsetThingPosition(mobj);
-						mobj->x = jetx;
-						mobj->y = jety;
-						mobj->destscale = mobj->target->scale;
-						if (!(dashmod && mobj->target->state == states+S_METALSONIC_BOUNCE))
-						{
-							mobj->destscale = (mobj->destscale + FixedDiv(R_PointToDist2(0, 0, mobj->target->momx, mobj->target->momy), 36*mobj->target->scale))/3;
-						}
-						if (mobj->target->eflags & MFE_VERTICALFLIP)
-							mobj->z = mobj->target->z + mobj->target->height/2 + mobj->height/2;
-						else
-							mobj->z = mobj->target->z + mobj->target->height/2 - mobj->height/2;
-						mobj->floorz = mobj->z;
-						mobj->ceilingz = mobj->z+mobj->height;
-						P_SetThingPosition(mobj);
-						if (dashmod)
-						{
-							mobj->color = SKINCOLOR_SUNSET;
-							if (mobj->target->movecount == 3 && !mobj->target->reactiontime && (mobj->target->movedir == 0 || mobj->target->movedir == 2))
-								P_SpawnGhostMobj(mobj);
-						}
-						else
-							mobj->color = SKINCOLOR_ICY;
-					}
-					mobj->fuse++;
+					dronemanoffset = droneboxmandiff;
+					goaloffset = dronemangoaldiff/2 + dronemanoffset;
 				}
-				break;
-			case MT_JETFLAME:
-				{
-					if (!mobj->target // if you have no target
-					|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
-					{ // then remove yourself as well!
-						P_RemoveMobj(mobj);
-						return;
-					}
 
-					P_UnsetThingPosition(mobj);
-					mobj->x = mobj->target->x;
-					mobj->y = mobj->target->y;
-					mobj->z = mobj->target->z - 50*mobj->target->scale;
-					mobj->floorz = mobj->z;
-					mobj->ceilingz = mobj->z+mobj->height;
-					P_SetThingPosition(mobj);
-				}
-				break;
-			case MT_EGGROBO1:
-#define SPECTATORRADIUS (96*mobj->scale)
-				{
-					if (!(mobj->flags2 & MF2_STRONGBOX))
-					{
-						mobj->cusval = mobj->x; // eat my SOCs, p_mobj.h warning, we have lua now
-						mobj->cvmem = mobj->y; // ditto
-						mobj->movedir = mobj->angle;
-						mobj->threshold = P_MobjFlip(mobj)*10*mobj->scale;
-						if (mobj->threshold < 0)
-							mobj->threshold += (mobj->ceilingz - mobj->height);
-						else
-							mobj->threshold += mobj->floorz;
-						var1 = 4;
-						A_BossJetFume(mobj);
-						mobj->flags2 |= MF2_STRONGBOX;
-					}
+				sparkleoffset = goaloffset + FixedMul(15*FRACUNIT, mobj->scale);
+			}
 
-					if (mobj->state == &states[mobj->info->deathstate]) // todo: make map actually set health to 0 for these
-					{
-						if (mobj->movecount)
-						{
-							if (!(--mobj->movecount))
-								S_StartSound(mobj, mobj->info->deathsound);
-						}
-						else
-						{
-							mobj->momz += P_MobjFlip(mobj)*mobj->scale;
-							if (mobj->momz > 0)
-							{
-								if (mobj->z + mobj->momz > mobj->ceilingz + (1000<<FRACBITS))
-								{
-									P_RemoveMobj(mobj);
-									return;
-								}
-							}
-							else if (mobj->z + mobj->height + mobj->momz < mobj->floorz - (1000<<FRACBITS))
-							{
-								P_RemoveMobj(mobj);
-								return;
-							}
-						}
-					}
-					else
-					{
+			P_TeleportMove(goalpost, mobj->x, mobj->y, mobj->z + goaloffset);
+			P_TeleportMove(sparkle, mobj->x, mobj->y, mobj->z + sparkleoffset);
+			if (goalpost->movefactor != mobj->z || goalpost->friction != mobj->height)
+			{
+				P_TeleportMove(droneman, mobj->x, mobj->y, mobj->z + dronemanoffset);
+				goalpost->movefactor = mobj->z;
+				goalpost->friction = mobj->height;
+			}
+			goalpost->threshold = mobj->flags & (MF_SLIDEME|MF_GRENADEBOUNCE);
+		}
+		else
+		{
+			if (goalpost->x != mobj->x || goalpost->y != mobj->y)
+			{
+				P_TeleportMove(goalpost, mobj->x, mobj->y, goalpost->z);
+				P_TeleportMove(sparkle, mobj->x, mobj->y, sparkle->z);
+			}
 
-						fixed_t basex = mobj->cusval, basey = mobj->cvmem;
+			if (droneman->x != mobj->x || droneman->y != mobj->y)
+				P_TeleportMove(droneman, mobj->x, mobj->y,
+					droneman->z >= mobj->floorz && droneman->z <= mobj->ceilingz ? droneman->z : mobj->z);
+		}
 
-						if (mobj->spawnpoint && mobj->spawnpoint->options & (MTF_AMBUSH|MTF_OBJECTSPECIAL))
-						{
-							angle_t sideang = mobj->movedir + ((mobj->spawnpoint->options & MTF_AMBUSH) ? ANGLE_90 : -ANGLE_90);
-							fixed_t oscillate = FixedMul(FINESINE(((leveltime*ANG1)>>(ANGLETOFINESHIFT+2)) & FINEMASK), 250*mobj->scale);
-							basex += P_ReturnThrustX(mobj, sideang, oscillate);
-							basey += P_ReturnThrustY(mobj, sideang, oscillate);
-						}
+		// now toggle states!
+		// GOAL mode?
+		if (sparkle->state >= &states[S_NIGHTSDRONE_SPARKLING1] && sparkle->state <= &states[S_NIGHTSDRONE_SPARKLING16])
+		{
+			INT32 i;
+			boolean bonustime = false;
 
-						mobj->z = mobj->threshold + FixedMul(FINESINE(((leveltime + mobj->movecount)*ANG2>>(ANGLETOFINESHIFT-2)) & FINEMASK), 8*mobj->scale);
-						if (mobj->state != &states[mobj->info->meleestate])
-						{
-							boolean didmove = false;
+			for (i = 0; i < MAXPLAYERS; i++)
+				if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE)
+				{
+					bonustime = true;
+					break;
+				}
 
-							if (mobj->state == &states[mobj->info->spawnstate])
-							{
-								UINT8 i;
-								fixed_t dist = INT32_MAX;
+			if (!bonustime)
+			{
+				CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n");
+				if (goalpost && goalpost->state != &states[S_INVISIBLE])
+					P_SetMobjState(goalpost, S_INVISIBLE);
+				if (sparkle && sparkle->state != &states[S_INVISIBLE])
+					P_SetMobjState(sparkle, S_INVISIBLE);
+			}
+		}
+		// Invisible/bouncing mode.
+		else
+		{
+			INT32 i;
+			boolean bonustime = false;
+			fixed_t zcomp;
 
-								for (i = 0; i < MAXPLAYERS; i++)
-								{
-									fixed_t compdist;
-									if (!playeringame[i])
-										continue;
-									if (players[i].spectator)
-										continue;
-									if (!players[i].mo)
-										continue;
-									if (!players[i].mo->health)
-										continue;
-									if (P_PlayerInPain(&players[i]))
-										continue;
-									if (players[i].mo->z > mobj->z + mobj->height + 8*mobj->scale)
-										continue;
-									if (players[i].mo->z + players[i].mo->height < mobj->z - 8*mobj->scale)
-										continue;
-									compdist = P_AproxDistance(
-										players[i].mo->x + players[i].mo->momx - basex,
-										players[i].mo->y + players[i].mo->momy - basey);
-									if (compdist >= dist)
-										continue;
-									dist = compdist;
-									P_SetTarget(&mobj->target, players[i].mo);
-								}
+			// Bouncy bouncy!
+			if (!flip)
+			{
+				if (topaligned)
+					zcomp = droneboxmandiff + mobj->z;
+				else if (middlealigned)
+					zcomp = (droneboxmandiff/2) + mobj->z;
+				else if (bottomoffsetted)
+					zcomp = mobj->z + FixedMul(24*FRACUNIT, mobj->scale);
+				else
+					zcomp = mobj->z;
+			}
+			else
+			{
+				if (topaligned)
+					zcomp = mobj->z;
+				else if (middlealigned)
+					zcomp = (droneboxmandiff/2) + mobj->z;
+				else if (bottomoffsetted)
+					zcomp = mobj->z + droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale);
+				else
+					zcomp = mobj->z + droneboxmandiff;
+			}
 
-								if (dist < (SPECTATORRADIUS<<1))
-								{
-									didmove = true;
-									mobj->frame = 3 + ((leveltime & 2)>>1);
-									mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
-
-									if (P_AproxDistance(
-										mobj->x - basex,
-										mobj->y - basey)
-										< mobj->scale)
-										S_StartSound(mobj, mobj->info->seesound);
-
-									P_TeleportMove(mobj,
-										(15*(mobj->x>>4)) + (basex>>4) + P_ReturnThrustX(mobj, mobj->angle, SPECTATORRADIUS>>4),
-										(15*(mobj->y>>4)) + (basey>>4) + P_ReturnThrustY(mobj, mobj->angle, SPECTATORRADIUS>>4),
-										mobj->z);
-								}
-								else
-								{
-									angle_t diff = (mobj->movedir - mobj->angle);
-									if (diff > ANGLE_180)
-										diff = InvAngle(InvAngle(diff)/8);
-									else
-										diff /= 8;
-									mobj->angle += diff;
-
-									dist = FINECOSINE(((leveltime + mobj->movecount)*ANG2>>(ANGLETOFINESHIFT-2)) & FINEMASK);
-
-									if (abs(dist) < FRACUNIT/2)
-										mobj->frame = 0;
-									else
-										mobj->frame = (dist > 0) ? 1 : 2;
-								}
-							}
+			droneman->angle += ANG10;
+			if (!flip && droneman->z <= zcomp)
+				droneman->momz = FixedMul(5*FRACUNIT, droneman->scale);
+			else if (flip && droneman->z >= zcomp)
+				droneman->momz = FixedMul(-5*FRACUNIT, droneman->scale);
 
-							if (!didmove)
-							{
-								if (P_AproxDistance(mobj->x - basex, mobj->y - basey) < mobj->scale)
-									P_TeleportMove(mobj, basex, basey, mobj->z);
-								else
-									P_TeleportMove(mobj,
-										(15*(mobj->x>>4)) + (basex>>4),
-										(15*(mobj->y>>4)) + (basey>>4),
-										mobj->z);
-							}
-						}
-					}
-				}
-				break;
-#undef SPECTATORRADIUS
-			case MT_EGGROBO1JET:
+			// state switching logic
+			for (i = 0; i < MAXPLAYERS; i++)
+				if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE)
 				{
-					if (!mobj->target || P_MobjWasRemoved(mobj->target) // if you have no target
-					|| (mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
-					{ // then remove yourself as well!
-						P_RemoveMobj(mobj);
-						return;
+					bonustime = true;
+					break;
+				}
+
+			if (bonustime)
+			{
+				CONS_Debug(DBG_NIGHTSBASIC, "Adding goal post\n");
+				if (!(droneman->flags2 & MF2_DONTDRAW))
+					droneman->flags2 |= MF2_DONTDRAW;
+				if (goalpost->state == &states[S_INVISIBLE])
+					P_SetMobjState(goalpost, mobjinfo[goalpost->type].meleestate);
+				if (sparkle->state == &states[S_INVISIBLE])
+					P_SetMobjState(sparkle, mobjinfo[sparkle->type].meleestate);
+			}
+			else if (!G_IsSpecialStage(gamemap))
+			{
+				for (i = 0; i < MAXPLAYERS; i++)
+					if (playeringame[i] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+					{
+						bonustime = true; // variable reuse
+						break;
 					}
 
-					mobj->flags2 ^= MF2_DONTDRAW;
-
-					P_UnsetThingPosition(mobj);
-					mobj->x = mobj->target->x + P_ReturnThrustX(mobj, mobj->target->angle+ANGLE_90, mobj->movefactor*mobj->target->scale) - P_ReturnThrustX(mobj, mobj->target->angle, 19*mobj->target->scale);
-					mobj->y = mobj->target->y + P_ReturnThrustY(mobj, mobj->target->angle+ANGLE_90, mobj->movefactor*mobj->target->scale) - P_ReturnThrustY(mobj, mobj->target->angle, 19*mobj->target->scale);
-					mobj->z = mobj->target->z;
-					if (mobj->target->eflags & MFE_VERTICALFLIP)
-						mobj->z += (mobj->target->height - mobj->height);
-					mobj->floorz = mobj->z;
-					mobj->ceilingz = mobj->z+mobj->height;
-					P_SetThingPosition(mobj);
+				if (bonustime)
+				{
+					// show droneman if at least one player is non-nights
+					if (goalpost->state != &states[S_INVISIBLE])
+						P_SetMobjState(goalpost, S_INVISIBLE);
+					if (sparkle->state != &states[S_INVISIBLE])
+						P_SetMobjState(sparkle, S_INVISIBLE);
+					if (droneman->state != &states[mobjinfo[droneman->type].meleestate])
+						P_SetMobjState(droneman, mobjinfo[droneman->type].meleestate);
+					if (droneman->flags2 & MF2_DONTDRAW)
+						droneman->flags2 &= ~MF2_DONTDRAW;
 				}
-				break;
-			case MT_NIGHTSDRONE:
+				else
 				{
-					// variable setup
-					mobj_t *goalpost = NULL;
-					mobj_t *sparkle = NULL;
-					mobj_t *droneman = NULL;
+					// else, hide it
+					if (!(droneman->flags2 & MF2_DONTDRAW))
+						droneman->flags2 |= MF2_DONTDRAW;
+				}
+			}
+		}
+	}
+}
 
-					boolean flip = mobj->flags2 & MF2_OBJECTFLIP;
-					boolean topaligned = (mobj->flags & MF_SLIDEME) && !(mobj->flags & MF_GRENADEBOUNCE);
-					boolean middlealigned = (mobj->flags & MF_GRENADEBOUNCE) && !(mobj->flags & MF_SLIDEME);
-					boolean bottomoffsetted = !(mobj->flags & MF_SLIDEME) && !(mobj->flags & MF_GRENADEBOUNCE);
-					boolean flipchanged = false;
+static boolean P_TurretThink(mobj_t *mobj)
+{
+	P_MobjCheckWater(mobj);
+	P_CheckPosition(mobj, mobj->x, mobj->y);
+	if (P_MobjWasRemoved(mobj))
+		return false;
+	mobj->floorz = tmfloorz;
+	mobj->ceilingz = tmceilingz;
+	mobj->floorrover = tmfloorrover;
+	mobj->ceilingrover = tmceilingrover;
 
-					fixed_t dronemanoffset, goaloffset, sparkleoffset, droneboxmandiff, dronemangoaldiff;
+	if ((mobj->eflags & MFE_UNDERWATER) && mobj->health > 0)
+	{
+		P_SetMobjState(mobj, mobj->info->deathstate);
+		mobj->health = 0;
+		mobj->flags2 &= ~MF2_FIRING;
+	}
+	else if (mobj->health > 0 && mobj->z + mobj->height > mobj->ceilingz) // Crushed
+	{
+		INT32 i, j;
+		fixed_t ns;
+		fixed_t x, y, z;
+		mobj_t* mo2;
 
-					if (mobj->target && mobj->target->type == MT_NIGHTSDRONE_GOAL)
-					{
-						goalpost = mobj->target;
-						if (goalpost->target && goalpost->target->type == MT_NIGHTSDRONE_SPARKLING)
-							sparkle = goalpost->target;
-						if (goalpost->tracer && goalpost->tracer->type == MT_NIGHTSDRONE_MAN)
-							droneman = goalpost->tracer;
-					}
+		z = mobj->subsector->sector->floorheight + FixedMul(64*FRACUNIT, mobj->scale);
+		for (j = 0; j < 2; j++)
+		{
+			for (i = 0; i < 32; i++)
+			{
+				const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
+				ns = FixedMul(64*FRACUNIT, mobj->scale);
+				x = mobj->x + FixedMul(FINESINE(fa), ns);
+				y = mobj->y + FixedMul(FINECOSINE(fa), ns);
 
-					if (!goalpost || !sparkle || !droneman)
-						break;
+				mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
+				ns = FixedMul(16*FRACUNIT, mobj->scale);
+				mo2->momx = FixedMul(FINESINE(fa), ns);
+				mo2->momy = FixedMul(FINECOSINE(fa), ns);
+			}
+			z -= FixedMul(32*FRACUNIT, mobj->scale);
+		}
+		P_SetMobjState(mobj, mobj->info->deathstate);
+		mobj->health = 0;
+		mobj->flags2 &= ~MF2_FIRING;
+	}
+	return true;
+}
 
-					// did NIGHTSDRONE position, scale, flip, or flags change? all elements need to be synced
-					droneboxmandiff = max(mobj->height - droneman->height, 0);
-					dronemangoaldiff = max(droneman->height - goalpost->height, 0);
+static void P_SaloonDoorThink(mobj_t *mobj)
+{
+	fixed_t x = mobj->tracer->x;
+	fixed_t y = mobj->tracer->y;
+	fixed_t z = mobj->tracer->z;
+	angle_t oang = FixedAngle(mobj->extravalue1);
+	angle_t fa = (oang >> ANGLETOFINESHIFT) & FINEMASK;
+	fixed_t c0 = -96*FINECOSINE(fa);
+	fixed_t s0 = -96*FINESINE(fa);
+	angle_t fma;
+	fixed_t c, s;
+	angle_t angdiff;
+
+	// Adjust angular speed
+	fixed_t da = AngleFixed(mobj->angle - oang);
+	if (da > 180*FRACUNIT)
+		da -= 360*FRACUNIT;
+	mobj->extravalue2 = 8*(mobj->extravalue2 - da/32)/9;
+
+	// Update angle
+	mobj->angle += FixedAngle(mobj->extravalue2);
+
+	angdiff = mobj->angle - FixedAngle(mobj->extravalue1);
+	if (angdiff > (ANGLE_90 - ANG2) && angdiff < ANGLE_180)
+	{
+		mobj->angle = FixedAngle(mobj->extravalue1) + (ANGLE_90 - ANG2);
+		mobj->extravalue2 /= 2;
+	}
+	else if (angdiff < (ANGLE_270 + ANG2) && angdiff >= ANGLE_180)
+	{
+		mobj->angle = FixedAngle(mobj->extravalue1) + (ANGLE_270 + ANG2);
+		mobj->extravalue2 /= 2;
+	}
+
+	// Update position
+	fma = (mobj->angle >> ANGLETOFINESHIFT) & FINEMASK;
+	c = 48*FINECOSINE(fma);
+	s = 48*FINESINE(fma);
+	P_TeleportMove(mobj, x + c0 + c, y + s0 + s, z);
+}
 
-					if (!(goalpost->flags2 & MF2_OBJECTFLIP) && (mobj->flags2 & MF2_OBJECTFLIP))
-					{
-						goalpost->eflags |= MFE_VERTICALFLIP;
-						goalpost->flags2 |= MF2_OBJECTFLIP;
-						sparkle->eflags |= MFE_VERTICALFLIP;
-						sparkle->flags2 |= MF2_OBJECTFLIP;
-						droneman->eflags |= MFE_VERTICALFLIP;
-						droneman->flags2 |= MF2_OBJECTFLIP;
-						flipchanged = true;
-					}
-					else if ((goalpost->flags2 & MF2_OBJECTFLIP) && !(mobj->flags2 & MF2_OBJECTFLIP))
-					{
-						goalpost->eflags &= ~MFE_VERTICALFLIP;
-						goalpost->flags2 &= ~MF2_OBJECTFLIP;
-						sparkle->eflags &= ~MFE_VERTICALFLIP;
-						sparkle->flags2 &= ~MF2_OBJECTFLIP;
-						droneman->eflags &= ~MFE_VERTICALFLIP;
-						droneman->flags2 &= ~MF2_OBJECTFLIP;
-						flipchanged = true;
-					}
+static void P_PyreFlyThink(mobj_t *mobj)
+{
+	fixed_t hdist;
 
-					if (goalpost->destscale != mobj->destscale
-					    || goalpost->movefactor != mobj->z
-						|| goalpost->friction != mobj->height
-						|| flipchanged
-						|| goalpost->threshold != (INT32)(mobj->flags & (MF_SLIDEME | MF_GRENADEBOUNCE)))
-					{
-						goalpost->destscale = sparkle->destscale = droneman->destscale = mobj->destscale;
+	mobj->extravalue1 = (mobj->extravalue1 + 3) % 360;
+	mobj->z += FINESINE(((mobj->extravalue1 * ANG1) >> ANGLETOFINESHIFT) & FINEMASK);
 
-						// straight copy-pasta from P_SpawnMapThing, case MT_NIGHTSDRONE
-						if (!flip)
-						{
-							if (topaligned) // Align droneman to top of hitbox
-							{
-								dronemanoffset = droneboxmandiff;
-								goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-							}
-							else if (middlealigned) // Align droneman to center of hitbox
-							{
-								dronemanoffset = droneboxmandiff / 2;
-								goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-							}
-							else if (bottomoffsetted)
-							{
-								dronemanoffset = 24*FRACUNIT;
-								goaloffset = dronemangoaldiff + dronemanoffset;
-							}
-							else
-							{
-								dronemanoffset = 0;
-								goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-							}
+	if (!(mobj->flags2 & MF2_BOSSNOTRAP))
+		P_LookForPlayers(mobj, true, false, 1500*FRACUNIT);
+
+	if (!mobj->target)
+		return;
+
+	if (mobj->extravalue2 == 1)
+		P_PyreFlyBurn(mobj, 0, 20, MT_SMOKE, 4*FRACUNIT);
+	else if (mobj->extravalue2 == 2)
+	{
+		INT32 fireradius = min(100 - mobj->fuse, 52);
+		P_PyreFlyBurn(mobj, P_RandomRange(0, fireradius) << FRACBITS, 20, MT_FLAMEPARTICLE, 4*FRACUNIT);
+		P_PyreFlyBurn(mobj, fireradius*FRACUNIT, 40, MT_PYREFLY_FIRE, 0);
+	}
+
+	hdist = R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+
+	if (hdist > 1500*FRACUNIT)
+	{
+		mobj->flags2 &= ~MF2_BOSSNOTRAP;
+		P_SetTarget(&mobj->target, NULL);
+		return;
+	}
 
-							sparkleoffset = goaloffset - FixedMul(15*FRACUNIT, mobj->scale);
-						}
-						else
-						{
-							if (topaligned) // Align droneman to top of hitbox
-							{
-								dronemanoffset = 0;
-								goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-							}
-							else if (middlealigned) // Align droneman to center of hitbox
-							{
-								dronemanoffset = droneboxmandiff / 2;
-								goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-							}
-							else if (bottomoffsetted)
-							{
-								dronemanoffset = droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale);
-								goaloffset = dronemangoaldiff + dronemanoffset;
-							}
-							else
-							{
-								dronemanoffset = droneboxmandiff;
-								goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-							}
+	if (!(mobj->flags2 & MF2_BOSSNOTRAP) && hdist <= 450*FRACUNIT)
+		mobj->flags2 |= MF2_BOSSNOTRAP;
 
-							sparkleoffset = goaloffset + FixedMul(15*FRACUNIT, mobj->scale);
-						}
+	if (!(mobj->flags2 & MF2_BOSSNOTRAP))
+		return;
 
-						P_TeleportMove(goalpost, mobj->x, mobj->y, mobj->z + goaloffset);
-						P_TeleportMove(sparkle, mobj->x, mobj->y, mobj->z + sparkleoffset);
-						if (goalpost->movefactor != mobj->z || goalpost->friction != mobj->height)
-						{
-							P_TeleportMove(droneman, mobj->x, mobj->y, mobj->z + dronemanoffset);
-							goalpost->movefactor = mobj->z;
-							goalpost->friction = mobj->height;
-						}
-						goalpost->threshold = mobj->flags & (MF_SLIDEME | MF_GRENADEBOUNCE);
-					}
-					else
-					{
-						if (goalpost->x != mobj->x || goalpost->y != mobj->y)
-						{
-							P_TeleportMove(goalpost, mobj->x, mobj->y, goalpost->z);
-							P_TeleportMove(sparkle, mobj->x, mobj->y, sparkle->z);
-						}
+	if (hdist < 1000*FRACUNIT)
+	{
+		//Aim for player z position. If too close to floor/ceiling, aim just above/below them.
+		fixed_t destz = min(max(mobj->target->z, mobj->target->floorz + 70*FRACUNIT), mobj->target->ceilingz - 80*FRACUNIT - mobj->height);
+		fixed_t dist = P_AproxDistance(hdist, destz - mobj->z);
+		P_InstaThrust(mobj, R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y), 2*FRACUNIT);
+		mobj->momz = FixedMul(FixedDiv(destz - mobj->z, dist), 2*FRACUNIT);
+	}
+	else
+	{
+		mobj->momx = 0;
+		mobj->momy = 0;
+		mobj->momz = 0;
+	}
+}
 
-						if (droneman->x != mobj->x || droneman->y != mobj->y)
-							P_TeleportMove(droneman, mobj->x, mobj->y,
-							               droneman->z >= mobj->floorz && droneman->z <= mobj->ceilingz ? droneman->z : mobj->z);
-					}
+static void P_PterabyteThink(mobj_t *mobj)
+{
+	if (mobj->extravalue1 & 4) // Cooldown after grabbing
+	{
+		if (mobj->movefactor)
+			mobj->movefactor--;
+		else
+		{
+			P_SetTarget(&mobj->target, NULL);
+			mobj->extravalue1 &= 3;
+		}
+	}
 
-					// now toggle states!
-					// GOAL mode?
-					if (sparkle->state >= &states[S_NIGHTSDRONE_SPARKLING1] && sparkle->state <= &states[S_NIGHTSDRONE_SPARKLING16])
-					{
-						INT32 i;
-						boolean bonustime = false;
+	if ((mobj->extravalue1 & 3) == 0) // Hovering
+	{
+		fixed_t vdist, hdist, time;
+		fixed_t hspeed = 3*mobj->info->speed;
+		angle_t fa;
 
-						for (i = 0; i < MAXPLAYERS; i++)
-							if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE)
-							{
-								bonustime = true;
-								break;
-							}
+		var1 = 1;
+		var2 = 0;
+		A_CapeChase(mobj);
 
-						if (!bonustime)
-						{
-							CONS_Debug(DBG_NIGHTSBASIC, "Removing goal post\n");
-							if (goalpost && goalpost->state != &states[S_INVISIBLE])
-								P_SetMobjState(goalpost, S_INVISIBLE);
-							if (sparkle && sparkle->state != &states[S_INVISIBLE])
-								P_SetMobjState(sparkle, S_INVISIBLE);
-						}
-					}
-					// Invisible/bouncing mode.
-					else
-					{
-						INT32 i;
-						boolean bonustime = false;
-						fixed_t zcomp;
+		if (mobj->target)
+			return; // Still carrying a player or in cooldown
 
-						// Bouncy bouncy!
-						if (!flip)
-						{
-							if (topaligned)
-								zcomp = droneboxmandiff + mobj->z;
-							else if (middlealigned)
-								zcomp = (droneboxmandiff / 2) + mobj->z;
-							else if (bottomoffsetted)
-								zcomp = mobj->z + FixedMul(24*FRACUNIT, mobj->scale);
-							else
-								zcomp = mobj->z;
-						}
-						else
-						{
-							if (topaligned)
-								zcomp = mobj->z;
-							else if (middlealigned)
-								zcomp = (droneboxmandiff / 2) + mobj->z;
-							else if (bottomoffsetted)
-								zcomp = mobj->z + droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale);
-							else
-								zcomp = mobj->z + droneboxmandiff;
-						}
+		P_LookForPlayers(mobj, true, false, 256*FRACUNIT);
 
-						droneman->angle += ANG10;
-						if (!flip && droneman->z <= zcomp)
-							droneman->momz = FixedMul(5*FRACUNIT, droneman->scale);
-						else if (flip && droneman->z >= zcomp)
-							droneman->momz = FixedMul(-5*FRACUNIT, droneman->scale);
+		if (!mobj->target)
+			return;
 
-						// state switching logic
-						for (i = 0; i < MAXPLAYERS; i++)
-							if (playeringame[i] && players[i].bonustime && players[i].powers[pw_carry] == CR_NIGHTSMODE)
-							{
-								bonustime = true;
-								break;
-							}
+		if (mobj->target->player->powers[pw_flashing])
+		{
+			P_SetTarget(&mobj->target, NULL);
+			return;
+		}
 
-						if (bonustime)
-						{
-							CONS_Debug(DBG_NIGHTSBASIC, "Adding goal post\n");
-							if (!(droneman->flags2 & MF2_DONTDRAW))
-								droneman->flags2 |= MF2_DONTDRAW;
-							if (goalpost->state == &states[S_INVISIBLE])
-								P_SetMobjState(goalpost, mobjinfo[goalpost->type].meleestate);
-							if (sparkle->state == &states[S_INVISIBLE])
-								P_SetMobjState(sparkle, mobjinfo[sparkle->type].meleestate);
-						}
-						else if (!G_IsSpecialStage(gamemap))
-						{
-							for (i = 0; i < MAXPLAYERS; i++)
-								if (playeringame[i] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
-								{
-									bonustime = true; // variable reuse
-									break;
-								}
+		vdist = mobj->z - mobj->target->z - mobj->target->height;
+		if (P_MobjFlip(mobj)*vdist <= 0)
+		{
+			P_SetTarget(&mobj->target, NULL);
+			return;
+		}
 
-							if (bonustime)
-							{
-								// show droneman if at least one player is non-nights
-								if (goalpost->state != &states[S_INVISIBLE])
-									P_SetMobjState(goalpost, S_INVISIBLE);
-								if (sparkle->state != &states[S_INVISIBLE])
-									P_SetMobjState(sparkle, S_INVISIBLE);
-								if (droneman->state != &states[mobjinfo[droneman->type].meleestate])
-									P_SetMobjState(droneman, mobjinfo[droneman->type].meleestate);
-								if (droneman->flags2 & MF2_DONTDRAW)
-									droneman->flags2 &= ~MF2_DONTDRAW;
-							}
-							else
-							{
-								// else, hide it
-								if (!(droneman->flags2 & MF2_DONTDRAW))
-									droneman->flags2 |= MF2_DONTDRAW;
-							}
-						}
-					}
-				}
-				break;
-			case MT_PLAYER:
-				if (mobj->player)
-					P_PlayerMobjThinker(mobj);
-				return;
-			case MT_SKIM:
-				// check mobj against possible water content, before movement code
-				P_MobjCheckWater(mobj);
+		hdist = R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+		if (hdist > 450*FRACUNIT)
+		{
+			P_SetTarget(&mobj->target, NULL);
+			return;
+		}
 
-				// Keep Skim at water surface
-				if (mobj->z <= mobj->watertop)
-				{
-					mobj->flags |= MF_NOGRAVITY;
-					if (mobj->z < mobj->watertop)
-					{
-						if (mobj->watertop - mobj->z <= FixedMul(mobj->info->speed*FRACUNIT, mobj->scale))
-							mobj->z = mobj->watertop;
-						else
-							mobj->momz = FixedMul(mobj->info->speed*FRACUNIT, mobj->scale);
-					}
-				}
-				else
-				{
-					mobj->flags &= ~MF_NOGRAVITY;
-					if (mobj->z > mobj->watertop && mobj->z - mobj->watertop < FixedMul(MAXSTEPMOVE, mobj->scale))
-						mobj->z = mobj->watertop;
-				}
-				break;
-			case MT_RING:
-			case MT_REDTEAMRING:
-			case MT_BLUETEAMRING:
-				P_KillRingsInLava(mobj);
-				if (P_MobjWasRemoved(mobj))
-					return;
-				/* FALLTHRU */
-			case MT_COIN:
-			case MT_BLUESPHERE:
-			case MT_BOMBSPHERE:
-			case MT_NIGHTSCHIP:
-			case MT_NIGHTSSTAR:
-				// No need to check water. Who cares?
-				P_RingThinker(mobj);
-				if (mobj->flags2 & MF2_NIGHTSPULL)
-					P_NightsItemChase(mobj);
-				else
-					A_AttractChase(mobj);
-				return;
-			// Flung items
-			case MT_FLINGRING:
-				P_KillRingsInLava(mobj);
-				if (P_MobjWasRemoved(mobj))
-					return;
-				/* FALLTHRU */
-			case MT_FLINGCOIN:
-			case MT_FLINGBLUESPHERE:
-			case MT_FLINGNIGHTSCHIP:
-				if (mobj->flags2 & MF2_NIGHTSPULL)
-					P_NightsItemChase(mobj);
+		P_SetMobjState(mobj, S_PTERABYTE_SWOOPDOWN);
+		mobj->extravalue1++;
+		S_StartSound(mobj, mobj->info->attacksound);
+		time = FixedDiv(hdist, hspeed);
+		mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+		fa = (mobj->angle >> ANGLETOFINESHIFT) & FINEMASK;
+		mobj->momx = FixedMul(FINECOSINE(fa), hspeed);
+		mobj->momy = FixedMul(FINESINE(fa), hspeed);
+		mobj->momz = -2*FixedDiv(vdist, time);
+		mobj->extravalue2 = -FixedDiv(mobj->momz, time); //Z accel
+		mobj->movecount = time >> FRACBITS;
+		mobj->reactiontime = mobj->movecount;
+	}
+	else if ((mobj->extravalue1 & 3) == 1) // Swooping
+	{
+		mobj->reactiontime--;
+		mobj->momz += mobj->extravalue2;
+		if (mobj->reactiontime)
+			return;
+
+		if (mobj->state - states == S_PTERABYTE_SWOOPDOWN)
+		{
+			P_SetMobjState(mobj, S_PTERABYTE_SWOOPUP);
+			mobj->reactiontime = mobj->movecount;
+		}
+		else if (mobj->state - states == S_PTERABYTE_SWOOPUP)
+		{
+			P_SetMobjState(mobj, S_PTERABYTE_FLY1);
+			mobj->extravalue1++;
+			if (mobj->target && mobj->target->tracer != mobj)
+				P_SetTarget(&mobj->target, NULL); // Failed to grab the target
+			mobj->momx = mobj->momy = mobj->momz = 0;
+		}
+	}
+	else // Returning
+	{
+		var1 = 2*mobj->info->speed;
+		var2 = 1;
+		A_HomingChase(mobj);
+		if (P_AproxDistance(mobj->x - mobj->tracer->x, mobj->y - mobj->tracer->y) <= mobj->info->speed)
+		{
+			mobj->extravalue1 -= 2;
+			mobj->momx = mobj->momy = mobj->momz = 0;
+		}
+	}
+}
+
+static void P_DragonbomberThink(mobj_t *mobj)
+{
+#define DRAGONTURNSPEED ANG2
+	mobj->movecount = (mobj->movecount + 9) % 360;
+	P_SetObjectMomZ(mobj, 4*FINESINE(((mobj->movecount*ANG1) >> ANGLETOFINESHIFT) & FINEMASK), false);
+	if (mobj->threshold > 0) // are we dropping mines?
+	{
+		mobj->threshold--;
+		if (mobj->threshold == 0) // if the timer hits 0, look for a mine to drop!
+		{
+			mobj_t *segment = mobj;
+			while (segment->tracer != NULL && !P_MobjWasRemoved(segment->tracer) && segment->tracer->state == &states[segment->tracer->info->spawnstate])
+				segment = segment->tracer;
+			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);
+				P_SetMobjState(segment, segment->info->raisestate);
+				mobj->threshold = mobj->info->painchance;
+			}
+		}
+	}
+	if (mobj->target) // Are we chasing a player?
+	{
+		fixed_t dist = P_AproxDistance(mobj->x - mobj->target->x, mobj->y - mobj->target->y);
+		if (dist > 2000*mobj->scale) // Not anymore!
+			P_SetTarget(&mobj->target, NULL);
+		else
+		{
+			fixed_t vspeed = FixedMul(mobj->info->speed >> 3, mobj->scale);
+			fixed_t z = mobj->target->z + (mobj->height >> 1) + (mobj->flags & MFE_VERTICALFLIP ? -128*mobj->scale : 128*mobj->scale + mobj->target->height);
+			angle_t diff = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) - mobj->angle;
+			if (diff > ANGLE_180)
+				mobj->angle -= DRAGONTURNSPEED;
+			else
+				mobj->angle += DRAGONTURNSPEED;
+			if (!mobj->threshold && dist < 512*mobj->scale) // Close enough to drop bombs
+			{
+				mobj->threshold = mobj->info->painchance;
+			}
+			mobj->momz += max(min(z - mobj->z, vspeed), -vspeed);
+		}
+	}
+	else // Can we find a player to chase?
+	{
+		if (mobj->tracer == NULL || mobj->tracer->state != &states[mobj->tracer->info->spawnstate]
+			|| !P_LookForPlayers(mobj, true, false, 2000*mobj->scale)) // if not, circle around the spawnpoint
+		{
+			if (!mobj->spawnpoint) // unless we don't have one, in which case uhhh just circle around wherever we currently are I guess??
+				mobj->angle += DRAGONTURNSPEED;
+			else
+			{
+				fixed_t vspeed = FixedMul(mobj->info->speed >> 3, mobj->scale);
+				fixed_t x = mobj->spawnpoint->x << FRACBITS;
+				fixed_t y = mobj->spawnpoint->y << FRACBITS;
+				fixed_t z = mobj->spawnpoint->z << FRACBITS;
+				angle_t diff = R_PointToAngle2(mobj->x, mobj->y, x, y) - mobj->angle;
+				if (diff > ANGLE_180)
+					mobj->angle -= DRAGONTURNSPEED;
 				else
-					A_AttractChase(mobj);
-				break;
-			case MT_EMBLEM:
-				if (mobj->flags2 & MF2_NIGHTSPULL)
-					P_NightsItemChase(mobj);
-				break;
-			case MT_SHELL:
-				if (mobj->threshold && mobj->threshold != TICRATE)
-					mobj->threshold--;
+					mobj->angle += DRAGONTURNSPEED;
+				mobj->momz += max(min(z - mobj->z, vspeed), -vspeed);
+			}
+		}
+	}
+	P_InstaThrust(mobj, mobj->angle, FixedMul(mobj->info->speed, mobj->scale));
+#undef DRAGONTURNSPEED
+}
 
-				if (mobj->threshold >= TICRATE)
-				{
-					mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h);
-					P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale));
-				}
-				break;
-			case MT_TURRET:
-				P_MobjCheckWater(mobj);
-				P_CheckPosition(mobj, mobj->x, mobj->y);
-				if (P_MobjWasRemoved(mobj))
-					return;
-				mobj->floorz = tmfloorz;
-				mobj->ceilingz = tmceilingz;
-				mobj->floorrover = tmfloorrover;
-				mobj->ceilingrover = tmceilingrover;
+static boolean P_MobjRegularThink(mobj_t *mobj)
+{
+	if ((mobj->flags & MF_ENEMY) && (mobj->state->nextstate == mobj->info->spawnstate && mobj->tics == 1))
+		mobj->flags2 &= ~MF2_FRET;
 
-				if ((mobj->eflags & MFE_UNDERWATER) && mobj->health > 0)
-				{
-					P_SetMobjState(mobj, mobj->info->deathstate);
-					mobj->health = 0;
-					mobj->flags2 &= ~MF2_FIRING;
-				}
-				else if (mobj->health > 0 && mobj->z + mobj->height > mobj->ceilingz) // Crushed
-				{
-					INT32 i,j;
-					fixed_t ns;
-					fixed_t x,y,z;
-					mobj_t *mo2;
+	if (mobj->eflags & MFE_TRACERANGLE)
+		P_TracerAngleThink(mobj);
 
-					z = mobj->subsector->sector->floorheight + FixedMul(64*FRACUNIT, mobj->scale);
-					for (j = 0; j < 2; j++)
-					{
-						for (i = 0; i < 32; i++)
-						{
-							const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
-							ns = FixedMul(64 * FRACUNIT, mobj->scale);
-							x = mobj->x + FixedMul(FINESINE(fa),ns);
-							y = mobj->y + FixedMul(FINECOSINE(fa),ns);
-
-							mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
-							ns = FixedMul(16 * FRACUNIT, mobj->scale);
-							mo2->momx = FixedMul(FINESINE(fa),ns);
-							mo2->momy = FixedMul(FINECOSINE(fa),ns);
-						}
-						z -= FixedMul(32*FRACUNIT, mobj->scale);
-					}
-					P_SetMobjState(mobj, mobj->info->deathstate);
-					mobj->health = 0;
-					mobj->flags2 &= ~MF2_FIRING;
-				}
-				break;
-			case MT_BLUEFLAG:
-			case MT_REDFLAG:
-				{
-					sector_t *sec2;
-					sec2 = P_ThingOnSpecial3DFloor(mobj);
-					if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2))
-						mobj->fuse = 1; // Return to base.
-					break;
-				}
-			case MT_CANNONBALL:
-#ifdef FLOORSPLATS
-				R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x,
-					mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE);
+	switch (mobj->type)
+	{
+	case MT_WALLSPIKEBASE:
+		if (!mobj->target) {
+			P_RemoveMobj(mobj);
+			return false;
+		}
+		mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK);
+#if 0
+		if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle
+		{
+			mobj_t* target = mobj->target; // shortcut
+			const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale);
+			P_UnsetThingPosition(mobj);
+			mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius);
+			mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius);
+			P_SetThingPosition(mobj);
+			mobj->angle = target->angle + ANGLE_90;
+		}
 #endif
-				break;
-			case MT_SPINDUST: // Spindash dust
-					mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
-					mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
-					//mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank
-					if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust!
-					{
-						P_MobjCheckWater(mobj);
-						if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT
-							&& mobj->z+mobj->height >= mobj->watertop - 5*FRACUNIT)
-							mobj->flags2 |= MF2_DONTDRAW;
-					}
-				break;
-			case MT_TRAINDUSTSPAWNER:
-				if (leveltime % 5 == 0) {
-					mobj_t *traindust = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PARTICLE);
-					traindust->flags = MF_SCENERY;
-					P_SetMobjState(traindust, S_TRAINDUST);
-					traindust->frame = P_RandomRange(0, 8) | FF_TRANS90;
-					traindust->angle = mobj->angle;
-					traindust->tics = TICRATE * 4;
-					traindust->destscale = FRACUNIT * 64;
-					traindust->scalespeed = FRACUNIT / 24;
-					P_SetScale(traindust, FRACUNIT * 6);
-				}
-				break;
-			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);
-					P_SetMobjState(steam, S_TRAINSTEAM);
-					steam->frame = P_RandomRange(0, 1) | FF_TRANS90;
-					steam->tics = TICRATE * 8;
-					steam->destscale = FRACUNIT * 64;
-					steam->scalespeed = FRACUNIT / 8;
-					P_SetScale(steam, FRACUNIT * 16);
-					steam->momx = P_SignedRandom() * 32;
-					steam->momy = -64 * FRACUNIT;
-					steam->momz = 2 * FRACUNIT;
-				}
-				break;
-			case MT_CANARIVORE_GAS:
-				{
-					fixed_t momz;
+		break;
+	case MT_FALLINGROCK:
+		// Despawn rocks here in case zmovement code can't do so (blame slopes)
+		if (!mobj->momx && !mobj->momy && !mobj->momz
+			&& ((mobj->eflags & MFE_VERTICALFLIP) ?
+				mobj->z + mobj->height >= mobj->ceilingz
+				: mobj->z <= mobj->floorz))
+		{
+			P_RemoveMobj(mobj);
+			return false;
+		}
+		P_MobjCheckWater(mobj);
+		break;
+	case MT_ARROW:
+		P_ArrowThink(mobj);
+		break;
+	case MT_EMERALDSPAWN:
+		if (mobj->threshold)
+		{
+			mobj->threshold--;
 
-					if (mobj->flags2 & MF2_AMBUSH)
-					{
-						mobj->momx = FixedMul(mobj->momx, 50 * FRACUNIT / 51);
-						mobj->momy = FixedMul(mobj->momy, 50 * FRACUNIT / 51);
-						break;
-					}
+			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);
+			}
+		}
+		break;
+	case MT_BUGGLE:
+		mobj->eflags |= MFE_UNDERWATER; //P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn
+		{
+			if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0
+				&& P_AproxDistance(P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y), mobj->tracer->z - mobj->z) <= mobj->radius*16)
+			{
+				// Home in on the target.
+				P_HomingAttack(mobj, mobj->tracer);
 
-					if (mobj->eflags & MFE_VERTICALFLIP)
-					{
-						if ((mobj->z + mobj->height + mobj->momz) <= mobj->ceilingz)
-							break;
-					}
-					else
-					{
-						if ((mobj->z + mobj->momz) >= mobj->floorz)
-							break;
-					}
+				if (mobj->z < mobj->floorz)
+					mobj->z = mobj->floorz;
 
-					momz = abs(mobj->momz);
-					if (R_PointToDist2(0, 0, mobj->momx, mobj->momy) < momz)
-						P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), momz);
-					mobj->flags2 |= MF2_AMBUSH;
-					break;
-				}
-			case MT_SALOONDOOR:
+				if (leveltime % mobj->info->painchance == 0)
+					S_StartSound(mobj, mobj->info->activesound);
+
+				if ((statenum_t)(mobj->state - states) != mobj->info->seestate)
+					P_SetMobjState(mobj, mobj->info->seestate);
+			}
+			else
+			{
+				// Try to find a player
+				P_LookForPlayers(mobj, true, true, mobj->radius*16);
+				mobj->momx >>= 1;
+				mobj->momy >>= 1;
+				mobj->momz >>= 1;
+				if ((statenum_t)(mobj->state - states) != mobj->info->spawnstate)
+					P_SetMobjState(mobj, mobj->info->spawnstate);
+			}
+		}
+		break;
+	case MT_BUMBLEBORE:
+		P_BumbleboreThink(mobj);
+		break;
+	case MT_BIGMINE:
+		mobj->extravalue1 += 3;
+		mobj->extravalue1 %= 360;
+		P_UnsetThingPosition(mobj);
+		mobj->z += FINESINE(mobj->extravalue1*(FINEMASK + 1)/360);
+		P_SetThingPosition(mobj);
+		break;
+	case MT_FLAME:
+		if (mobj->flags2 & MF2_BOSSNOTRAP)
+		{
+			if (!mobj->target || P_MobjWasRemoved(mobj->target))
+			{
+				if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
+					P_RemoveMobj(mobj->tracer);
+				P_RemoveMobj(mobj);
+				return false;
+			}
+			mobj->z = mobj->target->z + mobj->target->momz;
+			if (!(mobj->eflags & MFE_VERTICALFLIP))
+				mobj->z += mobj->target->height;
+		}
+		if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer))
+		{
+			mobj->tracer->z = mobj->z + P_MobjFlip(mobj)*20*mobj->scale;
+			if (mobj->eflags & MFE_VERTICALFLIP)
+				mobj->tracer->z += mobj->height;
+		}
+		break;
+	case MT_WAVINGFLAG1:
+	case MT_WAVINGFLAG2:
+	{
+		fixed_t base = (leveltime << (FRACBITS + 1));
+		mobj_t *seg = mobj->tracer, *prev = mobj;
+		mobj->movedir = mobj->angle
+			+ ((((FINESINE((FixedAngle(base << 1) >> ANGLETOFINESHIFT) & FINEMASK)
+				+ FINESINE((FixedAngle(base << 4) >> ANGLETOFINESHIFT) & FINEMASK)) >> 1)
+				+ FINESINE((FixedAngle(base*9) >> ANGLETOFINESHIFT) & FINEMASK)
+				+ FINECOSINE(((FixedAngle(base*9)) >> ANGLETOFINESHIFT) & FINEMASK)) << 12); //*2^12
+		while (seg)
+		{
+			seg->movedir = seg->angle;
+			seg->angle = prev->movedir;
+			P_UnsetThingPosition(seg);
+			seg->x = prev->x + P_ReturnThrustX(prev, prev->angle, prev->radius);
+			seg->y = prev->y + P_ReturnThrustY(prev, prev->angle, prev->radius);
+			seg->z = prev->z + prev->height - (seg->scale >> 1);
+			P_SetThingPosition(seg);
+			prev = seg;
+			seg = seg->tracer;
+		}
+	}
+	break;
+	case MT_SPINCUSHION:
+		if (mobj->target && mobj->state - states >= S_SPINCUSHION_AIM1 && mobj->state - states <= S_SPINCUSHION_AIM5)
+			mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+		break;
+	case MT_CRUSHCLAW:
+		if (mobj->state - states == S_CRUSHCLAW_STAY && mobj->target)
+		{
+			mobj_t *chain = mobj->target->target;
+			SINT8 sign = ((mobj->tics & 1) ? mobj->tics : -(SINT8)(mobj->tics));
+			while (chain)
+			{
+				chain->z = chain->movefactor + sign*mobj->scale;
+				sign = -sign;
+				chain = chain->target;
+			}
+		}
+		break;
+	case MT_SMASHINGSPIKEBALL:
+		mobj->momx = mobj->momy = 0;
+		if (mobj->state - states == S_SMASHSPIKE_FALL && P_IsObjectOnGround(mobj))
+		{
+			P_SetMobjState(mobj, S_SMASHSPIKE_STOMP1);
+			S_StartSound(mobj, sfx_spsmsh);
+		}
+		else if (mobj->state - states == S_SMASHSPIKE_RISE2 && P_MobjFlip(mobj)*(mobj->z - mobj->movecount) >= 0)
+		{
+			mobj->momz = 0;
+			P_SetMobjState(mobj, S_SMASHSPIKE_FLOAT);
+		}
+		break;
+	case MT_HANGSTER:
+		if (!P_HangsterThink(mobj))
+			return false;
+		break;
+	case MT_LHRT:
+		mobj->momx = FixedMul(mobj->momx, mobj->extravalue2);
+		mobj->momy = FixedMul(mobj->momy, mobj->extravalue2);
+		break;
+	case MT_EGGCAPSULE:
+		if (!mobj->reactiontime)
+		{
+			// Target nearest player on your mare.
+			// (You can make it float up/down by adding MF_FLOAT,
+			//  but beware level design pitfalls.)
+			fixed_t shortest = 1024*FRACUNIT;
+			INT32 i;
+			P_SetTarget(&mobj->target, NULL);
+			for (i = 0; i < MAXPLAYERS; i++)
+				if (playeringame[i] && players[i].mo
+					&& players[i].mare == mobj->threshold && players[i].spheres > 0)
 				{
-					fixed_t x = mobj->tracer->x;
-					fixed_t y = mobj->tracer->y;
-					fixed_t z = mobj->tracer->z;
-					angle_t oang = FixedAngle(mobj->extravalue1);
-					angle_t fa = (oang >> ANGLETOFINESHIFT) & FINEMASK;
-					fixed_t c0 = -96*FINECOSINE(fa);
-					fixed_t s0 = -96*FINESINE(fa);
-					angle_t fma;
-					fixed_t c, s;
-					angle_t angdiff;
-
-					// Adjust angular speed
-					fixed_t da = AngleFixed(mobj->angle - oang);
-					if (da > 180*FRACUNIT)
-						da -= 360*FRACUNIT;
-					mobj->extravalue2 = 8*(mobj->extravalue2 - da/32)/9;
-
-					// Update angle
-					mobj->angle += FixedAngle(mobj->extravalue2);
-
-					angdiff = mobj->angle - FixedAngle(mobj->extravalue1);
-					if (angdiff > (ANGLE_90 - ANG2) && angdiff < ANGLE_180)
+					fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y);
+					if (dist < shortest)
 					{
-						mobj->angle = FixedAngle(mobj->extravalue1) + (ANGLE_90 - ANG2);
-						mobj->extravalue2 /= 2;
+						P_SetTarget(&mobj->target, players[i].mo);
+						shortest = dist;
 					}
-					else if (angdiff < (ANGLE_270 + ANG2) && angdiff >= ANGLE_180)
-					{
-						mobj->angle = FixedAngle(mobj->extravalue1) + (ANGLE_270 + ANG2);
-						mobj->extravalue2 /= 2;
-					}
-
-					// Update position
-					fma = (mobj->angle >> ANGLETOFINESHIFT) & FINEMASK;
-					c = 48*FINECOSINE(fma);
-					s = 48*FINESINE(fma);
-					P_TeleportMove(mobj, x + c0 + c, y + s0 + s, z);
-					break;
 				}
-			case MT_MINECARTSPAWNER:
-				P_HandleMinecartSegments(mobj);
-				if (!mobj->fuse || mobj->fuse > TICRATE)
-					break;
-				if (mobj->fuse == 2)
-				{
-					mobj->fuse = 0;
-					break;
-				}
-				mobj->flags2 ^= MF2_DONTDRAW;
-				break;
-			case MT_LAVAFALLROCK:
-				if (P_IsObjectOnGround(mobj))
-					P_RemoveMobj(mobj);
-				break;
-			case MT_PYREFLY:
-				{
-					fixed_t hdist;
-
-					mobj->extravalue1 = (mobj->extravalue1 + 3) % 360;
-					mobj->z += FINESINE(((mobj->extravalue1*ANG1) >> ANGLETOFINESHIFT) & FINEMASK);
-
-					if (!(mobj->flags2 & MF2_BOSSNOTRAP))
-						P_LookForPlayers(mobj, true, false, 1500*FRACUNIT);
-
-					if (!mobj->target)
-						break;
-
-					if (mobj->extravalue2 == 1)
-						P_PyreFlyBurn(mobj, 0, 20, MT_SMOKE, 4*FRACUNIT);
-					else if (mobj->extravalue2 == 2)
-					{
-						INT32 fireradius = min(100 - mobj->fuse, 52);
-						P_PyreFlyBurn(mobj, P_RandomRange(0, fireradius)*FRACUNIT, 20, MT_FLAMEPARTICLE, 4*FRACUNIT);
-						P_PyreFlyBurn(mobj, fireradius*FRACUNIT, 40, MT_PYREFLY_FIRE, 0);
-					}
+		}
+		break;
+	case MT_EGGMOBILE2_POGO:
+		if (!mobj->target
+			|| !mobj->target->health
+			|| mobj->target->state == &states[mobj->target->info->spawnstate]
+			|| mobj->target->state == &states[mobj->target->info->raisestate])
+		{
+			P_RemoveMobj(mobj);
+			return false;
+		}
+		P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z - mobj->height);
+		break;
+	case MT_HAMMER:
+		if (mobj->z <= mobj->floorz)
+		{
+			P_RemoveMobj(mobj);
+			return false;
+		}
+		break;
+	case MT_KOOPA:
+		P_KoopaThinker(mobj);
+		break;
+	case MT_FIREBALL:
+		if (P_AproxDistance(mobj->momx, mobj->momy) <= 16*FRACUNIT) // Once fireballs lose enough speed, kill them
+		{
+			P_KillMobj(mobj, NULL, NULL, 0);
+			return false;
+		}
+		break;
+	case MT_REDRING:
+		if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz))
+			&& mobj->flags & MF_MISSILE)
+		{
+			P_ExplodeMissile(mobj);
+			return false;
+		}
+		break;
+	case MT_BOSSFLYPOINT:
+		return false;
+	case MT_NIGHTSCORE:
+		mobj->color = (UINT8)(leveltime % SKINCOLOR_WHITE);
+		break;
+	case MT_JETFUME1:
+		if (!P_JetFume1Think(mobj))
+			return false;
+		break;
+	case MT_JETFLAME:
+	{
+		if (!mobj->target // if you have no target
+			|| (!(mobj->target->flags & MF_BOSS) && mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
+		{ // then remove yourself as well!
+			P_RemoveMobj(mobj);
+			return false;
+		}
 
-					hdist = R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+		P_UnsetThingPosition(mobj);
+		mobj->x = mobj->target->x;
+		mobj->y = mobj->target->y;
+		mobj->z = mobj->target->z - 50*mobj->target->scale;
+		mobj->floorz = mobj->z;
+		mobj->ceilingz = mobj->z + mobj->height;
+		P_SetThingPosition(mobj);
+	}
+	break;
+	case MT_EGGROBO1:
+		if (!P_EggRobo1Think(mobj))
+			return false;
+		break;
+	case MT_EGGROBO1JET:
+	{
+		if (!mobj->target || P_MobjWasRemoved(mobj->target) // if you have no target
+			|| (mobj->target->health <= 0)) // or your target isn't a boss and it's popped now
+		{ // then remove yourself as well!
+			P_RemoveMobj(mobj);
+			return false;
+		}
 
-					if (hdist > 1500*FRACUNIT)
-					{
-						mobj->flags2 &= ~MF2_BOSSNOTRAP;
-						P_SetTarget(&mobj->target, NULL);
-						break;
-					}
+		mobj->flags2 ^= MF2_DONTDRAW;
 
-					if (!(mobj->flags2 & MF2_BOSSNOTRAP) && hdist <= 450*FRACUNIT)
-						mobj->flags2 |= MF2_BOSSNOTRAP;
+		P_UnsetThingPosition(mobj);
+		mobj->x = mobj->target->x + P_ReturnThrustX(mobj, mobj->target->angle + ANGLE_90, mobj->movefactor*mobj->target->scale) - P_ReturnThrustX(mobj, mobj->target->angle, 19*mobj->target->scale);
+		mobj->y = mobj->target->y + P_ReturnThrustY(mobj, mobj->target->angle + ANGLE_90, mobj->movefactor*mobj->target->scale) - P_ReturnThrustY(mobj, mobj->target->angle, 19*mobj->target->scale);
+		mobj->z = mobj->target->z;
+		if (mobj->target->eflags & MFE_VERTICALFLIP)
+			mobj->z += (mobj->target->height - mobj->height);
+		mobj->floorz = mobj->z;
+		mobj->ceilingz = mobj->z + mobj->height;
+		P_SetThingPosition(mobj);
+	}
+	break;
+	case MT_NIGHTSDRONE:
+		P_NiGHTSDroneThink(mobj);
+		break;
+	case MT_PLAYER:
+		if (mobj->player)
+			P_PlayerMobjThinker(mobj);
+		return false;
+	case MT_SKIM:
+		// check mobj against possible water content, before movement code
+		P_MobjCheckWater(mobj);
 
-					if (!(mobj->flags2 & MF2_BOSSNOTRAP))
-						break;
+		// Keep Skim at water surface
+		if (mobj->z <= mobj->watertop)
+		{
+			mobj->flags |= MF_NOGRAVITY;
+			if (mobj->z < mobj->watertop)
+			{
+				if (mobj->watertop - mobj->z <= FixedMul(mobj->info->speed*FRACUNIT, mobj->scale))
+					mobj->z = mobj->watertop;
+				else
+					mobj->momz = FixedMul(mobj->info->speed*FRACUNIT, mobj->scale);
+			}
+		}
+		else
+		{
+			mobj->flags &= ~MF_NOGRAVITY;
+			if (mobj->z > mobj->watertop && mobj->z - mobj->watertop < FixedMul(MAXSTEPMOVE, mobj->scale))
+				mobj->z = mobj->watertop;
+		}
+		break;
+	case MT_RING:
+	case MT_REDTEAMRING:
+	case MT_BLUETEAMRING:
+		P_KillRingsInLava(mobj);
+		if (P_MobjWasRemoved(mobj))
+			return false;
+		/* FALLTHRU */
+	case MT_COIN:
+	case MT_BLUESPHERE:
+	case MT_BOMBSPHERE:
+	case MT_NIGHTSCHIP:
+	case MT_NIGHTSSTAR:
+		// No need to check water. Who cares?
+		P_RingThinker(mobj);
+		if (mobj->flags2 & MF2_NIGHTSPULL)
+			P_NightsItemChase(mobj);
+		else
+			A_AttractChase(mobj);
+		return false;
+		// Flung items
+	case MT_FLINGRING:
+		P_KillRingsInLava(mobj);
+		if (P_MobjWasRemoved(mobj))
+			return false;
+		/* FALLTHRU */
+	case MT_FLINGCOIN:
+	case MT_FLINGBLUESPHERE:
+	case MT_FLINGNIGHTSCHIP:
+		if (mobj->flags2 & MF2_NIGHTSPULL)
+			P_NightsItemChase(mobj);
+		else
+			A_AttractChase(mobj);
+		break;
+	case MT_EMBLEM:
+		if (mobj->flags2 & MF2_NIGHTSPULL)
+			P_NightsItemChase(mobj);
+		break;
+	case MT_SHELL:
+		if (mobj->threshold && mobj->threshold != TICRATE)
+			mobj->threshold--;
 
-					if (hdist < 1000*FRACUNIT)
-					{
-						//Aim for player z position. If too close to floor/ceiling, aim just above/below them.
-						fixed_t destz = min(max(mobj->target->z, mobj->target->floorz + 70*FRACUNIT), mobj->target->ceilingz - 80*FRACUNIT - mobj->height);
-						fixed_t dist = P_AproxDistance(hdist, destz - mobj->z);
-						P_InstaThrust(mobj, R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y), 2*FRACUNIT);
-						mobj->momz = FixedMul(FixedDiv(destz - mobj->z, dist), 2*FRACUNIT);
-					}
-					else
-					{
-						mobj->momx = 0;
-						mobj->momy = 0;
-						mobj->momz = 0;
-					}
-					break;
-				}
-			case MT_PTERABYTE:
-				{
-					if (mobj->extravalue1 & 4) // Cooldown after grabbing
-					{
-						if (mobj->movefactor)
-							mobj->movefactor--;
-						else
-						{
-							P_SetTarget(&mobj->target, NULL);
-							mobj->extravalue1 &= 3;
-						}
-					}
+		if (mobj->threshold >= TICRATE)
+		{
+			mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h);
+			P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale));
+		}
+		break;
+	case MT_TURRET:
+		if (!P_TurretThink(mobj))
+			return false;
+		break;
+	case MT_BLUEFLAG:
+	case MT_REDFLAG:
+	{
+		sector_t* sec2;
+		sec2 = P_ThingOnSpecial3DFloor(mobj);
+		if ((sec2 && GETSECSPECIAL(sec2->special, 4) == 2) || (GETSECSPECIAL(mobj->subsector->sector->special, 4) == 2))
+			mobj->fuse = 1; // Return to base.
+		break;
+	}
+	case MT_CANNONBALL:
+#ifdef FLOORSPLATS
+		R_AddFloorSplat(mobj->tracer->subsector, mobj->tracer, "TARGET", mobj->tracer->x,
+			mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE);
+#endif
+		break;
+	case MT_SPINDUST: // Spindash dust
+		mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
+		mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
+		//mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank
+		if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust!
+		{
+			P_MobjCheckWater(mobj);
+			if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT
+				&& mobj->z + mobj->height >= mobj->watertop - 5*FRACUNIT)
+				mobj->flags2 |= MF2_DONTDRAW;
+		}
+		break;
+	case MT_TRAINDUSTSPAWNER:
+		if (leveltime % 5 == 0) {
+			mobj_t* traindust = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PARTICLE);
+			traindust->flags = MF_SCENERY;
+			P_SetMobjState(traindust, S_TRAINDUST);
+			traindust->frame = P_RandomRange(0, 8)|FF_TRANS90;
+			traindust->angle = mobj->angle;
+			traindust->tics = TICRATE*4;
+			traindust->destscale = FRACUNIT*64;
+			traindust->scalespeed = FRACUNIT/24;
+			P_SetScale(traindust, FRACUNIT*6);
+		}
+		break;
+	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);
+			P_SetMobjState(steam, S_TRAINSTEAM);
+			steam->frame = P_RandomRange(0, 1)|FF_TRANS90;
+			steam->tics = TICRATE*8;
+			steam->destscale = FRACUNIT*64;
+			steam->scalespeed = FRACUNIT/8;
+			P_SetScale(steam, FRACUNIT*16);
+			steam->momx = P_SignedRandom()*32;
+			steam->momy = -64*FRACUNIT;
+			steam->momz = 2*FRACUNIT;
+		}
+		break;
+	case MT_CANARIVORE_GAS:
+	{
+		fixed_t momz;
 
-					if ((mobj->extravalue1 & 3) == 0) // Hovering
-					{
-						fixed_t vdist, hdist, time;
-						fixed_t hspeed = 3*mobj->info->speed;
-						angle_t fa;
+		if (mobj->flags2 & MF2_AMBUSH)
+		{
+			mobj->momx = FixedMul(mobj->momx, 50*FRACUNIT/51);
+			mobj->momy = FixedMul(mobj->momy, 50*FRACUNIT/51);
+			break;
+		}
 
-						var1 = 1;
-						var2 = 0;
-						A_CapeChase(mobj);
+		if (mobj->eflags & MFE_VERTICALFLIP)
+		{
+			if ((mobj->z + mobj->height + mobj->momz) <= mobj->ceilingz)
+				break;
+		}
+		else
+		{
+			if ((mobj->z + mobj->momz) >= mobj->floorz)
+				break;
+		}
 
-						if (mobj->target)
-							break; // Still carrying a player or in cooldown
+		momz = abs(mobj->momz);
+		if (R_PointToDist2(0, 0, mobj->momx, mobj->momy) < momz)
+			P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), momz);
+		mobj->flags2 |= MF2_AMBUSH;
+		break;
+	}
+	case MT_SALOONDOOR:
+		P_SaloonDoorThink(mobj);
+		break;
+	case MT_MINECARTSPAWNER:
+		P_HandleMinecartSegments(mobj);
+		if (!mobj->fuse || mobj->fuse > TICRATE)
+			break;
+		if (mobj->fuse == 2)
+		{
+			mobj->fuse = 0;
+			break;
+		}
+		mobj->flags2 ^= MF2_DONTDRAW;
+		break;
+	case MT_LAVAFALLROCK:
+		if (P_IsObjectOnGround(mobj))
+			P_RemoveMobj(mobj);
+		break;
+	case MT_PYREFLY:
+		P_PyreFlyThink(mobj);
+		break;
+	case MT_PTERABYTE:
+		P_PterabyteThink(mobj);
+		break;
+	case MT_DRAGONBOMBER:
+		P_DragonbomberThink(mobj);
+		break;
+	case MT_MINUS:
+		if (P_IsObjectOnGround(mobj))
+			mobj->rollangle = 0;
+		else
+			mobj->rollangle = R_PointToAngle2(0, 0, mobj->momz, (mobj->scale << 1) - min(abs(mobj->momz), mobj->scale << 1));
+		break;
+	case MT_SPINFIRE:
+		if (mobj->flags & MF_NOGRAVITY)
+		{
+			if (mobj->eflags & MFE_VERTICALFLIP)
+				mobj->z = mobj->ceilingz - mobj->height;
+			else
+				mobj->z = mobj->floorz;
+		}
+		else if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z <= mobj->floorz)
+			|| ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z + mobj->height >= mobj->ceilingz))
+		{
+			mobj->flags |= MF_NOGRAVITY;
+			mobj->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others
+			mobj->momy = mobj->momz = 0;
+			mobj->z = ((mobj->eflags & MFE_VERTICALFLIP) ? mobj->ceilingz - mobj->height : mobj->floorz);
+		}
+		/* FALLTHRU */
+	default:
+		// check mobj against possible water content, before movement code
+		P_MobjCheckWater(mobj);
 
-						P_LookForPlayers(mobj, true, false, 256*FRACUNIT);
+		// Extinguish fire objects in water
+		if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
+			&& (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
+		{
+			P_KillMobj(mobj, NULL, NULL, 0);
+			return false;
+		}
+		break;
+	}
+	return true;
+}
 
-						if (!mobj->target)
-							break;
+static void P_FiringThink(mobj_t *mobj)
+{
+	if (!mobj->target)
+		return;
 
-						if (mobj->target->player->powers[pw_flashing])
-						{
-							P_SetTarget(&mobj->target, NULL);
-							break;
-						}
+	if (mobj->health <= 0)
+		return;
 
-						vdist = mobj->z - mobj->target->z - mobj->target->height;
-						if (P_MobjFlip(mobj)*vdist <= 0)
-						{
-							P_SetTarget(&mobj->target, NULL);
-							break;
-						}
+	if (mobj->state->action.acp1 == (actionf_p1)A_Boss1Laser)
+	{
+		if (mobj->state->tics > 1)
+		{
+			var1 = mobj->state->var1;
+			var2 = mobj->state->var2 & 65535;
+			mobj->state->action.acp1(mobj);
+		}
+	}
+	else if (leveltime & 1) // Fire mode
+	{
+		mobj_t *missile;
 
-						hdist = R_PointToDist2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
-						if (hdist > 450*FRACUNIT)
-						{
-							P_SetTarget(&mobj->target, NULL);
-							break;
-						}
+		if (mobj->target->player && mobj->target->player->powers[pw_carry] == CR_NIGHTSMODE)
+		{
+			fixed_t oldval = mobjinfo[mobj->extravalue1].speed;
 
-						P_SetMobjState(mobj, S_PTERABYTE_SWOOPDOWN);
-						mobj->extravalue1++;
-						S_StartSound(mobj, mobj->info->attacksound);
-						time = FixedDiv(hdist, hspeed);
-						mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
-						fa = (mobj->angle >> ANGLETOFINESHIFT) & FINEMASK;
-						mobj->momx = FixedMul(FINECOSINE(fa), hspeed);
-						mobj->momy = FixedMul(FINESINE(fa), hspeed);
-						mobj->momz = -2*FixedDiv(vdist, time);
-						mobj->extravalue2 = -FixedDiv(mobj->momz, time); //Z accel
-						mobj->movecount = time >> FRACBITS;
-						mobj->reactiontime = mobj->movecount;
-					}
-					else if ((mobj->extravalue1 & 3) == 1) // Swooping
-					{
-						mobj->reactiontime--;
-						mobj->momz += mobj->extravalue2;
-						if (mobj->reactiontime)
-							break;
+			mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx, mobj->target->y + mobj->target->momy);
+			mobjinfo[mobj->extravalue1].speed = FixedMul(60*FRACUNIT, mobj->scale);
+			missile = P_SpawnMissile(mobj, mobj->target, mobj->extravalue1);
+			mobjinfo[mobj->extravalue1].speed = oldval;
+		}
+		else
+		{
+			mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+			missile = P_SpawnMissile(mobj, mobj->target, mobj->extravalue1);
+		}
 
-						if (mobj->state - states == S_PTERABYTE_SWOOPDOWN)
-						{
-							P_SetMobjState(mobj, S_PTERABYTE_SWOOPUP);
-							mobj->reactiontime = mobj->movecount;
-						}
-						else if (mobj->state - states == S_PTERABYTE_SWOOPUP)
-						{
-							P_SetMobjState(mobj, S_PTERABYTE_FLY1);
-							mobj->extravalue1++;
-							if (mobj->target && mobj->target->tracer != mobj)
-								P_SetTarget(&mobj->target, NULL); // Failed to grab the target
-							mobj->momx = mobj->momy = mobj->momz = 0;
-						}
-					}
-					else // Returning
-					{
-						var1 = 2*mobj->info->speed;
-						var2 = 1;
-						A_HomingChase(mobj);
-						if (P_AproxDistance(mobj->x - mobj->tracer->x, mobj->y - mobj->tracer->y) <= mobj->info->speed)
-						{
-							mobj->extravalue1 -= 2;
-							mobj->momx = mobj->momy = mobj->momz = 0;
-						}
-					}
-					break;
-				}
-			case MT_DRAGONBOMBER:
-				{
-#define DRAGONTURNSPEED ANG2
-					mobj->movecount = (mobj->movecount + 9) % 360;
-					P_SetObjectMomZ(mobj, 4*FINESINE(((mobj->movecount*ANG1) >> ANGLETOFINESHIFT) & FINEMASK), false);
-					if (mobj->threshold > 0) // are we dropping mines?
-					{
-						mobj->threshold--;
-						if (mobj->threshold == 0) // if the timer hits 0, look for a mine to drop!
-						{
-							mobj_t *segment = mobj;
-							while (segment->tracer != NULL && !P_MobjWasRemoved(segment->tracer) && segment->tracer->state == &states[segment->tracer->info->spawnstate])
-							{
-								segment = segment->tracer;
-							}
-							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);
-								P_SetMobjState(segment, segment->info->raisestate);
-								mobj->threshold = mobj->info->painchance;
-							}
-						}
-					}
-					if (mobj->target != NULL) // Are we chasing a player?
-					{
-						fixed_t dist = P_AproxDistance(mobj->x - mobj->target->x, mobj->y - mobj->target->y);
-						if (dist > 2000 * mobj->scale) // Not anymore!
-							P_SetTarget(&mobj->target, NULL);
-						else
-						{
-							fixed_t vspeed = FixedMul(mobj->info->speed >> 3, mobj->scale);
-							fixed_t z = mobj->target->z + (mobj->height >> 1) + (mobj->flags & MFE_VERTICALFLIP ? -128*mobj->scale : 128*mobj->scale + mobj->target->height);
-							angle_t diff = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) - mobj->angle;
-							if (diff > ANGLE_180)
-								mobj->angle -= DRAGONTURNSPEED;
-							else
-								mobj->angle += DRAGONTURNSPEED;
-							if (!mobj->threshold && dist < 512 * mobj->scale) // Close enough to drop bombs
-							{
-								mobj->threshold = mobj->info->painchance;
-							}
-							mobj->momz += max(min(z - mobj->z, vspeed), -vspeed);
-						}
-					}
-					else // Can we find a player to chase?
-					{
-						if (mobj->tracer == NULL || mobj->tracer->state != &states[mobj->tracer->info->spawnstate]
-							|| !P_LookForPlayers(mobj, true, false, 2000*mobj->scale)) // if not, circle around the spawnpoint
-						{
-							if (!mobj->spawnpoint) // unless we don't have one, in which case uhhh just circle around wherever we currently are I guess??
-								mobj->angle += DRAGONTURNSPEED;
-							else
-							{
-								fixed_t vspeed = FixedMul(mobj->info->speed >> 3, mobj->scale);
-								fixed_t x = mobj->spawnpoint->x << FRACBITS;
-								fixed_t y = mobj->spawnpoint->y << FRACBITS;
-								fixed_t z = mobj->spawnpoint->z << FRACBITS;
-								angle_t diff = R_PointToAngle2(mobj->x, mobj->y, x, y) - mobj->angle;
-								if (diff > ANGLE_180)
-									mobj->angle -= DRAGONTURNSPEED;
-								else
-									mobj->angle += DRAGONTURNSPEED;
-								mobj->momz += max(min(z - mobj->z, vspeed), -vspeed);
-							}
-						}
-					}
-					P_InstaThrust(mobj, mobj->angle, FixedMul(mobj->info->speed, mobj->scale));
-#undef DRAGONTURNSPEED
-				}
-				break;
-			case MT_SPINFIRE:
-				if (mobj->flags & MF_NOGRAVITY)
-				{
-					if (mobj->eflags & MFE_VERTICALFLIP)
-						mobj->z = mobj->ceilingz - mobj->height;
-					else
-						mobj->z = mobj->floorz;
-				}
-				else if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z <= mobj->floorz)
-				|| ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z+mobj->height >= mobj->ceilingz))
-				{
-					mobj->flags |= MF_NOGRAVITY;
-					mobj->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others
-					mobj->momy = mobj->momz = 0;
-					mobj->z = ((mobj->eflags & MFE_VERTICALFLIP) ? mobj->ceilingz-mobj->height : mobj->floorz);
-				}
-				/* FALLTHRU */
-			default:
-				// check mobj against possible water content, before movement code
-				P_MobjCheckWater(mobj);
+		if (missile)
+		{
+			if (mobj->flags2 & MF2_SUPERFIRE)
+				missile->flags2 |= MF2_SUPERFIRE;
 
-				// Extinguish fire objects in water
-				if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
-					&& (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
-				{
-					P_KillMobj(mobj, NULL, NULL, 0);
-					return;
-				}
-				break;
+			if (mobj->info->attacksound)
+				S_StartSound(missile, mobj->info->attacksound);
 		}
 	}
-	if (P_MobjWasRemoved(mobj))
-		return;
+	else
+		mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+}
 
-	if (mobj->flags2 & MF2_FIRING && mobj->target && mobj->health > 0)
+static void P_MonitorFuseThink(mobj_t *mobj)
+{
+	mobj_t *newmobj;
+
+	// Special case for ALL monitors.
+	// If a box's speed is nonzero, it's allowed to respawn as a WRM/SRM.
+	if (mobj->info->speed != 0 && (mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX)))
 	{
-		if (mobj->state->action.acp1 == (actionf_p1)A_Boss1Laser)
-		{
-			if (mobj->state->tics > 1)
-			{
-				var1 = mobj->state->var1;
-				var2 = mobj->state->var2 & 65535;
-				mobj->state->action.acp1(mobj);
-			}
-		}
-		else if (leveltime & 1) // Fire mode
-		{
-			mobj_t *missile;
+		mobjtype_t spawnchance[64];
+		INT32 numchoices = 0, i = 0;
 
-			if (mobj->target->player && mobj->target->player->powers[pw_carry] == CR_NIGHTSMODE)
-			{
-				fixed_t oldval = mobjinfo[mobj->extravalue1].speed;
+		// This define should make it a lot easier to organize and change monitor weights
+#define SETMONITORCHANCES(type, strongboxamt, weakboxamt) \
+for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) spawnchance[numchoices++] = type
 
-				mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x+mobj->target->momx, mobj->target->y+mobj->target->momy);
-				mobjinfo[mobj->extravalue1].speed = FixedMul(60*FRACUNIT, mobj->scale);
-				missile = P_SpawnMissile(mobj, mobj->target, mobj->extravalue1);
-				mobjinfo[mobj->extravalue1].speed = oldval;
-			}
-			else
-			{
-				mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
-				missile = P_SpawnMissile(mobj, mobj->target, mobj->extravalue1);
-			}
+					//                Type             SRM WRM
+		SETMONITORCHANCES(MT_SNEAKERS_BOX, 0, 10); // Super Sneakers
+		SETMONITORCHANCES(MT_INVULN_BOX, 2, 0); // Invincibility
+		SETMONITORCHANCES(MT_WHIRLWIND_BOX, 3, 8); // Whirlwind Shield
+		SETMONITORCHANCES(MT_ELEMENTAL_BOX, 3, 8); // Elemental Shield
+		SETMONITORCHANCES(MT_ATTRACT_BOX, 2, 0); // Attraction Shield
+		SETMONITORCHANCES(MT_FORCE_BOX, 3, 3); // Force Shield
+		SETMONITORCHANCES(MT_ARMAGEDDON_BOX, 2, 0); // Armageddon Shield
+		SETMONITORCHANCES(MT_MIXUP_BOX, 0, 1); // Teleporters
+		SETMONITORCHANCES(MT_RECYCLER_BOX, 0, 1); // Recycler
+		SETMONITORCHANCES(MT_1UP_BOX, 1, 1); // 1-Up
+		// =======================================
+		//                Total             16  32
 
-			if (missile)
-			{
-				if (mobj->flags2 & MF2_SUPERFIRE)
-					missile->flags2 |= MF2_SUPERFIRE;
+#undef SETMONITORCHANCES
 
-				if (mobj->info->attacksound)
-					S_StartSound(missile, mobj->info->attacksound);
-			}
-		}
-		else
-			mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y);
+		i = P_RandomKey(numchoices); // Gotta love those random numbers!
+		newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, spawnchance[i]);
 	}
+	else
+		newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
 
-	if (mobj->flags & MF_AMBIENT)
-	{
-		if (!(leveltime % mobj->health) && mobj->info->seesound)
-			S_StartSound(mobj, mobj->info->seesound);
+	// Transfer flags2 (ambush, strongbox, objectflip)
+	newmobj->flags2 = mobj->flags2;
+	P_RemoveMobj(mobj); // make sure they disappear
+}
+
+static void P_FlagFuseThink(mobj_t *mobj)
+{
+	subsector_t *ss;
+	fixed_t x, y, z;
+	mobj_t* flagmo;
+
+	if (!mobj->spawnpoint)
 		return;
+
+	x = mobj->spawnpoint->x << FRACBITS;
+	y = mobj->spawnpoint->y << FRACBITS;
+	z = mobj->spawnpoint->z << FRACBITS;
+	ss = R_PointInSubsector(x, y);
+	if (mobj->spawnpoint->options & MTF_OBJECTFLIP)
+		z = ss->sector->ceilingheight - mobjinfo[mobj->type].height - z;
+	else
+		z = ss->sector->floorheight + z;
+	flagmo = P_SpawnMobj(x, y, z, mobj->type);
+	flagmo->spawnpoint = mobj->spawnpoint;
+	if (mobj->spawnpoint->options & MTF_OBJECTFLIP)
+	{
+		flagmo->eflags |= MFE_VERTICALFLIP;
+		flagmo->flags2 |= MF2_OBJECTFLIP;
 	}
 
-	// Check fuse
-	if (mobj->fuse)
+	if (mobj->type == MT_REDFLAG)
 	{
+		if (!(mobj->flags2 & MF2_JUSTATTACKED))
+			CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x85, M_GetText("Red flag"), 0x80);
 
-		if (mobj->type == MT_SNAPPER_HEAD || mobj->type == MT_SNAPPER_LEG || mobj->type == MT_MINECARTSEG)
-			mobj->flags2 ^= MF2_DONTDRAW;
+		// Assumedly in splitscreen players will be on opposing teams
+		if (players[consoleplayer].ctfteam == 1 || splitscreen)
+			S_StartSound(NULL, sfx_hoop1);
+		else if (players[consoleplayer].ctfteam == 2)
+			S_StartSound(NULL, sfx_hoop3);
 
-		mobj->fuse--;
-		if (!mobj->fuse)
-		{
-			subsector_t *ss;
-			fixed_t x, y, z;
-			mobj_t *flagmo, *newmobj;
+		redflag = flagmo;
+	}
+	else // MT_BLUEFLAG
+	{
+		if (!(mobj->flags2 & MF2_JUSTATTACKED))
+			CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x84, M_GetText("Blue flag"), 0x80);
+
+		// Assumedly in splitscreen players will be on opposing teams
+		if (players[consoleplayer].ctfteam == 2 || splitscreen)
+			S_StartSound(NULL, sfx_hoop1);
+		else if (players[consoleplayer].ctfteam == 1)
+			S_StartSound(NULL, sfx_hoop3);
+
+		blueflag = flagmo;
+	}
+}
+
+static boolean P_FuseThink(mobj_t *mobj)
+{
+	if (mobj->type == MT_SNAPPER_HEAD || mobj->type == MT_SNAPPER_LEG || mobj->type == MT_MINECARTSEG)
+		mobj->flags2 ^= MF2_DONTDRAW;
+
+	mobj->fuse--;
+
+	if (mobj->fuse)
+		return true;
 
 #ifdef HAVE_BLUA
-			if (LUAh_MobjFuse(mobj) || P_MobjWasRemoved(mobj))
-				;
-			else
+	if (LUAh_MobjFuse(mobj) || P_MobjWasRemoved(mobj))
+		;
+	else
 #endif
-			if (mobj->info->flags & MF_MONITOR)
+		if (mobj->info->flags & MF_MONITOR)
+		{
+			P_MonitorFuseThink(mobj);
+			return false;
+		}
+		else switch (mobj->type)
+		{
+			// gargoyle and snowman handled in P_PushableThinker, not here
+		case MT_THROWNGRENADE:
+		case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE:
+			P_SetMobjState(mobj, mobj->info->deathstate);
+			break;
+		case MT_LHRT:
+			P_KillMobj(mobj, NULL, NULL, 0);
+			break;
+		case MT_BLUEFLAG:
+		case MT_REDFLAG:
+			P_FlagFuseThink(mobj);
+			P_RemoveMobj(mobj);
+			return false;
+		case MT_FANG:
+			if (mobj->flags2 & MF2_SLIDEPUSH)
 			{
-				// Special case for ALL monitors.
-				// If a box's speed is nonzero, it's allowed to respawn as a WRM/SRM.
-				if (mobj->info->speed != 0 && (mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX)))
-				{
-					mobjtype_t spawnchance[64];
-					INT32 numchoices = 0, i = 0;
+				var1 = 0;
+				var2 = 0;
+				A_BossDeath(mobj);
+				return false;
+			}
+			P_SetMobjState(mobj, mobj->state->nextstate);
+			if (P_MobjWasRemoved(mobj))
+				return false;
+			break;
+		case MT_METALSONIC_BATTLE:
+			break; // don't remove
+		case MT_SPIKE:
+			P_SetMobjState(mobj, mobj->state->nextstate);
+			mobj->fuse = mobj->info->speed;
+			if (mobj->spawnpoint)
+				mobj->fuse += mobj->spawnpoint->angle;
+			break;
+		case MT_WALLSPIKE:
+			P_SetMobjState(mobj, mobj->state->nextstate);
+			mobj->fuse = mobj->info->speed;
+			if (mobj->spawnpoint)
+				mobj->fuse += (mobj->spawnpoint->angle / 360);
+			break;
+		case MT_NIGHTSCORE:
+			P_RemoveMobj(mobj);
+			return false;
+		case MT_LAVAFALL:
+			if (mobj->state - states == S_LAVAFALL_DORMANT)
+			{
+				mobj->fuse = 30;
+				P_SetMobjState(mobj, S_LAVAFALL_TELL);
+				S_StartSound(mobj, mobj->info->seesound);
+			}
+			else if (mobj->state - states == S_LAVAFALL_TELL)
+			{
+				mobj->fuse = 40;
+				P_SetMobjState(mobj, S_LAVAFALL_SHOOT);
+				S_StopSound(mobj);
+				S_StartSound(mobj, mobj->info->attacksound);
+			}
+			else
+			{
+				mobj->fuse = 30;
+				P_SetMobjState(mobj, S_LAVAFALL_DORMANT);
+				S_StopSound(mobj);
+			}
+			return false;
+		case MT_PYREFLY:
+			if (mobj->health <= 0)
+				break;
 
-// This define should make it a lot easier to organize and change monitor weights
-#define SETMONITORCHANCES(type, strongboxamt, weakboxamt) \
-for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) spawnchance[numchoices++] = type
+			mobj->extravalue2 = (mobj->extravalue2 + 1) % 3;
+			if (mobj->extravalue2 == 0)
+			{
+				P_SetMobjState(mobj, mobj->info->spawnstate);
+				mobj->fuse = 100;
+				S_StopSound(mobj);
+				S_StartSound(mobj, sfx_s3k8c);
+			}
+			else if (mobj->extravalue2 == 1)
+			{
+				mobj->fuse = 50;
+				S_StartSound(mobj, sfx_s3ka3);
+			}
+			else
+			{
+				P_SetMobjState(mobj, mobj->info->meleestate);
+				mobj->fuse = 100;
+				S_StopSound(mobj);
+				S_StartSound(mobj, sfx_s3kc2l);
+			}
+			return false;
+		case MT_PLAYER:
+			break; // don't remove
+		default:
+			P_SetMobjState(mobj, mobj->info->xdeathstate); // will remove the mobj if S_NULL.
+			break;
+			// Looking for monitors? They moved to a special condition above.
+		}
 
-					//                Type             SRM WRM
-					SETMONITORCHANCES(MT_SNEAKERS_BOX,   0, 10); // Super Sneakers
-					SETMONITORCHANCES(MT_INVULN_BOX,     2,  0); // Invincibility
-					SETMONITORCHANCES(MT_WHIRLWIND_BOX,  3,  8); // Whirlwind Shield
-					SETMONITORCHANCES(MT_ELEMENTAL_BOX,  3,  8); // Elemental Shield
-					SETMONITORCHANCES(MT_ATTRACT_BOX,    2,  0); // Attraction Shield
-					SETMONITORCHANCES(MT_FORCE_BOX,      3,  3); // Force Shield
-					SETMONITORCHANCES(MT_ARMAGEDDON_BOX, 2,  0); // Armageddon Shield
-					SETMONITORCHANCES(MT_MIXUP_BOX,      0,  1); // Teleporters
-					SETMONITORCHANCES(MT_RECYCLER_BOX,   0,  1); // Recycler
-					SETMONITORCHANCES(MT_1UP_BOX,        1,  1); // 1-Up
-					// =======================================
-					//                Total             16  32
+	return !P_MobjWasRemoved(mobj);
+}
 
-#undef SETMONITORCHANCES
+//
+// P_MobjThinker
+//
+void P_MobjThinker(mobj_t *mobj)
+{
+	I_Assert(mobj != NULL);
+	I_Assert(!P_MobjWasRemoved(mobj));
 
-					i = P_RandomKey(numchoices); // Gotta love those random numbers!
-					newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, spawnchance[i]);
-				}
-				else
-					newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
+	if (mobj->flags & MF_NOTHINK)
+		return;
 
-				// Transfer flags2 (ambush, strongbox, objectflip)
-				newmobj->flags2 = mobj->flags2;
-				P_RemoveMobj(mobj); // make sure they disappear
-				return;
-			}
-			else switch (mobj->type)
-			{
-				// gargoyle and snowman handled in P_PushableThinker, not here
-				case MT_THROWNGRENADE:
-				case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE:
-					P_SetMobjState(mobj, mobj->info->deathstate);
-					break;
-				case MT_LHRT:
-					P_KillMobj(mobj, NULL, NULL, 0);
-					break;
-				case MT_BLUEFLAG:
-				case MT_REDFLAG:
-					if (mobj->spawnpoint)
-					{
-						x = mobj->spawnpoint->x << FRACBITS;
-						y = mobj->spawnpoint->y << FRACBITS;
-						ss = R_PointInSubsector(x, y);
-						if (mobj->spawnpoint->options & MTF_OBJECTFLIP)
-						{
-							z = ss->sector->ceilingheight - mobjinfo[mobj->type].height;
-							if (mobj->spawnpoint->options >> ZSHIFT)
-								z -= (mobj->spawnpoint->options >> ZSHIFT) << FRACBITS;
-						}
-						else
-						{
-							z = ss->sector->floorheight;
-							if (mobj->spawnpoint->options >> ZSHIFT)
-								z += (mobj->spawnpoint->options >> ZSHIFT) << FRACBITS;
-						}
-						flagmo = P_SpawnMobj(x, y, z, mobj->type);
-						flagmo->spawnpoint = mobj->spawnpoint;
-						if (mobj->spawnpoint->options & MTF_OBJECTFLIP)
-						{
-							flagmo->eflags |= MFE_VERTICALFLIP;
-							flagmo->flags2 |= MF2_OBJECTFLIP;
-						}
+	if ((mobj->flags & MF_BOSS) && mobj->spawnpoint && (bossdisabled & (1<<mobj->spawnpoint->extrainfo)))
+		return;
 
-						if (mobj->type == MT_REDFLAG)
-						{
-							if (!(mobj->flags2 & MF2_JUSTATTACKED))
-								CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x85, M_GetText("Red flag"), 0x80);
+	// Remove dead target/tracer.
+	if (mobj->target && P_MobjWasRemoved(mobj->target))
+		P_SetTarget(&mobj->target, NULL);
+	if (mobj->tracer && P_MobjWasRemoved(mobj->tracer))
+		P_SetTarget(&mobj->tracer, NULL);
+	if (mobj->hnext && P_MobjWasRemoved(mobj->hnext))
+		P_SetTarget(&mobj->hnext, NULL);
+	if (mobj->hprev && P_MobjWasRemoved(mobj->hprev))
+		P_SetTarget(&mobj->hprev, NULL);
 
-							// Assumedly in splitscreen players will be on opposing teams
-							if (players[consoleplayer].ctfteam == 1 || splitscreen)
-								S_StartSound(NULL, sfx_hoop1);
-							else if (players[consoleplayer].ctfteam == 2)
-								S_StartSound(NULL, sfx_hoop3);
+	mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG);
 
-							redflag = flagmo;
-						}
-						else // MT_BLUEFLAG
-						{
-							if (!(mobj->flags2 & MF2_JUSTATTACKED))
-								CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x84, M_GetText("Blue flag"), 0x80);
+	tmfloorthing = tmhitthing = NULL;
 
-							// Assumedly in splitscreen players will be on opposing teams
-							if (players[consoleplayer].ctfteam == 2 || splitscreen)
-								S_StartSound(NULL, sfx_hoop1);
-							else if (players[consoleplayer].ctfteam == 1)
-								S_StartSound(NULL, sfx_hoop3);
+	// Sector special (2,8) allows ANY mobj to trigger a linedef exec
+	if (mobj->subsector && GETSECSPECIAL(mobj->subsector->sector->special, 2) == 8)
+	{
+		sector_t *sec2;
 
-							blueflag = flagmo;
-						}
-					}
-					P_RemoveMobj(mobj);
-					return;
-				case MT_FANG:
-					if (mobj->flags2 & MF2_SLIDEPUSH)
-					{
-						var1 = 0;
-						var2 = 0;
-						A_BossDeath(mobj);
-						return;
-					}
-					P_SetMobjState(mobj, mobj->state->nextstate);
-					if (P_MobjWasRemoved(mobj))
-						return;
-					break;
-				case MT_METALSONIC_BATTLE:
-					break; // don't remove
-				case MT_SPIKE:
-					P_SetMobjState(mobj, mobj->state->nextstate);
-					mobj->fuse = mobj->info->speed;
-					if (mobj->spawnpoint)
-						mobj->fuse += mobj->spawnpoint->angle;
-					break;
-				case MT_WALLSPIKE:
-					P_SetMobjState(mobj, mobj->state->nextstate);
-					mobj->fuse = mobj->info->speed;
-					if (mobj->spawnpoint)
-						mobj->fuse += (mobj->spawnpoint->angle/360);
-					break;
-				case MT_NIGHTSCORE:
-					P_RemoveMobj(mobj);
-					return;
-				case MT_LAVAFALL:
-					if (mobj->state - states == S_LAVAFALL_DORMANT)
-					{
-						mobj->fuse = 30;
-						P_SetMobjState(mobj, S_LAVAFALL_TELL);
-						S_StartSound(mobj, mobj->info->seesound);
-					}
-					else if (mobj->state - states == S_LAVAFALL_TELL)
-					{
-						mobj->fuse = 40;
-						P_SetMobjState(mobj, S_LAVAFALL_SHOOT);
-						S_StopSound(mobj);
-						S_StartSound(mobj, mobj->info->attacksound);
-					}
-					else
-					{
-						mobj->fuse = 30;
-						P_SetMobjState(mobj, S_LAVAFALL_DORMANT);
-						S_StopSound(mobj);
-					}
-					return;
-				case MT_PYREFLY:
-					if (mobj->health <= 0)
-						break;
+		sec2 = P_ThingOnSpecial3DFloor(mobj);
+		if (sec2 && GETSECSPECIAL(sec2->special, 2) == 1)
+			P_LinedefExecute(sec2->tag, mobj, sec2);
+	}
 
-					mobj->extravalue2 = (mobj->extravalue2 + 1) % 3;
-					if (mobj->extravalue2 == 0)
-					{
-						P_SetMobjState(mobj, mobj->info->spawnstate);
-						mobj->fuse = 100;
-						S_StopSound(mobj);
-						S_StartSound(mobj, sfx_s3k8c);
-					}
-					else if (mobj->extravalue2 == 1)
-					{
-						mobj->fuse = 50;
-						S_StartSound(mobj, sfx_s3ka3);
-					}
-					else
-					{
-						P_SetMobjState(mobj, mobj->info->meleestate);
-						mobj->fuse = 100;
-						S_StopSound(mobj);
-						S_StartSound(mobj, sfx_s3kc2l);
-					}
-					return;
-				case MT_PLAYER:
-					break; // don't remove
-				default:
-					P_SetMobjState(mobj, mobj->info->xdeathstate); // will remove the mobj if S_NULL.
-					break;
-				// Looking for monitors? They moved to a special condition above.
-			}
-			if (P_MobjWasRemoved(mobj))
-				return;
+	if (mobj->scale != mobj->destscale)
+		P_MobjScaleThink(mobj); // Slowly scale up/down to reach your destscale.
+
+	if ((mobj->type == MT_GHOST || mobj->type == MT_THOK) && mobj->fuse > 0) // Not guaranteed to be MF_SCENERY or not MF_SCENERY!
+	{
+		if (mobj->flags2 & MF2_BOSSNOTRAP) // "fast" flag
+		{
+			if ((signed)((mobj->frame & FF_TRANSMASK) >> FF_TRANSSHIFT) < (NUMTRANSMAPS-1) - (2*mobj->fuse)/3)
+				// fade out when nearing the end of fuse...
+				mobj->frame = (mobj->frame & ~FF_TRANSMASK) | (((NUMTRANSMAPS-1) - (2*mobj->fuse)/3) << FF_TRANSSHIFT);
+		}
+		else
+		{
+			if ((signed)((mobj->frame & FF_TRANSMASK) >> FF_TRANSSHIFT) < (NUMTRANSMAPS-1) - mobj->fuse / 2)
+				// fade out when nearing the end of fuse...
+				mobj->frame = (mobj->frame & ~FF_TRANSMASK) | (((NUMTRANSMAPS-1) - mobj->fuse / 2) << FF_TRANSSHIFT);
 		}
 	}
 
+	// Special thinker for scenery objects
+	if (mobj->flags & MF_SCENERY)
+	{
+		P_MobjSceneryThink(mobj);
+		return;
+	}
+
+#ifdef HAVE_BLUA
+	// Check for a Lua thinker first
+	if (!mobj->player)
+	{
+		if (LUAh_MobjThinker(mobj) || P_MobjWasRemoved(mobj))
+			return;
+	}
+	else if (!mobj->player->spectator)
+	{
+		// You cannot short-circuit the player thinker like you can other thinkers.
+		LUAh_MobjThinker(mobj);
+		if (P_MobjWasRemoved(mobj))
+			return;
+	}
+#endif
+	// if it's pushable, or if it would be pushable other than temporary disablement, use the
+	// separate thinker
+	if (mobj->flags & MF_PUSHABLE || (mobj->info->flags & MF_PUSHABLE && mobj->fuse))
+	{
+		if (!P_MobjPushableThink(mobj))
+			return;
+	}
+	else if (mobj->flags & MF_BOSS)
+	{
+		if (!P_MobjBossThink(mobj))
+			return;
+	}
+	else if (mobj->health <= 0) // Dead things think differently than the living.
+	{
+		if (!P_MobjDeadThink(mobj))
+			return;
+	}
+	else
+	{
+		if (!P_MobjRegularThink(mobj))
+			return;
+	}
+	if (P_MobjWasRemoved(mobj))
+		return;
+
+	if (mobj->flags2 & MF2_FIRING)
+		P_FiringThink(mobj);
+
+	if (mobj->flags & MF_AMBIENT)
+	{
+		if (!(leveltime % mobj->health) && mobj->info->seesound)
+			S_StartSound(mobj, mobj->info->seesound);
+		return;
+	}
+
+	// Check fuse
+	if (mobj->fuse && !P_FuseThink(mobj))
+		return;
+
 	I_Assert(mobj != NULL);
 	I_Assert(!P_MobjWasRemoved(mobj));
 
@@ -11085,14 +11268,24 @@ void P_PrecipitationEffects(void)
 	}
 }
 
+/** Returns corresponding mobj type from mapthing number.
+ * \param mthingtype Mapthing number in question.
+ * \return Mobj type; MT_UNKNOWN if nothing found.
+ */
+static mobjtype_t P_GetMobjtype(UINT16 mthingtype)
+{
+	mobjtype_t i;
+	for (i = 0; i < NUMMOBJTYPES; i++)
+		if (mthingtype == mobjinfo[i].doomednum)
+			return i;
+	return MT_UNKNOWN;
+}
+
 //
 // P_RespawnSpecials
 //
 void P_RespawnSpecials(void)
 {
-	fixed_t x, y, z;
-	subsector_t *ss;
-	mobj_t *mo = NULL;
 	mapthing_t *mthing = NULL;
 
 	// only respawn items when cv_itemrespawn is on
@@ -11122,68 +11315,8 @@ void P_RespawnSpecials(void)
 #endif
 
 	if (mthing)
-	{
-		mobjtype_t i;
-		x = mthing->x << FRACBITS;
-		y = mthing->y << FRACBITS;
-		ss = R_PointInSubsector(x, y);
-
-		// find which type to spawn
-		for (i = 0; i < NUMMOBJTYPES; i++)
-			if (mthing->type == mobjinfo[i].doomednum)
-				break;
+		P_SpawnMapThing(mthing);
 
-		if (i == NUMMOBJTYPES) // prevent creation of objects with this type -- Monster Iestyn 17/12/17
-		{
-			// 3D Mode start Thing is unlikely to be added to the que,
-			// so don't bother checking for that specific type
-			CONS_Alert(CONS_WARNING, M_GetText("P_RespawnSpecials: Unknown thing type %d attempted to respawn at (%d, %d)\n"), mthing->type, mthing->x, mthing->y);
-			// pull it from the que
-			iquetail = (iquetail+1)&(ITEMQUESIZE-1);
-			return;
-		}
-
-		//CTF rings should continue to respawn as normal rings outside of CTF.
-		if (gametype != GT_CTF)
-		{
-			if (i == MT_REDTEAMRING || i == MT_BLUETEAMRING)
-				i = MT_RING;
-		}
-
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			z = (
-#ifdef ESLOPE
-			ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) :
-#endif
-			ss->sector->ceilingheight) - (mthing->options >> ZSHIFT) * FRACUNIT;
-			if (mthing->options & MTF_AMBUSH
-			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i)))
-				z -= 24*FRACUNIT;
-			z -= mobjinfo[i].height; // Don't forget the height!
-		}
-		else
-		{
-			z = (
-#ifdef ESLOPE
-			ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) :
-#endif
-			ss->sector->floorheight) + (mthing->options >> ZSHIFT) * FRACUNIT;
-			if (mthing->options & MTF_AMBUSH
-			&& (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || i == MT_NIGHTSSTAR || P_WeaponOrPanel(i)))
-				z += 24*FRACUNIT;
-		}
-
-		mo = P_SpawnMobj(x, y, z, i);
-		mo->spawnpoint = mthing;
-		mo->angle = ANGLE_45 * (mthing->angle/45);
-
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			mo->eflags |= MFE_VERTICALFLIP;
-			mo->flags2 |= MF2_OBJECTFLIP;
-		}
-	}
 	// pull it from the que
 	iquetail = (iquetail+1)&(ITEMQUESIZE-1);
 }
@@ -11215,7 +11348,10 @@ void P_SpawnPlayer(INT32 playernum)
 	{
 		p->outofcoop = false;
 		if (netgame && p->jointime < 1)
-			p->spectator = true;
+		{
+			// Averted by GTR_NOSPECTATORSPAWN.
+			p->spectator = (gametyperules & GTR_NOSPECTATORSPAWN) ? false : true;
+		}
 		else if (multiplayer && !netgame)
 		{
 			// If you're in a team game and you don't have a team assigned yet...
@@ -11258,7 +11394,7 @@ void P_SpawnPlayer(INT32 playernum)
 			p->skincolor = skincolor_blueteam;
 	}
 
-	if ((netgame || multiplayer) && (gametype != GT_COOP || leveltime) && !p->spectator && !(maptol & TOL_NIGHTS))
+	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);
@@ -11294,7 +11430,7 @@ void P_SpawnPlayer(INT32 playernum)
 	mobj->radius = FixedMul(skins[p->skin].radius, mobj->scale);
 	mobj->height = P_GetPlayerHeight(p);
 
-	if (!leveltime && ((maptol & TOL_NIGHTS) == TOL_NIGHTS) != (G_IsSpecialStage(gamemap))) // non-special NiGHTS stage or special non-NiGHTS stage
+	if (!leveltime && !p->spectator && ((maptol & TOL_NIGHTS) == TOL_NIGHTS) != (G_IsSpecialStage(gamemap))) // non-special NiGHTS stage or special non-NiGHTS stage
 	{
 		if (maptol & TOL_NIGHTS)
 		{
@@ -11366,7 +11502,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
 
 	fixed_t z;
 	sector_t *sector;
-	fixed_t floor, ceiling;
+	fixed_t floor, ceiling, ceilingspawn;
 
 	player_t *p = &players[playernum];
 	mobj_t *mobj = p->mo;
@@ -11393,23 +11529,18 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
 	sector->c_slope ? P_GetZAt(sector->c_slope, x, y) :
 #endif
 	sector->ceilingheight;
+	ceilingspawn = ceiling - mobjinfo[MT_PLAYER].height;
 
 	if (mthing)
 	{
+		fixed_t offset = mthing->z << FRACBITS;
+
 		// Flagging a player's ambush will make them start on the ceiling
 		// Objectflip inverts
 		if (!!(mthing->options & MTF_AMBUSH) ^ !!(mthing->options & MTF_OBJECTFLIP))
-		{
-			z = ceiling - mobjinfo[MT_PLAYER].height;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
+			z = ceilingspawn - offset;
 		else
-		{
-			z = floor;
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
+			z = floor + offset;
 
 		if (mthing->options & MTF_OBJECTFLIP) // flip the player!
 		{
@@ -11426,8 +11557,8 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
 
 	if (z < floor)
 		z = floor;
-	else if (z > ceiling - mobjinfo[MT_PLAYER].height)
-		z = ceiling - mobjinfo[MT_PLAYER].height;
+	else if (z > ceilingspawn)
+		z = ceilingspawn;
 
 	mobj->floorz = floor;
 	mobj->ceilingz = ceiling;
@@ -11515,442 +11646,980 @@ void P_MovePlayerToStarpost(INT32 playernum)
 mapthing_t *huntemeralds[MAXHUNTEMERALDS];
 INT32 numhuntemeralds;
 
-//
-// P_SpawnMapThing
-// The fields of the mapthing should
-// already be in host byte order.
-//
-void P_SpawnMapThing(mapthing_t *mthing)
+static fixed_t P_GetMobjSpawnHeight(const mobjtype_t mobjtype, const fixed_t x, const fixed_t y, const fixed_t offset, const boolean flip)
 {
-	mobjtype_t i;
-	mobj_t *mobj;
-	fixed_t x, y, z;
-	subsector_t *ss;
-	boolean doangle = true;
+	const subsector_t *ss = R_PointInSubsector(x, y);
+
+	// Axis objects snap to the floor.
+	if (mobjtype == MT_AXIS || mobjtype == MT_AXISTRANSFER || mobjtype == MT_AXISTRANSFERLINE)
+		return ONFLOORZ;
+
+	// Establish height.
+	if (flip)
+		return (
+#ifdef ESLOPE
+			ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) :
+#endif
+			ss->sector->ceilingheight) - offset - mobjinfo[mobjtype].height;
+	else
+		return (
+#ifdef ESLOPE
+			ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) :
+#endif
+			ss->sector->floorheight) + offset;
+}
+
+static fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mthing, const fixed_t x, const fixed_t y)
+{
+	fixed_t offset = mthing->z << FRACBITS;
+	boolean flip = (!!(mobjinfo[mobjtype].flags & MF_SPAWNCEILING) ^ !!(mthing->options & MTF_OBJECTFLIP));
+
+	switch (mobjtype)
+	{
+	// Bumpers never spawn flipped.
+	case MT_NIGHTSBUMPER:
+		flip = false;
+		break;
+
+	// Objects with a non-zero default height.
+	case MT_CRAWLACOMMANDER:
+	case MT_DETON:
+	case MT_JETTBOMBER:
+	case MT_JETTGUNNER:
+	case MT_EGGMOBILE2:
+		if (!offset)
+			offset = 33*FRACUNIT;
+		break;
+	case MT_EGGMOBILE:
+		if (!offset)
+			offset = 128*FRACUNIT;
+		break;
+	case MT_GOLDBUZZ:
+	case MT_REDBUZZ:
+		if (!offset)
+			offset = 288*FRACUNIT;
+		break;
+
+	// Horizontal springs, may float additional units with MTF_AMBUSH.
+	case MT_YELLOWHORIZ:
+	case MT_REDHORIZ:
+	case MT_BLUEHORIZ:
+		offset += mthing->options & MTF_AMBUSH ? 16*FRACUNIT : 0;
+		break;
+
+	// Ring-like items, may float additional units with MTF_AMBUSH.
+	case MT_SPIKEBALL:
+	case MT_EMERALDSPAWN:
+	case MT_TOKEN:
+	case MT_EMBLEM:
+	case MT_RING:
+	case MT_REDTEAMRING:
+	case MT_BLUETEAMRING:
+	case MT_COIN:
+	case MT_BLUESPHERE:
+	case MT_BOMBSPHERE:
+	case MT_NIGHTSCHIP:
+	case MT_NIGHTSSTAR:
+		offset += mthing->options & MTF_AMBUSH ? 24*FRACUNIT : 0;
+		break;
+
+	// Remaining objects.
+	default:
+		if (P_WeaponOrPanel(mobjtype))
+			offset += mthing->options & MTF_AMBUSH ? 24*FRACUNIT : 0;
+	}
+
+	if (!offset) // Snap to the surfaces when there's no offset set.
+	{
+		if (flip)
+			return ONCEILINGZ;
+		else
+			return ONFLOORZ;
+	}
+
+	return P_GetMobjSpawnHeight(mobjtype, x, y, offset, flip);
+}
+
+static boolean P_SpawnNonMobjMapThing(mapthing_t *mthing)
+{
+#if MAXPLAYERS > 32
+	You should think about modifying the deathmatch starts to take full advantage of this!
+#endif
+	if (mthing->type <= MAXPLAYERS) // Player starts
+	{
+		// save spots for respawning in network games
+		if (!metalrecording)
+			playerstarts[mthing->type - 1] = mthing;
+		return true;
+	}
+	else if (mthing->type == 33) // Match starts
+	{
+		if (numdmstarts < MAX_DM_STARTS)
+		{
+			deathmatchstarts[numdmstarts] = mthing;
+			mthing->type = 0;
+			numdmstarts++;
+		}
+		return true;
+	}
+	else if (mthing->type == 34) // Red CTF starts
+	{
+		if (numredctfstarts < MAXPLAYERS)
+		{
+			redctfstarts[numredctfstarts] = mthing;
+			mthing->type = 0;
+			numredctfstarts++;
+		}
+		return true;
+	}
+	else if (mthing->type == 35) // Blue CTF starts
+	{
+		if (numbluectfstarts < MAXPLAYERS)
+		{
+			bluectfstarts[numbluectfstarts] = mthing;
+			mthing->type = 0;
+			numbluectfstarts++;
+		}
+		return true;
+	}
+	else if (metalrecording && mthing->type == mobjinfo[MT_METALSONIC_RACE].doomednum)
+	{ // If recording, you ARE Metal Sonic. Do not spawn it, do not save normal spawnpoints.
+		playerstarts[0] = mthing;
+		return true;
+	}
+	else if (mthing->type == 750 // Slope vertex point (formerly chaos spawn)
+		     || (mthing->type >= 600 && mthing->type <= 609) // Special placement patterns
+		     || mthing->type == 1705 || mthing->type == 1713) // Hoops
+		return true; // These are handled elsewhere.
+	else if (mthing->type == mobjinfo[MT_EMERHUNT].doomednum)
+	{
+		// Emerald Hunt is Coop only. Don't spawn the emerald yet, but save the spawnpoint for later.
+		if ((gametyperules & GTR_EMERALDHUNT) && numhuntemeralds < MAXHUNTEMERALDS)
+			huntemeralds[numhuntemeralds++] = mthing;
+		return true;
+	}
+
+	return false;
+}
+
+static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
+{
+	switch (i)
+	{
+	case MT_EMERALD1:
+	case MT_EMERALD2:
+	case MT_EMERALD3:
+	case MT_EMERALD4:
+	case MT_EMERALD5:
+	case MT_EMERALD6:
+	case MT_EMERALD7:
+		if (gametype != GT_COOP) // Don't place emeralds in non-coop modes
+			return false;
+
+		if (metalrecording)
+			return false; // Metal Sonic isn't for collecting emeralds.
+
+		if (emeralds & mobjinfo[i].speed) // You already have this emerald!
+			return false;
+
+		break;
+	case MT_EMERALDSPAWN:
+		if (!cv_powerstones.value)
+			return false;
+
+		if (!(gametyperules & GTR_POWERSTONES))
+			return false;
+
+		runemeraldmanager = true;
+		break;
+	case MT_ROSY:
+		if (!(gametype == GT_COOP || (mthing->options & MTF_EXTRA)))
+			return false; // she doesn't hang out here
+
+		if (!mariomode && !(netgame || multiplayer) && players[consoleplayer].skin == 3)
+			return false; // no doubles
+
+		break;
+	case MT_TOKEN:
+		if (!(gametyperules & GTR_EMERALDTOKENS))
+			return false; // Gametype's not right
+
+		if (tokenbits == 30)
+			return false; // Too many tokens
+
+		if (tokenlist & (1 << tokenbits++))
+			return false; // You already got this token
+
+		break;
+	case MT_EMBLEM:
+		if (netgame || multiplayer)
+			return false; // Single player
+
+		if (modifiedgame && !savemoddata)
+			return false; // No cheating!!
+
+		break;
+	default:
+		break;
+	}
+
+	if (metalrecording) // Metal Sonic can't use these things.
+	{
+		if ((mobjinfo[i].flags & (MF_ENEMY|MF_BOSS)) || i == MT_TOKEN || i == MT_STARPOST
+			|| i == MT_RING || i == MT_BLUETEAMRING || i == MT_REDTEAMRING || i == MT_COIN
+			|| i == MT_BLUESPHERE || i == MT_BOMBSPHERE || i == MT_NIGHTSCHIP || i == MT_NIGHTSSTAR)
+			return false;
+	}
+
+	if (((mobjinfo[i].flags & MF_ENEMY) || (mobjinfo[i].flags & MF_BOSS)) && !(gametyperules & GTR_SPAWNENEMIES))
+		return false; // No enemies in ringslinger modes
+
+	if (!(gametyperules & GTR_ALLOWEXIT) && (i == MT_SIGN))
+		return false; // Don't spawn exit signs in wrong game modes
+
+	if (!G_PlatformGametype() && (i == MT_STARPOST))
+		return false; // Don't spawn starposts in wrong game modes
+
+	if (!G_RingSlingerGametype() || !cv_specialrings.value)
+		if (P_WeaponOrPanel(i))
+			return false; // Don't place weapons/panels in non-ringslinger modes
+
+	if (!(gametyperules & GTR_TEAMFLAGS)) // CTF specific things
+	{
+		if (i == MT_BLUEFLAG || i == MT_REDFLAG)
+			return false; // No flags in non-CTF modes!
+	}
+	else
+	{
+		if ((i == MT_BLUEFLAG && blueflag) || (i == MT_REDFLAG && redflag))
+		{
+			CONS_Alert(CONS_ERROR, M_GetText("Only one flag per team allowed in CTF!\n"));
+			return false;
+		}
+	}
+
+	if (modeattacking) // Record Attack special stuff
+	{
+		// Don't spawn starposts that wouldn't be usable
+		if (i == MT_STARPOST)
+			return false;
+	}
+
+	if (ultimatemode)
+	{
+		if (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING
+			|| i == MT_COIN || i == MT_NIGHTSSTAR || i == MT_NIGHTSCHIP
+			|| i == MT_PITY_BOX || i == MT_ELEMENTAL_BOX || i == MT_ATTRACT_BOX
+			|| i == MT_FORCE_BOX || i == MT_ARMAGEDDON_BOX || i == MT_WHIRLWIND_BOX
+			|| i == MT_FLAMEAURA_BOX || i == MT_BUBBLEWRAP_BOX || i == MT_THUNDERCOIN_BOX
+			|| i == MT_RING_BOX || i == MT_STARPOST)
+			return false; // No rings or shields in Ultimate mode
+
+		// Don't include the gold repeating boxes here please.
+		// They're likely facets of the level's design and therefore required to progress.
+	}
+
+	return true;
+}
+
+#define nightsreplace ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap))
+
+static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i)
+{
+	// Altering monitor spawns via cvars
+	// If MF_GRENADEBOUNCE is set in the monitor's info,
+	// skip this step. (Used for gold monitors)
+	// Yeah, this is a dirty hack.
+	if ((mobjinfo[i].flags & (MF_MONITOR|MF_GRENADEBOUNCE)) == MF_MONITOR)
+	{
+		if (gametyperules & GTR_RACE)
+		{
+			// Set powerup boxes to user settings for competition.
+			switch (cv_competitionboxes.value)
+			{
+			case 1: // Mystery
+				return MT_MYSTERY_BOX;
+			case 2: // Teleport
+				return MT_MIXUP_BOX;
+			case 3: // None
+				return MT_NULL; // Don't spawn!
+			default:
+				return i;
+			}
+		}
+		// Set powerup boxes to user settings for other netplay modes
+		else if (gametype != GT_COOP)
+		{
+			switch (cv_matchboxes.value)
+			{
+			case 1: // Mystery
+				return MT_MYSTERY_BOX;
+			case 2: // Unchanging
+				if (i == MT_MYSTERY_BOX)
+					return MT_NULL; // don't spawn
+				mthing->options &= ~(MTF_AMBUSH|MTF_OBJECTSPECIAL); // no random respawning!
+				return i;
+			case 3: // Don't spawn
+				return MT_NULL;
+			default:
+				return i;
+			}
+		}
+	}
 
-	if (!mthing->type)
-		return; // Ignore type-0 things as NOPs
+	if (nightsreplace)
+	{
+		if (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN)
+			return MT_NIGHTSSTAR;
 
-	// Always spawn in objectplace.
-	// Skip all returning code.
-	if (objectplacing)
+		if (i == MT_BLUESPHERE)
+			return MT_NIGHTSCHIP;
+	}
+
+	if (!(gametyperules & GTR_TEAMS))
 	{
-		// find which type to spawn
-		for (i = 0; i < NUMMOBJTYPES; i++)
-			if (mthing->type == mobjinfo[i].doomednum)
-				break;
+		if (i == MT_BLUETEAMRING || i == MT_REDTEAMRING)
+			return MT_RING;
 
-		if (i == NUMMOBJTYPES)
-		{
-			if (mthing->type == 3328) // 3D Mode start Thing
-				return;
-			CONS_Alert(CONS_WARNING, M_GetText("Unknown thing type %d placed at (%d, %d)\n"), mthing->type, mthing->x, mthing->y);
-			i = MT_UNKNOWN;
-		}
-		goto noreturns;
+		if (i == MT_RING_BLUEBOX || i == MT_RING_REDBOX)
+			return MT_RING_BOX;
 	}
 
-	// count deathmatch start positions
-	if (mthing->type == 33)
+	if (modeattacking && i == MT_1UP_BOX) // 1UPs -->> Score TVs
 	{
-		if (numdmstarts < MAX_DM_STARTS)
-		{
-			deathmatchstarts[numdmstarts] = mthing;
-			mthing->type = 0;
-			numdmstarts++;
-		}
-		return;
+		// Either or, doesn't matter which.
+		if (mthing->options & (MTF_AMBUSH | MTF_OBJECTSPECIAL))
+			return MT_SCORE10K_BOX; // 10,000
+		else
+			return MT_SCORE1K_BOX; // 1,000
 	}
 
-	else if (mthing->type == 34) // Red CTF Starts
+	if (mariomode && i == MT_ROSY)
+		return MT_TOAD; // don't remove on penalty of death
+
+	return i;
+}
+
+static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj)
+{
+	INT32 j;
+	emblem_t* emblem = M_GetLevelEmblems(gamemap);
+	skincolors_t emcolor;
+
+	while (emblem)
 	{
-		if (numredctfstarts < MAXPLAYERS)
-		{
-			redctfstarts[numredctfstarts] = mthing;
-			mthing->type = 0;
-			numredctfstarts++;
-		}
-		return;
+		if ((emblem->type == ET_GLOBAL || emblem->type == ET_SKIN) && emblem->tag == mthing->angle)
+			break;
+
+		emblem = M_GetLevelEmblems(-1);
 	}
 
-	else if (mthing->type == 35) // Blue CTF Starts
+	if (!emblem)
 	{
-		if (numbluectfstarts < MAXPLAYERS)
-		{
-			bluectfstarts[numbluectfstarts] = mthing;
-			mthing->type = 0;
-			numbluectfstarts++;
-		}
-		return;
+		CONS_Debug(DBG_GAMELOGIC, "No map emblem for map %d with tag %d found!\n", gamemap, mthing->angle);
+		return false;
 	}
 
-	else if (mthing->type == 750) // Slope vertex point (formerly chaos spawn)
-		return;
+	j = emblem - emblemlocations;
+
+	I_Assert(emblemlocations[j].sprite >= 'A' && emblemlocations[j].sprite <= 'Z');
+	P_SetMobjState(mobj, mobj->info->spawnstate + (emblemlocations[j].sprite - 'A'));
+
+	mobj->health = j + 1;
+	emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
+	mobj->color = (UINT8)emcolor;
 
-	else if (mthing->type == mobjinfo[MT_RING].doomednum || mthing->type == mobjinfo[MT_COIN].doomednum
-	 || mthing->type == mobjinfo[MT_REDTEAMRING].doomednum || mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum
-	 || mthing->type == mobjinfo[MT_BLUESPHERE].doomednum || mthing->type == mobjinfo[MT_BOMBSPHERE].doomednum
-	 || (mthing->type >= 600 && mthing->type <= 609) // circles and diagonals
-	 || mthing->type == 1705 || mthing->type == 1713) // hoops
+	if (emblemlocations[j].collected
+		|| (emblemlocations[j].type == ET_SKIN && emblemlocations[j].var != players[0].skin))
 	{
-		// Don't spawn hoops, wings, or rings yet!
-		return;
+		P_UnsetThingPosition(mobj);
+		mobj->flags |= MF_NOCLIP;
+		mobj->flags &= ~MF_SPECIAL;
+		mobj->flags |= MF_NOBLOCKMAP;
+		mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
+		P_SetThingPosition(mobj);
 	}
-
-	// check for players specially
-#if MAXPLAYERS > 32
-You should think about modifying the deathmatch starts to take full advantage of this!
-#endif
-	if (mthing->type > 0 && mthing->type <= MAXPLAYERS)
+	else
 	{
-		// save spots for respawning in network games
-		if (!metalrecording)
-			playerstarts[mthing->type-1] = mthing;
-		return;
+		mobj->frame &= ~FF_TRANSMASK;
+
+		if (emblemlocations[j].type == ET_GLOBAL)
+		{
+			mobj->reactiontime = emblemlocations[j].var;
+			if (emblemlocations[j].var & GE_NIGHTSITEM)
+			{
+				mobj->flags |= MF_NIGHTSITEM;
+				mobj->flags &= ~MF_SPECIAL;
+				mobj->flags2 |= MF2_DONTDRAW;
+			}
+		}
 	}
+	return true;
+}
 
-	if (metalrecording && mthing->type == mobjinfo[MT_METALSONIC_RACE].doomednum)
-	{ // If recording, you ARE Metal Sonic. Do not spawn it, do not save normal spawnpoints.
-		playerstarts[0] = mthing;
-		return;
+static boolean P_SetupMace(mapthing_t *mthing, mobj_t *mobj, boolean *doangle)
+{
+	fixed_t mlength, mmaxlength, mlengthset, mspeed, mphase, myaw, mpitch, mminlength, mnumspokes, mpinch, mroll, mnumnospokes, mwidth, mwidthset, mmin, msound, radiusfactor, widthfactor;
+	angle_t mspokeangle;
+	mobjtype_t chainlink, macetype, firsttype, linktype;
+	boolean mdosound, mdocenter, mchainlike = false;
+	mobj_t *spawnee = NULL, *hprev = mobj;
+	mobjflag_t mflagsapply;
+	mobjflag2_t mflags2apply;
+	mobjeflag_t meflagsapply;
+	INT32 line;
+	const size_t mthingi = (size_t)(mthing - mapthings);
+
+	// Find the corresponding linedef special, using angle as tag
+	// P_FindSpecialLineFromTag works here now =D
+	line = P_FindSpecialLineFromTag(9, mthing->angle, -1);
+
+	if (line == -1)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Mace chain (mapthing #%s) needs to be tagged to a #9 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle);
+		return false;
+	}
+	/*
+	mapthing -
+	MTF_AMBUSH :
+		MT_SPRINGBALLPOINT - upgrade from yellow to red spring
+		anything else - bigger mace/chain theory
+	MTF_OBJECTSPECIAL - force silent
+	MTF_GRAVFLIP - flips objects, doesn't affect chain arrangements
+	Parameter value : number of "spokes"
+
+	linedef -
+	ML_NOCLIMB :
+		MT_CHAINPOINT/MT_CHAINMACEPOINT with ML_EFFECT1 applied - Direction not controllable
+		anything else - no functionality
+	ML_EFFECT1 : Swings instead of spins
+	ML_EFFECT2 : Linktype is replaced with macetype for all spokes not ending in chains (inverted for MT_FIREBARPOINT)
+	ML_EFFECT3 : Spawn a bonus linktype at the hinge point
+	ML_EFFECT4 : Don't clip inside the ground
+	ML_EFFECT5 : Don't stop thinking when too far away
+	*/
+	mlength = abs(lines[line].dx >> FRACBITS);
+	mspeed = abs(lines[line].dy >> (FRACBITS - 4));
+	mphase = (sides[lines[line].sidenum[0]].textureoffset >> FRACBITS) % 360;
+	if ((mminlength = -sides[lines[line].sidenum[0]].rowoffset >> FRACBITS) < 0)
+		mminlength = 0;
+	else if (mminlength > mlength - 1)
+		mminlength = mlength - 1;
+	mpitch = (lines[line].frontsector->floorheight >> FRACBITS) % 360;
+	myaw = (lines[line].frontsector->ceilingheight >> FRACBITS) % 360;
+
+	mnumspokes = mthing->extrainfo + 1;
+	mspokeangle = FixedAngle((360*FRACUNIT)/mnumspokes) >> ANGLETOFINESHIFT;
+
+	if (lines[line].backsector)
+	{
+		mpinch = (lines[line].backsector->floorheight >> FRACBITS) % 360;
+		mroll = (lines[line].backsector->ceilingheight >> FRACBITS) % 360;
+		mnumnospokes = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS);
+		if ((mwidth = sides[lines[line].sidenum[1]].rowoffset >> FRACBITS) < 0)
+			mwidth = 0;
 	}
+	else
+		mpinch = mroll = mnumnospokes = mwidth = 0;
+
+	CONS_Debug(DBG_GAMELOGIC, "Mace/Chain (mapthing #%s):\n"
+		"Length is %d (minus %d)\n"
+		"Speed is %d\n"
+		"Phase is %d\n"
+		"Yaw is %d\n"
+		"Pitch is %d\n"
+		"No. of spokes is %d (%d antispokes)\n"
+		"Pinch is %d\n"
+		"Roll is %d\n"
+		"Width is %d\n",
+		sizeu1(mthingi), mlength, mminlength, mspeed, mphase, myaw, mpitch, mnumspokes, mnumnospokes, mpinch, mroll, mwidth);
+
+	if (mnumnospokes > 0 && (mnumnospokes < mnumspokes))
+		mnumnospokes = mnumspokes/mnumnospokes;
+	else
+		mnumnospokes = ((mobj->type == MT_CHAINMACEPOINT) ? (mnumspokes) : 0);
 
-	// find which type to spawn
-	for (i = 0; i < NUMMOBJTYPES; i++)
-		if (mthing->type == mobjinfo[i].doomednum)
-			break;
+	mobj->lastlook = mspeed;
+	mobj->movecount = mobj->lastlook;
+	mobj->angle = FixedAngle(myaw << FRACBITS);
+	*doangle = false;
+	mobj->threshold = (FixedAngle(mpitch << FRACBITS) >> ANGLETOFINESHIFT);
+	mobj->movefactor = mpinch;
+	mobj->movedir = 0;
 
-	if (i == NUMMOBJTYPES)
+	// Mobjtype selection
+	switch (mobj->type)
 	{
-		if (mthing->type == 3328) // 3D Mode start Thing
-			return;
-		CONS_Alert(CONS_WARNING, M_GetText("Unknown thing type %d placed at (%d, %d)\n"), mthing->type, mthing->x, mthing->y);
-		i = MT_UNKNOWN;
+	case MT_SPRINGBALLPOINT:
+		macetype = ((mthing->options & MTF_AMBUSH)
+			? MT_REDSPRINGBALL
+			: MT_YELLOWSPRINGBALL);
+		chainlink = MT_SMALLMACECHAIN;
+		break;
+	case MT_FIREBARPOINT:
+		macetype = ((mthing->options & MTF_AMBUSH)
+			? MT_BIGFIREBAR
+			: MT_SMALLFIREBAR);
+		chainlink = MT_NULL;
+		break;
+	case MT_CUSTOMMACEPOINT:
+		macetype = (mobjtype_t)sides[lines[line].sidenum[0]].toptexture;
+		if (lines[line].backsector)
+			chainlink = (mobjtype_t)sides[lines[line].sidenum[1]].toptexture;
+		else
+			chainlink = MT_NULL;
+		break;
+	case MT_CHAINPOINT:
+		if (mthing->options & MTF_AMBUSH)
+		{
+			macetype = MT_BIGGRABCHAIN;
+			chainlink = MT_BIGMACECHAIN;
+		}
+		else
+		{
+			macetype = MT_SMALLGRABCHAIN;
+			chainlink = MT_SMALLMACECHAIN;
+		}
+		mchainlike = true;
+		break;
+	default:
+		if (mthing->options & MTF_AMBUSH)
+		{
+			macetype = MT_BIGMACE;
+			chainlink = MT_BIGMACECHAIN;
+		}
+		else
+		{
+			macetype = MT_SMALLMACE;
+			chainlink = MT_SMALLMACECHAIN;
+		}
+		break;
 	}
 
-	if (metalrecording) // Metal Sonic can't use these things.
-		if (mobjinfo[i].flags & (MF_ENEMY|MF_BOSS) || i == MT_TOKEN || i == MT_STARPOST)
-			return;
+	if (!macetype && !chainlink)
+		return true;
 
-	if (i >= MT_EMERALD1 && i <= MT_EMERALD7) // Pickupable Emeralds
+	if (mobj->type == MT_CHAINPOINT)
 	{
-		if (gametype != GT_COOP) // Don't place emeralds in non-coop modes
-			return;
+		if (!mlength)
+			return true;
+	}
+	else
+		mlength++;
 
-		if (metalrecording)
-			return; // Metal Sonic isn't for collecting emeralds.
+	firsttype = macetype;
 
-		if (emeralds & mobjinfo[i].speed) // You already have this emerald!
-			return;
-	}
+	// Adjustable direction
+	if (lines[line].flags & ML_NOCLIMB)
+		mobj->flags |= MF_SLIDEME;
 
-	if (i == MT_EMERHUNT)
+	// Swinging
+	if (lines[line].flags & ML_EFFECT1)
 	{
-		// Emerald Hunt is Coop only.
-		if (gametype != GT_COOP)
-			return;
+		mobj->flags2 |= MF2_STRONGBOX;
+		mmin = ((mnumnospokes > 1) ? 1 : 0);
+	}
+	else
+		mmin = mnumspokes;
 
-		ss = R_PointInSubsector(mthing->x << FRACBITS, mthing->y << FRACBITS);
-		mthing->z = (INT16)(((
-#ifdef ESLOPE
-								ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, mthing->x << FRACBITS, mthing->y << FRACBITS) :
-#endif
-								ss->sector->floorheight)>>FRACBITS) + (mthing->options >> ZSHIFT));
+	// If over distance away, don't move UNLESS this flag is applied
+	if (lines[line].flags & ML_EFFECT5)
+		mobj->flags2 |= MF2_BOSSNOTRAP;
 
-		if (numhuntemeralds < MAXHUNTEMERALDS)
-			huntemeralds[numhuntemeralds++] = mthing;
-		return;
+	// Make the links the same type as the end - repeated below
+	if ((mobj->type != MT_CHAINPOINT) && (((lines[line].flags & ML_EFFECT2) == ML_EFFECT2) != (mobj->type == MT_FIREBARPOINT))) // exclusive or
+	{
+		linktype = macetype;
+		radiusfactor = 2; // Double the radius.
 	}
+	else
+		radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
 
-	if (i == MT_EMERALDSPAWN)
-	{
-		if (!cv_powerstones.value)
-			return;
+	if (!mchainlike)
+		mchainlike = (firsttype == chainlink);
+	widthfactor = (mchainlike ? 1 : 2);
 
-		if (!(gametype == GT_MATCH || gametype == GT_CTF))
-			return;
+	mflagsapply = ((lines[line].flags & ML_EFFECT4) ? 0 : (MF_NOCLIP | MF_NOCLIPHEIGHT));
+	mflags2apply = ((mthing->options & MTF_OBJECTFLIP) ? MF2_OBJECTFLIP : 0);
+	meflagsapply = ((mthing->options & MTF_OBJECTFLIP) ? MFE_VERTICALFLIP : 0);
 
-		runemeraldmanager = true;
-	}
+	msound = (mchainlike ? 0 : (mwidth & 1));
 
-	if (!G_PlatformGametype()) // No enemies in match or CTF modes
-		if ((mobjinfo[i].flags & MF_ENEMY) || (mobjinfo[i].flags & MF_BOSS))
-			return;
+	// Quick and easy preparatory variable setting
+	mphase = (FixedAngle(mphase << FRACBITS) >> ANGLETOFINESHIFT);
+	mroll = (FixedAngle(mroll << FRACBITS) >> ANGLETOFINESHIFT);
 
-	if (!G_RingSlingerGametype() || !cv_specialrings.value)
-		if (P_WeaponOrPanel(i))
-			return; // Don't place weapons/panels in non-ringslinger modes
+#define makemace(mobjtype, dist, moreflags2) {\
+	spawnee = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobjtype);\
+	P_SetTarget(&spawnee->tracer, mobj);\
+	spawnee->threshold = mphase;\
+	spawnee->friction = mroll;\
+	spawnee->movefactor = mwidthset;\
+	spawnee->movecount = dist;\
+	spawnee->angle = myaw;\
+	spawnee->flags |= (MF_NOGRAVITY|mflagsapply);\
+	spawnee->flags2 |= (mflags2apply|moreflags2);\
+	spawnee->eflags |= meflagsapply;\
+	P_SetTarget(&hprev->hnext, spawnee);\
+	P_SetTarget(&spawnee->hprev, hprev);\
+	hprev = spawnee;\
+}
 
-	// Altering monitor spawns via cvars
-	// If MF_GRENADEBOUNCE is set in the monitor's info,
-	// skip this step. (Used for gold monitors)
-	// Yeah, this is a dirty hack.
-	if ((mobjinfo[i].flags & (MF_MONITOR|MF_GRENADEBOUNCE)) == MF_MONITOR)
+	mdosound = (mspeed && !(mthing->options & MTF_OBJECTSPECIAL));
+	mdocenter = (macetype && (lines[line].flags & ML_EFFECT3));
+
+	// The actual spawning of spokes
+	while (mnumspokes-- > 0)
 	{
-		if (gametype == GT_COMPETITION || gametype == GT_RACE)
+		// Offsets
+		if (lines[line].flags & ML_EFFECT1) // Swinging
+			mroll = (mroll - mspokeangle) & FINEMASK;
+		else // Spinning
+			mphase = (mphase - mspokeangle) & FINEMASK;
+
+		if (mnumnospokes && !(mnumspokes % mnumnospokes)) // Skipping a "missing" spoke
 		{
-			// Set powerup boxes to user settings for competition.
-			if (cv_competitionboxes.value == 1) // Mystery
-				i = MT_MYSTERY_BOX;
-			else if (cv_competitionboxes.value == 2) // Teleport
-				i = MT_MIXUP_BOX;
-			else if (cv_competitionboxes.value == 3) // None
-				return; // Don't spawn!
-			// default case: normal
+			if (mobj->type != MT_CHAINMACEPOINT)
+				continue;
+
+			linktype = chainlink;
+			firsttype = ((mthing->options & MTF_AMBUSH) ? MT_BIGGRABCHAIN : MT_SMALLGRABCHAIN);
+			mmaxlength = 1 + (mlength - 1) * radiusfactor;
+			radiusfactor = widthfactor = 1;
 		}
-		// Set powerup boxes to user settings for other netplay modes
-		else if (gametype != GT_COOP)
+		else
 		{
-			if (cv_matchboxes.value == 1) // Mystery
-				i = MT_MYSTERY_BOX;
-			else if (cv_matchboxes.value == 2) // Unchanging
+			if (mobj->type == MT_CHAINMACEPOINT)
 			{
-				if (i == MT_MYSTERY_BOX)
-					return; // don't spawn
-				mthing->options &= ~(MTF_AMBUSH|MTF_OBJECTSPECIAL); // no random respawning!
+				// Make the links the same type as the end - repeated above
+				if (lines[line].flags & ML_EFFECT2)
+				{
+					linktype = macetype;
+					radiusfactor = 2;
+				}
+				else
+					radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
+
+				firsttype = macetype;
+				widthfactor = 2;
 			}
-			else if (cv_matchboxes.value == 3) // Don't spawn
-				return;
-			// default case: normal
+
+			mmaxlength = mlength;
 		}
-	}
 
-	if (gametype != GT_CTF) // CTF specific things
-	{
-		if (i == MT_RING_BLUEBOX || i == MT_RING_REDBOX)
-			i = MT_RING_BOX;
-		else if (i == MT_BLUEFLAG || i == MT_REDFLAG)
-			return; // No flags in non-CTF modes!
-	}
-	else
-	{
-		if ((i == MT_BLUEFLAG && blueflag) || (i == MT_REDFLAG && redflag))
+		mwidthset = mwidth;
+		mlengthset = mminlength;
+
+		if (mdocenter) // Innermost link
+			makemace(linktype, 0, 0);
+
+		// Out from the center...
+		if (linktype)
 		{
-			CONS_Alert(CONS_ERROR, M_GetText("Only one flag per team allowed in CTF!\n"));
-			return;
+			while ((++mlengthset) < mmaxlength)
+				makemace(linktype, radiusfactor*mlengthset, 0);
 		}
-	}
-
-	if (!G_PlatformGametype() && (i == MT_SIGN || i == MT_STARPOST))
-		return; // Don't spawn exit signs or starposts in wrong game modes
+		else
+			mlengthset = mmaxlength;
 
-	if (modeattacking) // Record Attack special stuff
-	{
-		// Don't spawn starposts that wouldn't be usable
-		if (i == MT_STARPOST)
-			return;
+		// Outermost mace/link
+		if (firsttype)
+			makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH);
 
-		// 1UPs -->> Score TVs
-		else if (i == MT_1UP_BOX) // 1UP
+		if (!mwidth)
+		{
+			if (mdosound && mnumspokes <= mmin) // Can it make a sound?
+				spawnee->flags2 |= MF2_BOSSNOTRAP;
+		}
+		else
 		{
-			// Either or, doesn't matter which.
-			if (mthing->options & (MTF_AMBUSH|MTF_OBJECTSPECIAL))
-				i = MT_SCORE10K_BOX; // 10,000
+			// Across the bar!
+			if (!firsttype)
+				mwidthset = -mwidth;
+			else if (mwidth > 0)
+			{
+				while ((mwidthset -= widthfactor) > -mwidth)
+				{
+					makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH);
+					if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound?
+						spawnee->flags2 |= MF2_BOSSNOTRAP;
+				}
+			}
 			else
-				i = MT_SCORE1K_BOX; // 1,000
+			{
+				while ((mwidthset += widthfactor) < -mwidth)
+				{
+					makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH);
+					if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound?
+						spawnee->flags2 |= MF2_BOSSNOTRAP;
+				}
+			}
+			mwidth = -mwidth;
+
+			// Outermost mace/link again!
+			if (firsttype)
+				makemace(firsttype, radiusfactor*(mlengthset--), MF2_AMBUSH);
+
+			// ...and then back into the center!
+			if (linktype)
+				while (mlengthset > mminlength)
+					makemace(linktype, radiusfactor*(mlengthset--), 0);
+
+			if (mdocenter) // Innermost link
+				makemace(linktype, 0, 0);
 		}
 	}
+#undef makemace
+	return true;
+}
 
-	if (ultimatemode)
-	{
-		if (i == MT_PITY_BOX || i == MT_ELEMENTAL_BOX || i == MT_ATTRACT_BOX
-		 || i == MT_FORCE_BOX || i == MT_ARMAGEDDON_BOX || i == MT_WHIRLWIND_BOX
-		 || i == MT_FLAMEAURA_BOX || i == MT_BUBBLEWRAP_BOX || i == MT_THUNDERCOIN_BOX
-		 || i == MT_RING_BOX || i == MT_STARPOST)
-			return; // No rings or shields in Ultimate mode
+static boolean P_SetupParticleGen(mapthing_t *mthing, mobj_t *mobj)
+{
+	fixed_t radius, speed;
+	INT32 type, numdivisions, anglespeed, ticcount;
+	angle_t angledivision;
+	INT32 line;
+	const size_t mthingi = (size_t)(mthing - mapthings);
 
-		// Don't include the gold repeating boxes here please.
-		// They're likely facets of the level's design and therefore required to progress.
+	// Find the corresponding linedef special, using angle as tag
+	line = P_FindSpecialLineFromTag(15, mthing->angle, -1);
+
+	if (line == -1)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Particle generator (mapthing #%s) needs to be tagged to a #15 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle);
+		return false;
 	}
 
-	if (i == MT_ROSY)
+	if (sides[lines[line].sidenum[0]].toptexture)
+		type = sides[lines[line].sidenum[0]].toptexture; // Set as object type in p_setup.c...
+	else
+		type = (INT32)MT_PARTICLE;
+
+	if (!lines[line].backsector
+		|| (ticcount = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS)) < 1)
+		ticcount = 3;
+
+	numdivisions = mthing->z;
+
+	if (numdivisions)
 	{
-		if (!(gametype == GT_COOP || (mthing->options & MTF_EXTRA)))
-			return; // she doesn't hang out here
-		else if (mariomode)
-			i = MT_TOAD; // don't remove on penalty of death
-		else if (!(netgame || multiplayer) && players[consoleplayer].skin == 3)
-			return; // no doubles
+		radius = R_PointToDist2(lines[line].v1->x, lines[line].v1->y, lines[line].v2->x, lines[line].v2->y);
+		anglespeed = (sides[lines[line].sidenum[0]].rowoffset >> FRACBITS) % 360;
+		angledivision = 360/numdivisions;
+	}
+	else
+	{
+		numdivisions = 1; // Simple trick to make A_ParticleSpawn simpler.
+		radius = 0;
+		anglespeed = 0;
+		angledivision = 0;
 	}
 
-	if (i == MT_TOKEN && ((gametype != GT_COOP && gametype != GT_COMPETITION) || ultimatemode || tokenbits == 30 || tokenlist & (1 << tokenbits++)))
-		return; // you already got this token, or there are too many, or the gametype's not right
+	speed = abs(sides[lines[line].sidenum[0]].textureoffset);
+	if (mthing->options & MTF_OBJECTFLIP)
+		speed *= -1;
+
+	CONS_Debug(DBG_GAMELOGIC, "Particle Generator (mapthing #%s):\n"
+		"Radius is %d\n"
+		"Speed is %d\n"
+		"Anglespeed is %d\n"
+		"Numdivisions is %d\n"
+		"Angledivision is %d\n"
+		"Type is %d\n"
+		"Tic seperation is %d\n",
+		sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, type, ticcount);
 
-	if (i == MT_EMBLEM && (netgame || multiplayer || (modifiedgame && !savemoddata))) // No cheating!!
-		return;
+	mobj->angle = 0;
+	mobj->movefactor = speed;
+	mobj->lastlook = numdivisions;
+	mobj->movedir = angledivision*ANG1;
+	mobj->movecount = anglespeed*ANG1;
+	mobj->friction = radius;
+	mobj->threshold = type;
+	mobj->reactiontime = ticcount;
+	mobj->cvmem = line;
+	mobj->watertop = mobj->waterbottom = 0;
+	return true;
+}
 
-	// Objectplace landing point
-	noreturns:
+static boolean P_SetupNiGHTSDrone(mapthing_t* mthing, mobj_t* mobj)
+{
+	boolean flip = mthing->options & MTF_OBJECTFLIP;
+	boolean topaligned = (mthing->options & MTF_OBJECTSPECIAL) && !(mthing->options & MTF_EXTRA);
+	boolean middlealigned = (mthing->options & MTF_EXTRA) && !(mthing->options & MTF_OBJECTSPECIAL);
+	boolean bottomoffsetted = !(mthing->options & MTF_OBJECTSPECIAL) && !(mthing->options & MTF_EXTRA);
 
-	// spawn it
-	x = mthing->x << FRACBITS;
-	y = mthing->y << FRACBITS;
-	ss = R_PointInSubsector(x, y);
+	INT16 timelimit = mthing->angle & 0xFFF;
+	fixed_t hitboxradius = ((mthing->angle & 0xF000) >> 12)*32*FRACUNIT;
+	fixed_t hitboxheight = mthing->extrainfo*32*FRACUNIT;
+	fixed_t oldheight = mobj->height;
+	fixed_t dronemanoffset, goaloffset, sparkleoffset, droneboxmandiff, dronemangoaldiff;
 
-	if (i == MT_NIGHTSBUMPER)
-		z = (
-#ifdef ESLOPE
-			ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) :
-#endif
-			ss->sector->floorheight) + ((mthing->options >> ZSHIFT) << FRACBITS);
-	else if (i == MT_AXIS || i == MT_AXISTRANSFER || i == MT_AXISTRANSFERLINE)
-		z = ONFLOORZ;
-	else if (i == MT_SPIKEBALL || P_WeaponOrPanel(i) || i == MT_EMERALDSPAWN || i == MT_TOKEN || i == MT_EMBLEM)
-	{
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			z = (
-#ifdef ESLOPE
-			ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) :
-#endif
-			ss->sector->ceilingheight);
+	if (timelimit > 0)
+		mobj->health = timelimit;
+
+	if (hitboxradius > 0)
+		mobj->radius = hitboxradius;
+
+	if (hitboxheight > 0)
+		mobj->height = hitboxheight;
+	else
+		mobj->height = mobjinfo[MT_NIGHTSDRONE].height;
+
+	droneboxmandiff = max(mobj->height - mobjinfo[MT_NIGHTSDRONE_MAN].height, 0);
+	dronemangoaldiff = max(mobjinfo[MT_NIGHTSDRONE_MAN].height - mobjinfo[MT_NIGHTSDRONE_GOAL].height, 0);
 
-			if (mthing->options & MTF_AMBUSH) // Special flag for rings
-				z -= 24*FRACUNIT;
-			if (mthing->options >> ZSHIFT)
-				z -= (mthing->options >> ZSHIFT)*FRACUNIT;
+	if (flip && mobj->height != oldheight)
+		P_TeleportMove(mobj, mobj->x, mobj->y, mobj->z - (mobj->height - oldheight));
 
-			z -= mobjinfo[i].height; //Don't forget the height!
+	if (!flip)
+	{
+		if (topaligned) // Align droneman to top of hitbox
+		{
+			dronemanoffset = droneboxmandiff;
+			goaloffset = dronemangoaldiff/2 + dronemanoffset;
+		}
+		else if (middlealigned) // Align droneman to center of hitbox
+		{
+			dronemanoffset = droneboxmandiff/2;
+			goaloffset = dronemangoaldiff/2 + dronemanoffset;
+		}
+		else if (bottomoffsetted)
+		{
+			dronemanoffset = 24*FRACUNIT;
+			goaloffset = dronemangoaldiff + dronemanoffset;
 		}
 		else
 		{
-			z = (
-#ifdef ESLOPE
-			ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) :
-#endif
-			ss->sector->floorheight);
-
-			if (mthing->options & MTF_AMBUSH) // Special flag for rings
-				z += 24*FRACUNIT;
-			if (mthing->options >> ZSHIFT)
-				z += (mthing->options >> ZSHIFT)*FRACUNIT;
+			dronemanoffset = 0;
+			goaloffset = dronemangoaldiff/2 + dronemanoffset;
 		}
 
-		if (z == ONFLOORZ)
-			mthing->z = 0;
-		else
-			mthing->z = (INT16)(z>>FRACBITS);
+		sparkleoffset = goaloffset - FixedMul(15*FRACUNIT, mobj->scale);
 	}
 	else
 	{
-		fixed_t offset = 0;
-		boolean flip = (!!(mobjinfo[i].flags & MF_SPAWNCEILING) ^ !!(mthing->options & MTF_OBJECTFLIP));
-
-		// base positions
-		if (flip)
-			z = (
-#ifdef ESLOPE
-			ss->sector->c_slope ? P_GetZAt(ss->sector->c_slope, x, y) :
-#endif
-			ss->sector->ceilingheight) - mobjinfo[i].height;
-		else
-			z = (
-#ifdef ESLOPE
-			ss->sector->f_slope ? P_GetZAt(ss->sector->f_slope, x, y) :
-#endif
-			ss->sector->floorheight);
-
-		// offsetting
-		if (mthing->options >> ZSHIFT)
-			offset = ((mthing->options >> ZSHIFT) << FRACBITS);
-		else if (i == MT_CRAWLACOMMANDER || i == MT_DETON || i == MT_JETTBOMBER || i == MT_JETTGUNNER || i == MT_EGGMOBILE2)
-			offset = 33*FRACUNIT;
-		else if (i == MT_EGGMOBILE)
-			offset = 128*FRACUNIT;
-		else if (i == MT_GOLDBUZZ || i == MT_REDBUZZ)
-			offset = 288*FRACUNIT;
+		mobj->eflags |= MFE_VERTICALFLIP;
+		mobj->flags2 |= MF2_OBJECTFLIP;
 
-		// applying offsets! (if any)
-		if (flip)
+		if (topaligned) // Align droneman to top of hitbox
 		{
-			if (offset)
-				z -= offset;
-			else
-				z = ONCEILINGZ;
+			dronemanoffset = 0;
+			goaloffset = dronemangoaldiff/2 + dronemanoffset;
+		}
+		else if (middlealigned) // Align droneman to center of hitbox
+		{
+			dronemanoffset = droneboxmandiff/2;
+			goaloffset = dronemangoaldiff/2 + dronemanoffset;
+		}
+		else if (bottomoffsetted)
+		{
+			dronemanoffset = droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale);
+			goaloffset = dronemangoaldiff + dronemanoffset;
 		}
 		else
 		{
-			if (offset)
-				z += offset;
-			else
-				z = ONFLOORZ;
+			dronemanoffset = droneboxmandiff;
+			goaloffset = dronemangoaldiff/2 + dronemanoffset;
 		}
 
-		if (z == ONFLOORZ)
-			mthing->z = 0;
-		else
-			mthing->z = (INT16)(z>>FRACBITS);
+		sparkleoffset = goaloffset + FixedMul(15*FRACUNIT, mobj->scale);
 	}
 
-	mobj = P_SpawnMobj(x, y, z, i);
-	mobj->spawnpoint = mthing;
-
-#ifdef HAVE_BLUA
-	if (LUAh_MapThingSpawn(mobj, mthing))
-	{
-		if (P_MobjWasRemoved(mobj))
-			return;
-	}
-	else if (P_MobjWasRemoved(mobj))
-		return;
-	else
-#endif
-	switch(mobj->type)
+	// spawn visual elements
 	{
-	case MT_EMBLEM:
-	{
-		INT32 j;
-		emblem_t *emblem = M_GetLevelEmblems(gamemap);
-		skincolors_t emcolor;
-
-		while (emblem)
-		{
-			if ((emblem->type == ET_GLOBAL || emblem->type == ET_SKIN) && emblem->tag == mthing->angle)
-				break;
+		mobj_t* goalpost = P_SpawnMobjFromMobj(mobj, 0, 0, goaloffset, MT_NIGHTSDRONE_GOAL);
+		mobj_t* sparkle = P_SpawnMobjFromMobj(mobj, 0, 0, sparkleoffset, MT_NIGHTSDRONE_SPARKLING);
+		mobj_t* droneman = P_SpawnMobjFromMobj(mobj, 0, 0, dronemanoffset, MT_NIGHTSDRONE_MAN);
 
-			emblem = M_GetLevelEmblems(-1);
-		}
+		P_SetTarget(&mobj->target, goalpost);
+		P_SetTarget(&goalpost->target, sparkle);
+		P_SetTarget(&goalpost->tracer, droneman);
 
-		if (!emblem)
+		// correct Z position
+		if (flip)
 		{
-			CONS_Debug(DBG_GAMELOGIC, "No map emblem for map %d with tag %d found!\n", gamemap, mthing->angle);
-			break;
+			P_TeleportMove(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset);
+			P_TeleportMove(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset);
+			P_TeleportMove(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset);
 		}
 
-		j = emblem - emblemlocations;
+		// Remember position preference for later
+		mobj->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE);
+		if (topaligned)
+			mobj->flags |= MF_SLIDEME;
+		else if (middlealigned)
+			mobj->flags |= MF_GRENADEBOUNCE;
+		else if (!bottomoffsetted)
+			mobj->flags |= MF_SLIDEME|MF_GRENADEBOUNCE;
 
-		I_Assert(emblemlocations[j].sprite >= 'A' && emblemlocations[j].sprite <= 'Z');
-		P_SetMobjState(mobj, mobj->info->spawnstate + (emblemlocations[j].sprite - 'A'));
+		// 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);
+	}
+	return true;
+}
 
-		mobj->health = j + 1;
-		emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
-		mobj->color = (UINT8)emcolor;
+static boolean P_SetupBooster(mapthing_t* mthing, mobj_t* mobj, boolean strong)
+{
+	angle_t angle = FixedAngle(mthing->angle << FRACBITS);
+	fixed_t x1 = FINECOSINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
+	fixed_t y1 = FINESINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
+	fixed_t x2 = FINECOSINE(((angle + ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
+	fixed_t y2 = FINESINE(((angle + ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
+	statenum_t facestate = strong ? S_REDBOOSTERSEG_FACE : S_YELLOWBOOSTERSEG_FACE;
+	statenum_t leftstate = strong ? S_REDBOOSTERSEG_LEFT : S_YELLOWBOOSTERSEG_LEFT;
+	statenum_t rightstate = strong ? S_REDBOOSTERSEG_RIGHT : S_YELLOWBOOSTERSEG_RIGHT;
+	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);
+	seg = P_SpawnMobjFromMobj(mobj, -26*x1, -26*y1, 0, MT_BOOSTERSEG);
+	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);
+	seg = P_SpawnMobjFromMobj(mobj, -21*x2, -21*y2, 0, MT_BOOSTERSEG);
+	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);
+	seg = P_SpawnMobjFromMobj(mobj, 13*(x1 - x2), 13*(y1 - y2), 0, MT_BOOSTERROLLER);
+	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);
+	seg = P_SpawnMobjFromMobj(mobj, -13*(x1 - x2), -13*(y1 - y2), 0, MT_BOOSTERROLLER);
+	seg->angle = angle;
+	P_SetMobjState(seg, rollerstate);
 
-		if (emblemlocations[j].collected
-			|| (emblemlocations[j].type == ET_SKIN && emblemlocations[j].var != players[0].skin))
-		{
-			P_UnsetThingPosition(mobj);
-			mobj->flags |= MF_NOCLIP;
-			mobj->flags &= ~MF_SPECIAL;
-			mobj->flags |= MF_NOBLOCKMAP;
-			mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
-			P_SetThingPosition(mobj);
-		}
-		else
-		{
-			mobj->frame &= ~FF_TRANSMASK;
+	return true;
+}
 
-			if (emblemlocations[j].type == ET_GLOBAL)
-			{
-				mobj->reactiontime = emblemlocations[j].var;
-				if (emblemlocations[j].var & GE_NIGHTSITEM)
-				{
-					mobj->flags |= MF_NIGHTSITEM;
-					mobj->flags &= ~MF_SPECIAL;
-					mobj->flags2 |= MF2_DONTDRAW;
-				}
-			}
-		}
+static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean *doangle)
+{
+#ifdef HAVE_BLUA
+	boolean override = LUAh_MapThingSpawn(mobj, mthing);
+
+	if (P_MobjWasRemoved(mobj))
+		return false;
+
+	if (override)
+		return true;
+#endif
+
+	switch (mobj->type)
+	{
+	case MT_EMBLEM:
+	{
+		if (!P_SetupEmblem(mthing, mobj))
+			return false;
 		break;
 	}
 	case MT_SKYBOX:
@@ -11960,7 +12629,7 @@ You should think about modifying the deathmatch starts to take full advantage of
 			skyboxviewpnts[mthing->extrainfo] = mobj;
 		break;
 	case MT_EGGSTATUE:
-		if (tutorialmode != (mthing->options & MTF_OBJECTSPECIAL))
+		if (mthing->options & MTF_EXTRA)
 		{
 			mobj->color = SKINCOLOR_GOLD;
 			mobj->colorized = true;
@@ -11984,7 +12653,7 @@ You should think about modifying the deathmatch starts to take full advantage of
 		if (mthing->angle)
 			mobj->health = mthing->angle;
 		else
-			mobj->health = FixedMul(ss->sector->ceilingheight-ss->sector->floorheight, 3*(FRACUNIT/4))>>FRACBITS;
+			mobj->health = FixedMul(mobj->subsector->sector->ceilingheight - mobj->subsector->sector->floorheight, 3*(FRACUNIT/4)) >> FRACBITS;
 		break;
 	case MT_METALSONIC_RACE:
 	case MT_METALSONIC_BATTLE:
@@ -11999,7 +12668,7 @@ You should think about modifying the deathmatch starts to take full advantage of
 		break;
 	case MT_BALLOON:
 		if (mthing->angle > 0)
-			mobj->color = ((mthing->angle-1) % (MAXSKINCOLORS-1))+1;
+			mobj->color = ((mthing->angle - 1) % (MAXSKINCOLORS - 1)) + 1;
 		break;
 #define makesoftwarecorona(mo, h) \
 			corona = P_SpawnMobjFromMobj(mo, 0, 0, h<<FRACBITS, MT_PARTICLE);\
@@ -12020,442 +12689,61 @@ You should think about modifying the deathmatch starts to take full advantage of
 		{
 			mobj_t *flame = P_SpawnMobjFromMobj(mobj, 0, 0, mobj->height, MT_FLAME);
 			P_SetTarget(&flame->target, mobj);
-			flame->flags2 |= MF2_BOSSNOTRAP;
-			if (mthing->options & MTF_EXTRA)
-			{
-				mobj_t *corona;
-				makesoftwarecorona(flame, 20);
-				P_SetScale(corona, (corona->destscale = flame->scale*3));
-				P_SetTarget(&flame->tracer, corona);
-			}
-		}
-		break;
-	case MT_CANDLE:
-	case MT_CANDLEPRICKET:
-		if (mthing->options & MTF_EXTRA)
-		{
-			mobj_t *corona;
-			makesoftwarecorona(mobj, ((mobj->type == MT_CANDLE) ? 42 : 176));
-		}
-		break;
-#undef makesoftwarecorona
-	case MT_JACKO1:
-	case MT_JACKO2:
-	case MT_JACKO3:
-		if (!(mthing->options & MTF_EXTRA)) // take the torch out of the crafting recipe
-		{
-			mobj_t *overlay = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY);
-			P_SetTarget(&overlay->target, mobj);
-			P_SetMobjState(overlay, mobj->info->raisestate);
-		}
-		break;
-	case MT_WATERDRIP:
-		if (mthing->angle)
-			mobj->tics = 3*TICRATE + mthing->angle;
-		else
-			mobj->tics = 3*TICRATE;
-		break;
-	case MT_FLAMEJET:
-	case MT_VERTICALFLAMEJET:
-		mobj->threshold = (mthing->angle >> 10) & 7;
-		mobj->movecount = (mthing->angle >> 13);
-
-		mobj->threshold *= (TICRATE/2);
-		mobj->movecount *= (TICRATE/2);
-
-		mobj->movedir = mthing->extrainfo;
-		break;
-	case MT_MACEPOINT:
-	case MT_CHAINMACEPOINT:
-	case MT_SPRINGBALLPOINT:
-	case MT_CHAINPOINT:
-	case MT_FIREBARPOINT:
-	case MT_CUSTOMMACEPOINT:
-	{
-		fixed_t mlength, mmaxlength, mlengthset, mspeed, mphase, myaw, mpitch, mminlength, mnumspokes, mpinch, mroll, mnumnospokes, mwidth, mwidthset, mmin, msound, radiusfactor, widthfactor;
-		angle_t mspokeangle;
-		mobjtype_t chainlink, macetype, firsttype, linktype;
-		boolean mdosound, mdocenter, mchainlike = false;
-		mobj_t *spawnee = NULL, *hprev = mobj;
-		mobjflag_t mflagsapply;
-		mobjflag2_t mflags2apply;
-		mobjeflag_t meflagsapply;
-		INT32 line;
-		const size_t mthingi = (size_t)(mthing - mapthings);
-
-		// Find the corresponding linedef special, using angle as tag
-		// P_FindSpecialLineFromTag works here now =D
-		line = P_FindSpecialLineFromTag(9, mthing->angle, -1);
-
-		if (line == -1)
-		{
-			CONS_Debug(DBG_GAMELOGIC, "Mace chain (mapthing #%s) needs to be tagged to a #9 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle);
-			return;
-		}
-/*
-mapthing -
-MTF_AMBUSH :
-	MT_SPRINGBALLPOINT - upgrade from yellow to red spring
-	anything else - bigger mace/chain theory
-MTF_OBJECTSPECIAL - force silent
-MTF_GRAVFLIP - flips objects, doesn't affect chain arrangements
-Parameter value : number of "spokes"
-
-linedef -
-ML_NOCLIMB :
-	MT_CHAINPOINT/MT_CHAINMACEPOINT with ML_EFFECT1 applied - Direction not controllable
-	anything else - no functionality
-ML_EFFECT1 : Swings instead of spins
-ML_EFFECT2 : Linktype is replaced with macetype for all spokes not ending in chains (inverted for MT_FIREBARPOINT)
-ML_EFFECT3 : Spawn a bonus linktype at the hinge point
-ML_EFFECT4 : Don't clip inside the ground
-ML_EFFECT5 : Don't stop thinking when too far away
-*/
-		mlength = abs(lines[line].dx >> FRACBITS);
-		mspeed = abs(lines[line].dy >> (FRACBITS - 4));
-		mphase = (sides[lines[line].sidenum[0]].textureoffset >> FRACBITS) % 360;
-		if ((mminlength = -sides[lines[line].sidenum[0]].rowoffset>>FRACBITS) < 0)
-			mminlength = 0;
-		else if (mminlength > mlength-1)
-			mminlength = mlength-1;
-		mpitch = (lines[line].frontsector->floorheight >> FRACBITS) % 360;
-		myaw = (lines[line].frontsector->ceilingheight >> FRACBITS) % 360;
-
-		mnumspokes = mthing->extrainfo + 1;
-		mspokeangle = FixedAngle((360*FRACUNIT)/mnumspokes)>>ANGLETOFINESHIFT;
-
-		if (lines[line].backsector)
-		{
-			mpinch = (lines[line].backsector->floorheight >> FRACBITS) % 360;
-			mroll = (lines[line].backsector->ceilingheight >> FRACBITS) % 360;
-			mnumnospokes = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS);
-			if ((mwidth = sides[lines[line].sidenum[1]].rowoffset >> FRACBITS) < 0)
-				mwidth = 0;
-		}
-		else
-			mpinch = mroll = mnumnospokes = mwidth = 0;
-
-		CONS_Debug(DBG_GAMELOGIC, "Mace/Chain (mapthing #%s):\n"
-				"Length is %d (minus %d)\n"
-				"Speed is %d\n"
-				"Phase is %d\n"
-				"Yaw is %d\n"
-				"Pitch is %d\n"
-				"No. of spokes is %d (%d antispokes)\n"
-				"Pinch is %d\n"
-				"Roll is %d\n"
-				"Width is %d\n",
-				sizeu1(mthingi), mlength, mminlength, mspeed, mphase, myaw, mpitch, mnumspokes, mnumnospokes, mpinch, mroll, mwidth);
-
-		if (mnumnospokes > 0 && (mnumnospokes < mnumspokes))
-			mnumnospokes = mnumspokes/mnumnospokes;
-		else
-			mnumnospokes = ((mobj->type == MT_CHAINMACEPOINT) ? (mnumspokes) : 0);
-
-		mobj->lastlook = mspeed;
-		mobj->movecount = mobj->lastlook;
-		mobj->angle = FixedAngle(myaw*FRACUNIT);
-		doangle = false;
-		mobj->threshold = (FixedAngle(mpitch*FRACUNIT)>>ANGLETOFINESHIFT);
-		mobj->movefactor = mpinch;
-		mobj->movedir = 0;
-
-		// Mobjtype selection
-		switch(mobj->type)
-		{
-			case MT_SPRINGBALLPOINT:
-				macetype = ((mthing->options & MTF_AMBUSH)
-					? MT_REDSPRINGBALL
-					: MT_YELLOWSPRINGBALL);
-				chainlink = MT_SMALLMACECHAIN;
-				break;
-			case MT_FIREBARPOINT:
-				macetype = ((mthing->options & MTF_AMBUSH)
-						? MT_BIGFIREBAR
-						: MT_SMALLFIREBAR);
-				chainlink = MT_NULL;
-				break;
-			case MT_CUSTOMMACEPOINT:
-				macetype = (mobjtype_t)sides[lines[line].sidenum[0]].toptexture;
-				if (lines[line].backsector)
-					chainlink = (mobjtype_t)sides[lines[line].sidenum[1]].toptexture;
-				else
-					chainlink = MT_NULL;
-				break;
-			case MT_CHAINPOINT:
-				if (mthing->options & MTF_AMBUSH)
-				{
-					macetype = MT_BIGGRABCHAIN;
-					chainlink = MT_BIGMACECHAIN;
-				}
-				else
-				{
-					macetype = MT_SMALLGRABCHAIN;
-					chainlink = MT_SMALLMACECHAIN;
-				}
-				mchainlike = true;
-				break;
-			default:
-				if (mthing->options & MTF_AMBUSH)
-				{
-					macetype = MT_BIGMACE;
-					chainlink = MT_BIGMACECHAIN;
-				}
-				else
-				{
-					macetype = MT_SMALLMACE;
-					chainlink = MT_SMALLMACECHAIN;
-				}
-				break;
-		}
-
-		if (!macetype && !chainlink)
-			break;
-
-		if (mobj->type == MT_CHAINPOINT)
-		{
-			if (!mlength)
-				break;
-		}
-		else
-			mlength++;
-
-		firsttype = macetype;
-
-		// Adjustable direction
-		if (lines[line].flags & ML_NOCLIMB)
-			mobj->flags |= MF_SLIDEME;
-
-		// Swinging
-		if (lines[line].flags & ML_EFFECT1)
-		{
-			mobj->flags2 |= MF2_STRONGBOX;
-			mmin = ((mnumnospokes > 1) ? 1 : 0);
-		}
-		else
-			mmin = mnumspokes;
-
-		// If over distance away, don't move UNLESS this flag is applied
-		if (lines[line].flags & ML_EFFECT5)
-			mobj->flags2 |= MF2_BOSSNOTRAP;
-
-		// Make the links the same type as the end - repeated below
-		if ((mobj->type != MT_CHAINPOINT) && (((lines[line].flags & ML_EFFECT2) == ML_EFFECT2) != (mobj->type == MT_FIREBARPOINT))) // exclusive or
-		{
-			linktype = macetype;
-			radiusfactor = 2; // Double the radius.
-		}
-		else
-			radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
-
-		if (!mchainlike)
-			mchainlike = (firsttype == chainlink);
-		widthfactor = (mchainlike ? 1 : 2);
-
-		mflagsapply = ((lines[line].flags & ML_EFFECT4) ? 0 : (MF_NOCLIP|MF_NOCLIPHEIGHT));
-		mflags2apply = ((mthing->options & MTF_OBJECTFLIP) ? MF2_OBJECTFLIP : 0);
-		meflagsapply = ((mthing->options & MTF_OBJECTFLIP) ? MFE_VERTICALFLIP : 0);
-
-		msound = (mchainlike ? 0 : (mwidth & 1));
-
-		// Quick and easy preparatory variable setting
-		mphase = (FixedAngle(mphase*FRACUNIT)>>ANGLETOFINESHIFT);
-		mroll = (FixedAngle(mroll*FRACUNIT)>>ANGLETOFINESHIFT);
-
-#define makemace(mobjtype, dist, moreflags2) {\
-	spawnee = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobjtype);\
-	P_SetTarget(&spawnee->tracer, mobj);\
-	spawnee->threshold = mphase;\
-	spawnee->friction = mroll;\
-	spawnee->movefactor = mwidthset;\
-	spawnee->movecount = dist;\
-	spawnee->angle = myaw;\
-	spawnee->flags |= (MF_NOGRAVITY|mflagsapply);\
-	spawnee->flags2 |= (mflags2apply|moreflags2);\
-	spawnee->eflags |= meflagsapply;\
-	P_SetTarget(&hprev->hnext, spawnee);\
-	P_SetTarget(&spawnee->hprev, hprev);\
-	hprev = spawnee;\
-}
-
-		mdosound = (mspeed && !(mthing->options & MTF_OBJECTSPECIAL));
-		mdocenter = (macetype && (lines[line].flags & ML_EFFECT3));
-
-		// The actual spawning of spokes
-		while (mnumspokes-- > 0)
-		{
-			// Offsets
-			if (lines[line].flags & ML_EFFECT1) // Swinging
-				mroll = (mroll - mspokeangle) & FINEMASK;
-			else // Spinning
-				mphase = (mphase - mspokeangle) & FINEMASK;
-
-			if (mnumnospokes && !(mnumspokes % mnumnospokes)) // Skipping a "missing" spoke
-			{
-				if (mobj->type != MT_CHAINMACEPOINT)
-					continue;
-
-				linktype = chainlink;
-				firsttype = ((mthing->options & MTF_AMBUSH) ? MT_BIGGRABCHAIN : MT_SMALLGRABCHAIN);
-				mmaxlength = 1 + (mlength - 1)*radiusfactor;
-				radiusfactor = widthfactor = 1;
-			}
-			else
-			{
-				if (mobj->type == MT_CHAINMACEPOINT)
-				{
-					// Make the links the same type as the end - repeated above
-					if (lines[line].flags & ML_EFFECT2)
-					{
-						linktype = macetype;
-						radiusfactor = 2;
-					}
-					else
-						radiusfactor = (((linktype = chainlink) == MT_NULL) ? 2 : 1);
-
-					firsttype = macetype;
-					widthfactor = 2;
-				}
-
-				mmaxlength = mlength;
-			}
-
-			mwidthset = mwidth;
-			mlengthset = mminlength;
-
-			if (mdocenter) // Innermost link
-				makemace(linktype, 0, 0);
-
-			// Out from the center...
-			if (linktype)
-			{
-				while ((++mlengthset) < mmaxlength)
-					makemace(linktype, radiusfactor*mlengthset, 0);
-			}
-			else
-				mlengthset = mmaxlength;
-
-			// Outermost mace/link
-			if (firsttype)
-				makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH);
-
-			if (!mwidth)
-			{
-				if (mdosound && mnumspokes <= mmin) // Can it make a sound?
-					spawnee->flags2 |= MF2_BOSSNOTRAP;
-			}
-			else
-			{
-				// Across the bar!
-				if (!firsttype)
-					mwidthset = -mwidth;
-				else if (mwidth > 0)
-				{
-					while ((mwidthset -= widthfactor) > -mwidth)
-					{
-						makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH);
-						if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound?
-							spawnee->flags2 |= MF2_BOSSNOTRAP;
-					}
-				}
-				else
-				{
-					while ((mwidthset += widthfactor) < -mwidth)
-					{
-						makemace(firsttype, radiusfactor*mlengthset, MF2_AMBUSH);
-						if (mdosound && (mwidthset == msound) && mnumspokes <= mmin) // Can it make a sound?
-							spawnee->flags2 |= MF2_BOSSNOTRAP;
-					}
-				}
-				mwidth = -mwidth;
-
-				// Outermost mace/link again!
-				if (firsttype)
-					makemace(firsttype, radiusfactor*(mlengthset--), MF2_AMBUSH);
-
-				// ...and then back into the center!
-				if (linktype)
-					while (mlengthset > mminlength)
-						makemace(linktype, radiusfactor*(mlengthset--), 0);
-
-				if (mdocenter) // Innermost link
-					makemace(linktype, 0, 0);
-			}
-		}
-
-#undef makemace
-
-		break;
-	}
-	case MT_PARTICLEGEN:
-	{
-		fixed_t radius, speed;
-		INT32 type, numdivisions, anglespeed, ticcount;
-		angle_t angledivision;
-		INT32 line;
-		const size_t mthingi = (size_t)(mthing - mapthings);
-
-		// Find the corresponding linedef special, using angle as tag
-		line = P_FindSpecialLineFromTag(15, mthing->angle, -1);
-
-		if (line == -1)
-		{
-			CONS_Debug(DBG_GAMELOGIC, "Particle generator (mapthing #%s) needs to be tagged to a #15 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle);
-			return;
+			flame->flags2 |= MF2_BOSSNOTRAP;
+			if (mthing->options & MTF_EXTRA)
+			{
+				mobj_t *corona;
+				makesoftwarecorona(flame, 20);
+				P_SetScale(corona, (corona->destscale = flame->scale*3));
+				P_SetTarget(&flame->tracer, corona);
+			}
 		}
-
-		if (sides[lines[line].sidenum[0]].toptexture)
-			type = sides[lines[line].sidenum[0]].toptexture; // Set as object type in p_setup.c...
-		else
-			type = (INT32)MT_PARTICLE;
-
-		if (!lines[line].backsector
-		|| (ticcount = (sides[lines[line].sidenum[1]].textureoffset >> FRACBITS)) < 1)
-			ticcount = 3;
-
-		numdivisions = (mthing->options >> ZSHIFT);
-
-		if (numdivisions)
+		break;
+	case MT_CANDLE:
+	case MT_CANDLEPRICKET:
+		if (mthing->options & MTF_EXTRA)
 		{
-			radius = R_PointToDist2(lines[line].v1->x, lines[line].v1->y, lines[line].v2->x, lines[line].v2->y);
-			anglespeed = (sides[lines[line].sidenum[0]].rowoffset >> FRACBITS) % 360;
-			angledivision = 360/numdivisions;
+			mobj_t *corona;
+			makesoftwarecorona(mobj, ((mobj->type == MT_CANDLE) ? 42 : 176));
 		}
-		else
+		break;
+#undef makesoftwarecorona
+	case MT_JACKO1:
+	case MT_JACKO2:
+	case MT_JACKO3:
+		if (!(mthing->options & MTF_EXTRA)) // take the torch out of the crafting recipe
 		{
-			numdivisions = 1; // Simple trick to make A_ParticleSpawn simpler.
-			radius = 0;
-			anglespeed = 0;
-			angledivision = 0;
+			mobj_t *overlay = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_OVERLAY);
+			P_SetTarget(&overlay->target, mobj);
+			P_SetMobjState(overlay, mobj->info->raisestate);
 		}
+		break;
+	case MT_WATERDRIP:
+		mobj->tics = 3*TICRATE + mthing->angle;
+		break;
+	case MT_FLAMEJET:
+	case MT_VERTICALFLAMEJET:
+		mobj->threshold = (mthing->angle >> 10) & 7;
+		mobj->movecount = (mthing->angle >> 13);
 
-		speed = abs(sides[lines[line].sidenum[0]].textureoffset);
-		if (mthing->options & MTF_OBJECTFLIP)
-			speed *= -1;
-
-		CONS_Debug(DBG_GAMELOGIC, "Particle Generator (mapthing #%s):\n"
-				"Radius is %d\n"
-				"Speed is %d\n"
-				"Anglespeed is %d\n"
-				"Numdivisions is %d\n"
-				"Angledivision is %d\n"
-				"Type is %d\n"
-				"Tic seperation is %d\n",
-				sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, type, ticcount);
-
-		mobj->angle = 0;
-		mobj->movefactor = speed;
-		mobj->lastlook = numdivisions;
-		mobj->movedir = angledivision*ANG1;
-		mobj->movecount = anglespeed*ANG1;
-		mobj->friction = radius;
-		mobj->threshold = type;
-		mobj->reactiontime = ticcount;
-		mobj->cvmem = line;
-		mobj->watertop = mobj->waterbottom = 0;
+		mobj->threshold *= (TICRATE/2);
+		mobj->movecount *= (TICRATE/2);
 
+		mobj->movedir = mthing->extrainfo;
+		break;
+	case MT_MACEPOINT:
+	case MT_CHAINMACEPOINT:
+	case MT_SPRINGBALLPOINT:
+	case MT_CHAINPOINT:
+	case MT_FIREBARPOINT:
+	case MT_CUSTOMMACEPOINT:
+		if (!P_SetupMace(mthing, mobj, doangle))
+			return false;
+		break;
+	case MT_PARTICLEGEN:
+		if (!P_SetupParticleGen(mthing, mobj))
+			return false;
 		break;
-	}
 	case MT_ROCKSPAWNER:
 		mobj->threshold = mthing->angle;
 		mobj->movecount = mthing->extrainfo;
@@ -12470,7 +12758,7 @@ ML_EFFECT5 : Don't stop thinking when too far away
 		// Lower 4 bits specify the angle of
 		// the bumper in 30 degree increments.
 		mobj->threshold = (mthing->options & 15) % 12; // It loops over, etc
-		P_SetMobjState(mobj, mobj->info->spawnstate+mobj->threshold);
+		P_SetMobjState(mobj, mobj->info->spawnstate + mobj->threshold);
 		break;
 	case MT_EGGCAPSULE:
 		if (mthing->angle <= 0)
@@ -12487,122 +12775,8 @@ ML_EFFECT5 : Don't stop thinking when too far away
 		mobj->health = mthing->extrainfo;
 		break;
 	case MT_NIGHTSDRONE:
-		{
-			boolean flip = mthing->options & MTF_OBJECTFLIP;
-			boolean topaligned = (mthing->options & MTF_OBJECTSPECIAL) && !(mthing->options & MTF_EXTRA);
-			boolean middlealigned = (mthing->options & MTF_EXTRA) && !(mthing->options & MTF_OBJECTSPECIAL);
-			boolean bottomoffsetted = !(mthing->options & MTF_OBJECTSPECIAL) && !(mthing->options & MTF_EXTRA);
-
-			INT16 timelimit = mthing->angle & 0xFFF;
-			fixed_t hitboxradius = ((mthing->angle & 0xF000) >> 12) * 32 * FRACUNIT;
-			fixed_t hitboxheight = mthing->extrainfo * 32 * FRACUNIT;
-			fixed_t oldheight = mobj->height;
-			fixed_t dronemanoffset, goaloffset, sparkleoffset, droneboxmandiff, dronemangoaldiff;
-
-			if (timelimit > 0)
-				mobj->health = timelimit;
-
-			if (hitboxradius > 0)
-				mobj->radius = hitboxradius;
-
-			if (hitboxheight > 0)
-				mobj->height = hitboxheight;
-			else
-				mobj->height = mobjinfo[MT_NIGHTSDRONE].height;
-
-			droneboxmandiff = max(mobj->height - mobjinfo[MT_NIGHTSDRONE_MAN].height, 0);
-			dronemangoaldiff = max(mobjinfo[MT_NIGHTSDRONE_MAN].height - mobjinfo[MT_NIGHTSDRONE_GOAL].height, 0);
-
-			if (flip && mobj->height != oldheight)
-				P_TeleportMove(mobj, mobj->x, mobj->y, mobj->z - (mobj->height - oldheight));
-
-			if (!flip)
-			{
-				if (topaligned) // Align droneman to top of hitbox
-				{
-					dronemanoffset = droneboxmandiff;
-					goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-				}
-				else if (middlealigned) // Align droneman to center of hitbox
-				{
-					dronemanoffset = droneboxmandiff / 2;
-					goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-				}
-				else if (bottomoffsetted)
-				{
-					dronemanoffset = 24*FRACUNIT;
-					goaloffset = dronemangoaldiff + dronemanoffset;
-				}
-				else
-				{
-					dronemanoffset = 0;
-					goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-				}
-
-				sparkleoffset = goaloffset - FixedMul(15*FRACUNIT, mobj->scale);
-			}
-			else
-			{
-				mobj->eflags |= MFE_VERTICALFLIP;
-				mobj->flags2 |= MF2_OBJECTFLIP;
-
-				if (topaligned) // Align droneman to top of hitbox
-				{
-					dronemanoffset = 0;
-					goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-				}
-				else if (middlealigned) // Align droneman to center of hitbox
-				{
-					dronemanoffset = droneboxmandiff / 2;
-					goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-				}
-				else if (bottomoffsetted)
-				{
-					dronemanoffset = droneboxmandiff - FixedMul(24*FRACUNIT, mobj->scale);
-					goaloffset = dronemangoaldiff + dronemanoffset;
-				}
-				else
-				{
-					dronemanoffset = droneboxmandiff;
-					goaloffset = dronemangoaldiff / 2 + dronemanoffset;
-				}
-
-				sparkleoffset = goaloffset + FixedMul(15*FRACUNIT, mobj->scale);
-			}
-
-			// spawn visual elements
-			{
-				mobj_t *goalpost = P_SpawnMobjFromMobj(mobj, 0, 0, goaloffset, MT_NIGHTSDRONE_GOAL);
-				mobj_t *sparkle = P_SpawnMobjFromMobj(mobj, 0, 0, sparkleoffset, MT_NIGHTSDRONE_SPARKLING);
-				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);
-
-				// correct Z position
-				if (flip)
-				{
-					P_TeleportMove(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset);
-					P_TeleportMove(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset);
-					P_TeleportMove(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset);
-				}
-
-				// Remember position preference for later
-				mobj->flags &= ~(MF_SLIDEME | MF_GRENADEBOUNCE);
-				if (topaligned)
-					mobj->flags |= MF_SLIDEME;
-				else if (middlealigned)
-					mobj->flags |= MF_GRENADEBOUNCE;
-				else if (!bottomoffsetted)
-					mobj->flags |= MF_SLIDEME | MF_GRENADEBOUNCE;
-
-				// 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_SetupNiGHTSDrone(mthing, mobj))
+			return false;
 		break;
 	case MT_HIVEELEMENTAL:
 		if (mthing->extrainfo)
@@ -12613,7 +12787,7 @@ ML_EFFECT5 : Don't stop thinking when too far away
 	case MT_GLAREGOYLEDOWN:
 	case MT_GLAREGOYLELONG:
 		if (mthing->angle >= 360)
-			mobj->tics += 7*(mthing->angle / 360) + 1; // starting delay
+			mobj->tics += 7*(mthing->angle/360) + 1; // starting delay
 		break;
 	case MT_DSZSTALAGMITE:
 	case MT_DSZ2STALAGMITE:
@@ -12624,39 +12798,39 @@ ML_EFFECT5 : Don't stop thinking when too far away
 		}
 		break;
 	case MT_THZTREE:
-		{ // Spawn the branches
-			angle_t mobjangle = FixedAngle((mthing->angle % 113)<<FRACBITS);
-			P_SpawnMobjFromMobj(mobj, 1*FRACUNIT,  0,          0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_22h;
-			P_SpawnMobjFromMobj(mobj, 0,           1*FRACUNIT, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_157h;
-			P_SpawnMobjFromMobj(mobj, -1*FRACUNIT, 0,          0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_270;
-		}
-		break;
+	{ // 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;
+	}
+	break;
 	case MT_CEZPOLE1:
 	case MT_CEZPOLE2:
-		{ // Spawn the banner
-			angle_t mobjangle = FixedAngle(mthing->angle<<FRACBITS);
-			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;
-		}
-			break;
+	{ // Spawn the banner
+		angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS);
+		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;
+	}
+	break;
 	case MT_HHZTREE_TOP:
-		{ // Spawn the branches
-			angle_t mobjangle = FixedAngle(mthing->angle<<FRACBITS) & (ANGLE_90-1);
-			mobj_t *leaf;
+	{ // Spawn the branches
+		angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS) & (ANGLE_90 - 1);
+		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);\
 			mobjangle += ANGLE_90
-			doleaf(1*FRACUNIT, 0);
-			doleaf(0, 1*FRACUNIT);
-			doleaf(-1*FRACUNIT, 0);
-			doleaf(0, -1*FRACUNIT);
+		doleaf(FRACUNIT, 0);
+		doleaf(0, FRACUNIT);
+		doleaf(-FRACUNIT, 0);
+		doleaf(0, -FRACUNIT);
 #undef doleaf
-		}
-		break;
+	}
+	break;
 	case MT_SMASHINGSPIKEBALL:
 		if (mthing->angle > 0)
 			mobj->tics += mthing->angle;
@@ -12687,94 +12861,27 @@ ML_EFFECT5 : Don't stop thinking when too far away
 			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);
+			mobj_t* leaf = P_SpawnMobjFromMobj(mobj, xoffs, yoffs, 0, MT_BIGFERNLEAF);
 			leaf->angle = angle;
 			angle += ANGLE_45;
 		}
 		break;
 	}
 	case MT_REDBOOSTER:
-	{
-		angle_t angle = FixedAngle(mthing->angle << FRACBITS);
-		fixed_t x1 = FINECOSINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
-		fixed_t y1 = FINESINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
-		fixed_t x2 = FINECOSINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
-		fixed_t y2 = FINESINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
-
-		mobj_t *seg = P_SpawnMobjFromMobj(mobj, 26*x1, 26*y1, 0, MT_BOOSTERSEG);
-		seg->angle = angle-ANGLE_90;
-		P_SetMobjState(seg, S_REDBOOSTERSEG_FACE);
-		seg = P_SpawnMobjFromMobj(mobj, -26*x1, -26*y1, 0, MT_BOOSTERSEG);
-		seg->angle = angle+ANGLE_90;
-		P_SetMobjState(seg, S_REDBOOSTERSEG_FACE);
-		seg = P_SpawnMobjFromMobj(mobj, 21*x2, 21*y2, 0, MT_BOOSTERSEG);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_REDBOOSTERSEG_LEFT);
-		seg = P_SpawnMobjFromMobj(mobj, -21*x2, -21*y2, 0, MT_BOOSTERSEG);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_REDBOOSTERSEG_RIGHT);
-
-		seg = P_SpawnMobjFromMobj(mobj, 13*(x1+x2), 13*(y1+y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_REDBOOSTERROLLER);
-		seg = P_SpawnMobjFromMobj(mobj, 13*(x1-x2), 13*(y1-y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_REDBOOSTERROLLER);
-		seg = P_SpawnMobjFromMobj(mobj, -13*(x1+x2), -13*(y1+y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_REDBOOSTERROLLER);
-		seg = P_SpawnMobjFromMobj(mobj, -13*(x1-x2), -13*(y1-y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_REDBOOSTERROLLER);
-		break;
-	}
 	case MT_YELLOWBOOSTER:
-	{
-		angle_t angle = FixedAngle(mthing->angle << FRACBITS);
-		fixed_t x1 = FINECOSINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
-		fixed_t y1 = FINESINE((angle >> ANGLETOFINESHIFT) & FINEMASK);
-		fixed_t x2 = FINECOSINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
-		fixed_t y2 = FINESINE(((angle+ANGLE_90) >> ANGLETOFINESHIFT) & FINEMASK);
-
-		mobj_t *seg = P_SpawnMobjFromMobj(mobj, 26*x1, 26*y1, 0, MT_BOOSTERSEG);
-		seg->angle = angle-ANGLE_90;
-		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_FACE);
-		seg = P_SpawnMobjFromMobj(mobj, -26*x1, -26*y1, 0, MT_BOOSTERSEG);
-		seg->angle = angle+ANGLE_90;
-		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_FACE);
-		seg = P_SpawnMobjFromMobj(mobj, 21*x2, 21*y2, 0, MT_BOOSTERSEG);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_LEFT);
-		seg = P_SpawnMobjFromMobj(mobj, -21*x2, -21*y2, 0, MT_BOOSTERSEG);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_YELLOWBOOSTERSEG_RIGHT);
-
-		seg = P_SpawnMobjFromMobj(mobj, 13*(x1+x2), 13*(y1+y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
-		seg = P_SpawnMobjFromMobj(mobj, 13*(x1-x2), 13*(y1-y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
-		seg = P_SpawnMobjFromMobj(mobj, -13*(x1+x2), -13*(y1+y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
-		seg = P_SpawnMobjFromMobj(mobj, -13*(x1-x2), -13*(y1-y2), 0, MT_BOOSTERROLLER);
-		seg->angle = angle;
-		P_SetMobjState(seg, S_YELLOWBOOSTERROLLER);
-		break;
-	}
-	default:
+		if (!P_SetupBooster(mthing, mobj, mobj->type == MT_REDBOOSTER))
+			return false;
 		break;
-	}
-
-	if (mobj->flags & MF_BOSS)
-	{
-		if (mthing->options & MTF_OBJECTSPECIAL) // No egg trap for this boss
-			mobj->flags2 |= MF2_BOSSNOTRAP;
-	}
+	case MT_AXIS:
+		// Inverted if uppermost bit is set
+		if (mthing->angle & 16384)
+			mobj->flags2 |= MF2_AMBUSH;
 
-	if (i == MT_AXIS || i == MT_AXISTRANSFER || i == MT_AXISTRANSFERLINE) // Axis Points
-	{
+		if (mthing->angle > 0)
+			mobj->radius = (mthing->angle & 16383) << FRACBITS;
+		// FALLTHRU
+	case MT_AXISTRANSFER:
+	case MT_AXISTRANSFERLINE:
 		// Mare it belongs to
 		mobj->threshold = min(mthing->extrainfo, 7);
 
@@ -12782,38 +12889,29 @@ ML_EFFECT5 : Don't stop thinking when too far away
 		mobj->health = mthing->options;
 
 		mobj->flags2 |= MF2_AXIS;
-
-		if (i == MT_AXIS)
-		{
-			// Inverted if uppermost bit is set
-			if (mthing->angle & 16384)
-				mobj->flags2 |= MF2_AMBUSH;
-
-			if (mthing->angle > 0)
-				mobj->radius = (mthing->angle & 16383)*FRACUNIT;
-		}
-	}
-	else if (i == MT_TOKEN)
-	{
+		break;
+	case MT_TOKEN:
 		// We advanced tokenbits earlier due to the return check.
 		// Subtract 1 here for the correct value.
 		mobj->health = 1 << (tokenbits - 1);
-	}
-	else if (i == MT_CYBRAKDEMON && mthing->options & MTF_AMBUSH)
-	{
-		mobj_t *elecmobj;
-		elecmobj = P_SpawnMobj(x, y, z, MT_CYBRAKDEMON_ELECTRIC_BARRIER);
-		P_SetTarget(&elecmobj->target, mobj);
-		elecmobj->angle = FixedAngle(mthing->angle<<FRACBITS);;
-		elecmobj->destscale = mobj->scale*2;
-		P_SetScale(elecmobj, elecmobj->destscale);
-	}
-	else if (i == MT_STARPOST)
+		break;
+	case MT_CYBRAKDEMON:
+		if (mthing->options & MTF_AMBUSH)
+		{
+			mobj_t* elecmobj;
+			elecmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_CYBRAKDEMON_ELECTRIC_BARRIER);
+			P_SetTarget(&elecmobj->target, mobj);
+			elecmobj->angle = FixedAngle(mthing->angle << FRACBITS);
+			elecmobj->destscale = mobj->scale*2;
+			P_SetScale(elecmobj, elecmobj->destscale);
+		}
+		break;
+	case MT_STARPOST:
 	{
-		thinker_t *th;
-		mobj_t *mo2;
+		thinker_t* th;
+		mobj_t* mo2;
 		boolean foundanother = false;
-		mobj->health = (mthing->angle / 360) + 1;
+		mobj->health = (mthing->angle/360) + 1;
 
 		// See if other starposts exist in this level that have the same value.
 		for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
@@ -12821,7 +12919,7 @@ ML_EFFECT5 : Don't stop thinking when too far away
 			if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
 				continue;
 
-			mo2 = (mobj_t *)th;
+			mo2 = (mobj_t*)th;
 
 			if (mo2 == mobj)
 				continue;
@@ -12835,14 +12933,14 @@ ML_EFFECT5 : Don't stop thinking when too far away
 
 		if (!foundanother)
 			numstarposts++;
+		break;
 	}
-	else if (i == MT_SPIKE)
-	{
+	case MT_SPIKE:
 		// Pop up spikes!
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
 			mobj->flags &= ~MF_SCENERY;
-			mobj->fuse = (16 - mthing->extrainfo) * (mthing->angle + mobj->info->speed) / 16;
+			mobj->fuse = (16 - mthing->extrainfo)*(mthing->angle + mobj->info->speed)/16;
 			if (mthing->options & MTF_EXTRA)
 				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
@@ -12854,14 +12952,13 @@ ML_EFFECT5 : Don't stop thinking when too far away
 			mobj->flags |= MF_SOLID;
 			P_SetThingPosition(mobj);
 		}
-	}
-	else if (i == MT_WALLSPIKE)
-	{
+		break;
+	case MT_WALLSPIKE:
 		// Pop up spikes!
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
 			mobj->flags &= ~MF_SCENERY;
-			mobj->fuse = (16 - mthing->extrainfo) * ((mthing->angle/360) + mobj->info->speed) / 16;
+			mobj->fuse = (16 - mthing->extrainfo)*((mthing->angle/360) + mobj->info->speed)/16;
 			if (mthing->options & MTF_EXTRA)
 				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
@@ -12869,64 +12966,51 @@ ML_EFFECT5 : Don't stop thinking when too far away
 		if (!(mthing->options & MTF_AMBUSH) && !metalrecording)
 		{
 			P_UnsetThingPosition(mobj);
-			mobj->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIPHEIGHT);
+			mobj->flags &= ~(MF_NOBLOCKMAP | MF_NOCLIPHEIGHT);
 			mobj->flags |= MF_SOLID;
 			P_SetThingPosition(mobj);
 		}
 
 		// spawn base
 		{
-			const angle_t mobjangle = FixedAngle(mthing->angle<<FRACBITS); // the mobj's own angle hasn't been set quite yet so...
+			const angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS); // the mobj's own angle hasn't been set quite yet so...
 			const fixed_t baseradius = mobj->radius - mobj->scale;
-			mobj_t *base = P_SpawnMobj(
-					mobj->x - P_ReturnThrustX(mobj, mobjangle, baseradius),
-					mobj->y - P_ReturnThrustY(mobj, mobjangle, baseradius),
-					mobj->z, MT_WALLSPIKEBASE);
+			mobj_t* base = P_SpawnMobj(
+				mobj->x - P_ReturnThrustX(mobj, mobjangle, baseradius),
+				mobj->y - P_ReturnThrustY(mobj, mobjangle, baseradius),
+				mobj->z, MT_WALLSPIKEBASE);
 			base->angle = mobjangle + ANGLE_90;
 			base->destscale = mobj->destscale;
 			P_SetScale(base, mobj->scale);
 			P_SetTarget(&base->target, mobj);
 			P_SetTarget(&mobj->tracer, base);
 		}
-	}
-
-	//count 10 ring boxes into the number of rings equation too.
-	if (i == MT_RING_BOX && nummaprings >= 0)
-		nummaprings += 10;
-
-	if (i == MT_BIGTUMBLEWEED || i == MT_LITTLETUMBLEWEED)
-	{
+		break;
+	case MT_RING_BOX:
+		//count 10 ring boxes into the number of rings equation too.
+		if (nummaprings >= 0)
+			nummaprings += 10;
+		break;
+	case MT_BIGTUMBLEWEED:
+	case MT_LITTLETUMBLEWEED:
 		if (mthing->options & MTF_AMBUSH)
 		{
-			mobj->momz += FixedMul(16*FRACUNIT, mobj->scale);
-
-			if (P_RandomChance(FRACUNIT/2))
-				mobj->momx += FixedMul(16*FRACUNIT, mobj->scale);
-			else
-				mobj->momx -= FixedMul(16*FRACUNIT, mobj->scale);
-
-			if (P_RandomChance(FRACUNIT/2))
-				mobj->momy += FixedMul(16*FRACUNIT, mobj->scale);
-			else
-				mobj->momy -= FixedMul(16*FRACUNIT,mobj->scale);
+			fixed_t offset = FixedMul(16*FRACUNIT, mobj->scale);
+			mobj->momx += P_RandomChance(FRACUNIT/2) ? offset : -offset;
+			mobj->momy += P_RandomChance(FRACUNIT/2) ? offset : -offset;
+			mobj->momz += offset;
 		}
-	}
-
-	// CTF flag pointers
-	if (i == MT_REDFLAG)
-	{
+		break;
+	case MT_REDFLAG:
 		redflag = mobj;
 		rflagpoint = mobj->spawnpoint;
-	}
-	if (i == MT_BLUEFLAG)
-	{
+		break;
+	case MT_BLUEFLAG:
 		blueflag = mobj;
 		bflagpoint = mobj->spawnpoint;
-	}
-
-	// special push/pull stuff
-	if (i == MT_PUSH || i == MT_PULL)
-	{
+		break;
+	case MT_PUSH:
+	case MT_PULL:
 		mobj->health = 0; // Default behaviour: pushing uses XY, fading uses XYZ
 
 		if (mthing->options & MTF_AMBUSH)
@@ -12935,92 +13019,110 @@ ML_EFFECT5 : Don't stop thinking when too far away
 			mobj->health |= 2; // If object special is set, fade using XY
 
 		if (G_IsSpecialStage(gamemap))
-		{
-			if (i == MT_PUSH)
-				P_SetMobjState(mobj, S_GRAVWELLGREEN);
-			if (i == MT_PULL)
-				P_SetMobjState(mobj, S_GRAVWELLRED);
-		}
+			P_SetMobjState(mobj, (mobj->type == MT_PUSH) ? S_GRAVWELLGREEN : S_GRAVWELLRED);
+		break;
+	case MT_NIGHTSSTAR:
+		if (maptol & TOL_XMAS)
+			P_SetMobjState(mobj, mobj->info->seestate);
+		break;
+	default:
+		break;
 	}
 
-	if (doangle)
-		mobj->angle = FixedAngle(mthing->angle<<FRACBITS);
+	if (mobj->flags & MF_BOSS)
+	{
+		if (mthing->options & MTF_OBJECTSPECIAL) // No egg trap for this boss
+			mobj->flags2 |= MF2_BOSSNOTRAP;
+	}
 
-	// ignore MTF_ flags and return early
-	if (i == MT_NIGHTSBUMPER)
+	return true;
+}
+
+static void P_SetAmbush(mobj_t *mobj)
+{
+	if (mobj->type == MT_YELLOWDIAG || mobj->type == MT_REDDIAG || mobj->type == MT_BLUEDIAG)
+		mobj->angle += ANGLE_22h;
+
+	if (mobj->flags & MF_NIGHTSITEM)
 	{
-		mthing->mobj = mobj;
-		return;
+		// Spawn already displayed
+		mobj->flags |= MF_SPECIAL;
+		mobj->flags &= ~MF_NIGHTSITEM;
 	}
 
-	if ((mthing->options & MTF_AMBUSH)
-	&& (mthing->options & MTF_OBJECTSPECIAL)
-	&& (mobj->flags & MF_PUSHABLE))
-		mobj->flags2 |= MF2_CLASSICPUSH;
-	else
+	if (mobj->flags & MF_PUSHABLE)
+		mobj->flags &= ~MF_PUSHABLE;
+
+	if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
 	{
-		if (mthing->options & MTF_AMBUSH)
-		{
-			if (i == MT_YELLOWDIAG || i == MT_REDDIAG || i == MT_BLUEDIAG)
-				mobj->angle += ANGLE_22h;
+		// flag for strong/weak random boxes
+		// any monitor with nonzero speed is allowed to respawn like this
+		mobj->flags2 |= MF2_AMBUSH;
+	}
 
-			if (i == MT_YELLOWHORIZ || i == MT_REDHORIZ || i == MT_BLUEHORIZ)
-			{
-				if (mthing->options & MTF_OBJECTFLIP)
-					mobj->z -= 16*FRACUNIT;
-				else
-					mobj->z += 16*FRACUNIT;
-			}
+	else if (mobj->type != MT_AXIS &&
+		mobj->type != MT_AXISTRANSFER &&
+		mobj->type != MT_AXISTRANSFERLINE &&
+		mobj->type != MT_NIGHTSBUMPER &&
+		mobj->type != MT_STARPOST)
+		mobj->flags2 |= MF2_AMBUSH;
+}
 
+static void P_SetObjectSpecial(mobj_t *mobj)
+{
+	if (mobj->type == MT_YELLOWDIAG || mobj->type == MT_REDDIAG || mobj->type == MT_BLUEDIAG)
+		mobj->flags |= MF_NOGRAVITY;
 
-			if (mobj->flags & MF_NIGHTSITEM)
-			{
-				// Spawn already displayed
-				mobj->flags |= MF_SPECIAL;
-				mobj->flags &= ~MF_NIGHTSITEM;
-			}
+	if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
+	{
+		// flag for strong/weak random boxes
+		// any monitor with nonzero speed is allowed to respawn like this
+		mobj->flags2 |= MF2_STRONGBOX;
+	}
 
-			if (mobj->flags & MF_PUSHABLE)
-				mobj->flags &= ~MF_PUSHABLE;
+	// Requires you to be in bonus time to activate
+	if (mobj->flags & MF_NIGHTSITEM)
+		mobj->flags2 |= MF2_STRONGBOX;
 
-			if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
-			{
-				// flag for strong/weak random boxes
-				// any monitor with nonzero speed is allowed to respawn like this
-				mobj->flags2 |= MF2_AMBUSH;
-			}
+	// Pushables bounce and slide coolly with object special flag set
+	if (mobj->flags & MF_PUSHABLE)
+	{
+		mobj->flags2 |= MF2_SLIDEPUSH;
+		mobj->flags |= MF_BOUNCE;
+	}
+}
 
-			else if (mthing->type != mobjinfo[MT_AXIS].doomednum &&
-				mthing->type != mobjinfo[MT_AXISTRANSFER].doomednum &&
-				mthing->type != mobjinfo[MT_AXISTRANSFERLINE].doomednum &&
-				mthing->type != mobjinfo[MT_NIGHTSBUMPER].doomednum &&
-				mthing->type != mobjinfo[MT_STARPOST].doomednum)
-				mobj->flags2 |= MF2_AMBUSH;
-		}
+static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t i)
+{
+	mobj_t *mobj = NULL;
+	boolean doangle = true;
 
-		if (mthing->options & MTF_OBJECTSPECIAL)
-		{
-			if (i == MT_YELLOWDIAG || i == MT_REDDIAG || i == MT_BLUEDIAG)
-				mobj->flags |= MF_NOGRAVITY;
+	mobj = P_SpawnMobj(x, y, z, i);
+	mobj->spawnpoint = mthing;
 
-			if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
-			{
-				// flag for strong/weak random boxes
-				// any monitor with nonzero speed is allowed to respawn like this
-				mobj->flags2 |= MF2_STRONGBOX;
-			}
+	if (!P_SetupSpawnedMapThing(mthing, mobj, &doangle))
+		return mobj;
+
+	if (doangle)
+		mobj->angle = FixedAngle(mthing->angle << FRACBITS);
+
+	mthing->mobj = mobj;
+
+	// ignore MTF_ flags and return early
+	if (i == MT_NIGHTSBUMPER)
+		return mobj;
 
-			// Requires you to be in bonus time to activate
-			if (mobj->flags & MF_NIGHTSITEM)
-				mobj->flags2 |= MF2_STRONGBOX;
+	if ((mthing->options & MTF_AMBUSH)
+		&& (mthing->options & MTF_OBJECTSPECIAL)
+		&& (mobj->flags & MF_PUSHABLE))
+		mobj->flags2 |= MF2_CLASSICPUSH;
+	else
+	{
+		if (mthing->options & MTF_AMBUSH)
+			P_SetAmbush(mobj);
 
-			// Pushables bounce and slide coolly with object special flag set
-			if (mobj->flags & MF_PUSHABLE)
-			{
-				mobj->flags2 |= MF2_SLIDEPUSH;
-				mobj->flags |= MF_BOUNCE;
-			}
-		}
+		if (mthing->options & MTF_OBJECTSPECIAL)
+			P_SetObjectSpecial(mobj);
 	}
 
 	// Generic reverse gravity for individual objects flag.
@@ -13044,579 +13146,334 @@ ML_EFFECT5 : Don't stop thinking when too far away
 	if (mobj->flags & MF_NIGHTSITEM)
 		mobj->flags2 |= MF2_DONTDRAW;
 
-	mthing->mobj = mobj;
+	return mobj;
 }
 
-void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime)
+//
+// P_SpawnMapThing
+// The fields of the mapthing should
+// already be in host byte order.
+//
+mobj_t *P_SpawnMapThing(mapthing_t *mthing)
 {
-	mobjtype_t ringthing = MT_RING;
+	mobjtype_t i;
 	mobj_t *mobj = NULL;
-	INT32 r, i;
-	fixed_t x, y, z, finalx, finaly, finalz;
-	sector_t *sec;
-	TVector v, *res;
-	angle_t closestangle, fa;
-	boolean nightsreplace = ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap));
-
-	x = mthing->x << FRACBITS;
-	y = mthing->y << FRACBITS;
-
-	sec = R_PointInSubsector(x, y)->sector;
-
-	// NiGHTS hoop!
-	if (mthing->type == 1705)
-	{
-		mobj_t *nextmobj = NULL;
-		mobj_t *hoopcenter;
-		INT16 spewangle;
-
-		z = mthing->options << FRACBITS;
-
-		hoopcenter = P_SpawnMobj(x, y, z, MT_HOOPCENTER);
+	fixed_t x, y, z;
 
-		hoopcenter->spawnpoint = mthing;
+	if (!mthing->type)
+		return mobj; // Ignore type-0 things as NOPs
 
-		// Screw these damn hoops, I need this thinker.
-		//hoopcenter->flags |= MF_NOTHINK;
+	if (mthing->type == 3328) // 3D Mode start Thing
+		return mobj;
 
-		z +=
-#ifdef ESLOPE
-			sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-			sec->floorheight;
+	if (!objectplacing && P_SpawnNonMobjMapThing(mthing))
+		return mobj;
 
-		hoopcenter->z = z - hoopcenter->height/2;
+	i = P_GetMobjtype(mthing->type);
+	if (i == MT_UNKNOWN)
+		CONS_Alert(CONS_WARNING, M_GetText("Unknown thing type %d placed at (%d, %d)\n"), mthing->type, mthing->x, mthing->y);
 
-		P_UnsetThingPosition(hoopcenter);
-		hoopcenter->x = x;
-		hoopcenter->y = y;
-		P_SetThingPosition(hoopcenter);
+	// Skip all returning/substitution code in objectplace.
+	if (!objectplacing)
+	{
+		if (!P_AllowMobjSpawn(mthing, i))
+			return mobj;
 
-		// Scale 0-255 to 0-359 =(
-		closestangle = FixedAngle(FixedMul((mthing->angle>>8)*FRACUNIT,
-			360*(FRACUNIT/256)));
+		i = P_GetMobjtypeSubstitute(mthing, i);
+		if (i == MT_NULL) // Don't spawn mobj
+			return mobj;
+	}
 
-		hoopcenter->movedir = FixedInt(FixedMul((mthing->angle&255)*FRACUNIT,
-			360*(FRACUNIT/256)));
-		hoopcenter->movecount = FixedInt(AngleFixed(closestangle));
+	x = mthing->x << FRACBITS;
+	y = mthing->y << FRACBITS;
+	z = P_GetMapThingSpawnHeight(i, mthing, x, y);
+	return P_SpawnMobjFromMapThing(mthing, x, y, z, i);
+}
 
-		// For the hoop when it flies away
-		hoopcenter->extravalue1 = 32;
-		hoopcenter->extravalue2 = 8 * FRACUNIT;
+static void P_SpawnHoopInternal(mapthing_t *mthing, INT32 hoopsize, fixed_t sizefactor)
+{
+	mobj_t *mobj = NULL;
+	mobj_t *nextmobj = NULL;
+	mobj_t *hoopcenter;
+	TMatrix *pitchmatrix, *yawmatrix;
+	fixed_t radius = hoopsize*sizefactor;
+	INT32 i;
+	angle_t fa;
+	TVector v, *res;
+	fixed_t x = mthing->x << FRACBITS;
+	fixed_t y = mthing->y << FRACBITS;
+	fixed_t z = P_GetMobjSpawnHeight(MT_HOOP, x, y, mthing->z << FRACBITS, false);
 
-		spewangle = (INT16)hoopcenter->movedir;
+	hoopcenter = P_SpawnMobj(x, y, z, MT_HOOPCENTER);
+	hoopcenter->spawnpoint = mthing;
+	hoopcenter->z -= hoopcenter->height/2;
 
-		// Create the hoop!
-		for (i = 0; i < 32; i++)
-		{
-			fa = i*(FINEANGLES/32);
-			v[0] = FixedMul(FINECOSINE(fa),96*FRACUNIT);
-			v[1] = 0;
-			v[2] = FixedMul(FINESINE(fa),96*FRACUNIT);
-			v[3] = FRACUNIT;
+	P_UnsetThingPosition(hoopcenter);
+	hoopcenter->x = x;
+	hoopcenter->y = y;
+	P_SetThingPosition(hoopcenter);
 
-			res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(spewangle*FRACUNIT)));
-			M_Memcpy(&v, res, sizeof (v));
-			res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
-			M_Memcpy(&v, res, sizeof (v));
+	// Scale 0-255 to 0-359 =(
+	hoopcenter->movedir = ((mthing->angle & 255)*360)/256; // Pitch
+	pitchmatrix = RotateXMatrix(FixedAngle(hoopcenter->movedir << FRACBITS));
+	hoopcenter->movecount = (((UINT16)mthing->angle >> 8)*360)/256; // Yaw
+	yawmatrix = RotateZMatrix(FixedAngle(hoopcenter->movecount << FRACBITS));
 
-			finalx = x + v[0];
-			finaly = y + v[1];
-			finalz = z + v[2];
+	// For the hoop when it flies away
+	hoopcenter->extravalue1 = hoopsize;
+	hoopcenter->extravalue2 = radius/12;
 
-			mobj = P_SpawnMobj(finalx, finaly, finalz, MT_HOOP);
+	// Create the hoop!
+	for (i = 0; i < hoopsize; i++)
+	{
+		fa = i*(FINEANGLES/hoopsize);
+		v[0] = FixedMul(FINECOSINE(fa), radius);
+		v[1] = 0;
+		v[2] = FixedMul(FINESINE(fa), radius);
+		v[3] = FRACUNIT;
 
-			if (maptol & TOL_XMAS)
-				P_SetMobjState(mobj, mobj->info->seestate + (i & 1));
+		res = VectorMatrixMultiply(v, *pitchmatrix);
+		M_Memcpy(&v, res, sizeof(v));
+		res = VectorMatrixMultiply(v, *yawmatrix);
+		M_Memcpy(&v, res, sizeof(v));
 
-			mobj->z -= mobj->height/2;
-			P_SetTarget(&mobj->target, hoopcenter); // Link the sprite to the center.
-			mobj->fuse = 0;
+		mobj = P_SpawnMobj(x + v[0], y + v[1], z + v[2], MT_HOOP);
+		mobj->z -= mobj->height/2;
 
-			// Link all the sprites in the hoop together
-			if (nextmobj)
-			{
-				P_SetTarget(&mobj->hprev, nextmobj);
-				P_SetTarget(&mobj->hprev->hnext, mobj);
-			}
-			else
-				P_SetTarget(&mobj->hprev, P_SetTarget(&mobj->hnext, NULL));
+		if (maptol & TOL_XMAS)
+			P_SetMobjState(mobj, mobj->info->seestate + (i & 1));
 
-			nextmobj = mobj;
-		}
+		P_SetTarget(&mobj->target, hoopcenter); // Link the sprite to the center.
+		mobj->fuse = 0;
 
-		// Create the collision detectors!
-		for (i = 0; i < 16; i++)
+		// Link all the sprites in the hoop together
+		if (nextmobj)
 		{
-			fa = i*FINEANGLES/16;
-			v[0] = FixedMul(FINECOSINE(fa),32*FRACUNIT);
-			v[1] = 0;
-			v[2] = FixedMul(FINESINE(fa),32*FRACUNIT);
-			v[3] = FRACUNIT;
-			res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(spewangle*FRACUNIT)));
-			M_Memcpy(&v, res, sizeof (v));
-			res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
-			M_Memcpy(&v, res, sizeof (v));
-
-			finalx = x + v[0];
-			finaly = y + v[1];
-			finalz = z + v[2];
-
-			mobj = P_SpawnMobj(finalx, finaly, finalz, MT_HOOPCOLLIDE);
-			mobj->z -= mobj->height/2;
-
-			// Link all the collision sprites together.
-			P_SetTarget(&mobj->hnext, NULL);
 			P_SetTarget(&mobj->hprev, nextmobj);
 			P_SetTarget(&mobj->hprev->hnext, mobj);
-
-			nextmobj = mobj;
 		}
-		// Create the collision detectors!
-		for (i = 0; i < 16; i++)
-		{
-			fa = i*FINEANGLES/16;
-			v[0] = FixedMul(FINECOSINE(fa),64*FRACUNIT);
-			v[1] = 0;
-			v[2] = FixedMul(FINESINE(fa),64*FRACUNIT);
-			v[3] = FRACUNIT;
-			res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(spewangle*FRACUNIT)));
-			M_Memcpy(&v, res, sizeof (v));
-			res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
-			M_Memcpy(&v, res, sizeof (v));
-
-			finalx = x + v[0];
-			finaly = y + v[1];
-			finalz = z + v[2];
-
-			mobj = P_SpawnMobj(finalx, finaly, finalz, MT_HOOPCOLLIDE);
-			mobj->z -= mobj->height/2;
-
-			// Link all the collision sprites together.
-			P_SetTarget(&mobj->hnext, NULL);
-			P_SetTarget(&mobj->hprev, nextmobj);
-			P_SetTarget(&mobj->hprev->hnext, mobj);
+		else
+			P_SetTarget(&mobj->hprev, P_SetTarget(&mobj->hnext, NULL));
 
-			nextmobj = mobj;
-		}
-		return;
+		nextmobj = mobj;
 	}
-	// CUSTOMIZABLE NiGHTS hoop!
-	else if (mthing->type == 1713)
-	{
-		mobj_t *nextmobj = NULL;
-		mobj_t *hoopcenter;
-		INT16 spewangle;
-		INT32 hoopsize;
-		INT32 hoopplacement;
-
-		// Save our flags!
-		z = (mthing->options>>ZSHIFT) << FRACBITS;
-
-		hoopcenter = P_SpawnMobj(x, y, z, MT_HOOPCENTER);
-		hoopcenter->spawnpoint = mthing;
 
-		z +=
-#ifdef ESLOPE
-			sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-			sec->floorheight;
-		hoopcenter->z = z - hoopcenter->height/2;
-
-		P_UnsetThingPosition(hoopcenter);
-		hoopcenter->x = x;
-		hoopcenter->y = y;
-		P_SetThingPosition(hoopcenter);
-
-		// Scale 0-255 to 0-359 =(
-		closestangle = FixedAngle(FixedMul((mthing->angle>>8)*FRACUNIT,
-			360*(FRACUNIT/256)));
-
-		hoopcenter->movedir = FixedInt(FixedMul((mthing->angle&255)*FRACUNIT,
-			360*(FRACUNIT/256)));
-		hoopcenter->movecount = FixedInt(AngleFixed(closestangle));
-
-		spewangle = (INT16)hoopcenter->movedir;
-
-		// Super happy fun time
-		// For each flag add 4 fracunits to the size
-		// Default (0 flags) is 8 fracunits
-		hoopsize = 8 + (4 * (mthing->options & 0xF));
-		hoopplacement = hoopsize * (4*FRACUNIT);
+	// Create the collision detectors!
+	// Create them until the size is less than 8
+	// But always create at least ONE set of collision detectors
+	do
+	{
+		if (hoopsize >= 32)
+			hoopsize -= 16;
+		else
+			hoopsize /= 2;
 
-		// For the hoop when it flies away
-		hoopcenter->extravalue1 = hoopsize;
-		hoopcenter->extravalue2 = FixedDiv(hoopplacement, 12*FRACUNIT);
+		radius = hoopsize*sizefactor;
 
-		// Create the hoop!
 		for (i = 0; i < hoopsize; i++)
 		{
 			fa = i*(FINEANGLES/hoopsize);
-			v[0] = FixedMul(FINECOSINE(fa), hoopplacement);
+			v[0] = FixedMul(FINECOSINE(fa), radius);
 			v[1] = 0;
-			v[2] = FixedMul(FINESINE(fa), hoopplacement);
+			v[2] = FixedMul(FINESINE(fa), radius);
 			v[3] = FRACUNIT;
 
-			res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(spewangle*FRACUNIT)));
-			M_Memcpy(&v, res, sizeof (v));
-			res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
-			M_Memcpy(&v, res, sizeof (v));
-
-			finalx = x + v[0];
-			finaly = y + v[1];
-			finalz = z + v[2];
-
-			mobj = P_SpawnMobj(finalx, finaly, finalz, MT_HOOP);
-
-			if (maptol & TOL_XMAS)
-				P_SetMobjState(mobj, mobj->info->seestate + (i & 1));
+			res = VectorMatrixMultiply(v, *pitchmatrix);
+			M_Memcpy(&v, res, sizeof(v));
+			res = VectorMatrixMultiply(v, *yawmatrix);
+			M_Memcpy(&v, res, sizeof(v));
 
+			mobj = P_SpawnMobj(x + v[0], y + v[1], z + v[2], MT_HOOPCOLLIDE);
 			mobj->z -= mobj->height/2;
-			P_SetTarget(&mobj->target, hoopcenter); // Link the sprite to the center.
-			mobj->fuse = 0;
 
-			// Link all the sprites in the hoop together
-			if (nextmobj)
-			{
-				P_SetTarget(&mobj->hprev, nextmobj);
-				P_SetTarget(&mobj->hprev->hnext, mobj);
-			}
-			else
-				P_SetTarget(&mobj->hprev, P_SetTarget(&mobj->hnext, NULL));
+			// Link all the collision sprites together.
+			P_SetTarget(&mobj->hnext, NULL);
+			P_SetTarget(&mobj->hprev, nextmobj);
+			P_SetTarget(&mobj->hprev->hnext, mobj);
 
 			nextmobj = mobj;
 		}
+	} while (hoopsize >= 8);
+}
 
-		// Create the collision detectors!
-		// Create them until the size is less than 8
-		// But always create at least ONE set of collision detectors
-		do
-		{
-			if (hoopsize >= 32)
-				hoopsize -= 16;
-			else
-				hoopsize /= 2;
-
-			hoopplacement = hoopsize * (4*FRACUNIT);
-
-			for (i = 0; i < hoopsize; i++)
-			{
-				fa = i*FINEANGLES/hoopsize;
-				v[0] = FixedMul(FINECOSINE(fa), hoopplacement);
-				v[1] = 0;
-				v[2] = FixedMul(FINESINE(fa), hoopplacement);
-				v[3] = FRACUNIT;
-				res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(spewangle*FRACUNIT)));
-				M_Memcpy(&v, res, sizeof (v));
-				res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
-				M_Memcpy(&v, res, sizeof (v));
-
-				finalx = x + v[0];
-				finaly = y + v[1];
-				finalz = z + v[2];
-
-				mobj = P_SpawnMobj(finalx, finaly, finalz, MT_HOOPCOLLIDE);
-				mobj->z -= mobj->height/2;
-
-				// Link all the collision sprites together.
-				P_SetTarget(&mobj->hnext, NULL);
-				P_SetTarget(&mobj->hprev, nextmobj);
-				P_SetTarget(&mobj->hprev->hnext, mobj);
+void P_SpawnHoop(mapthing_t *mthing)
+{
+	if (metalrecording)
+		return;
 
-				nextmobj = mobj;
-			}
-		} while (hoopsize >= 8);
+	if (mthing->type == 1705) // Generic hoop
+		P_SpawnHoopInternal(mthing, 24, 4*FRACUNIT);
+	else // Customizable hoop
+		// For each flag add 16 fracunits to the size
+		// Default (0 flags) is 32 fracunits
+		P_SpawnHoopInternal(mthing, 8 + (4*(mthing->options & 0xF)), 4*FRACUNIT);
+}
 
+void P_SetBonusTime(mobj_t *mobj)
+{
+	if (!mobj)
 		return;
-	}
-	// ***
-	// Special placement patterns
-	// ***
-
-	// Vertical Rings - Stack of 5 (handles both red and yellow)
-	else if (mthing->type == 600 || mthing->type == 601)
-	{
-		INT32 dist = 64*FRACUNIT;
-		if (mthing->type == 601)
-			dist = 128*FRACUNIT;
 
-		if (ultimatemode)
-			return; // No rings in Ultimate!
+	if (mobj->type != MT_BLUESPHERE && mobj->type != MT_NIGHTSCHIP)
+		return;
 
-		if (nightsreplace)
-			ringthing = MT_NIGHTSSTAR;
+	P_SetMobjState(mobj, mobj->info->raisestate);
+}
 
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			z = (
-#ifdef ESLOPE
-				sec->c_slope ? P_GetZAt(sec->c_slope, x, y) :
-#endif
-				sec->ceilingheight) - mobjinfo[ringthing].height;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
-			}
-		else
-		{
-			z = (
-#ifdef ESLOPE
-				sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-				sec->floorheight);
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
+static void P_SpawnItemRow(mapthing_t *mthing, mobjtype_t* itemtypes, UINT8 numitemtypes, INT32 numitems, fixed_t horizontalspacing, fixed_t verticalspacing, INT16 fixedangle, boolean bonustime)
+{
+	mapthing_t dummything;
+	mobj_t *mobj = NULL;
+	fixed_t x = mthing->x << FRACBITS;
+	fixed_t y = mthing->y << FRACBITS;
+	fixed_t z = mthing->z << FRACBITS;
+	INT32 r;
+	angle_t angle = FixedAngle(fixedangle << FRACBITS);
+	angle_t fineangle = (angle >> ANGLETOFINESHIFT) & FINEMASK;
 
-		for (r = 1; r <= 5; r++)
+	for (r = 0; r < numitemtypes; r++)
+	{
+		dummything = *mthing;
+		dummything.type = mobjinfo[itemtypes[r]].doomednum;
+		// Skip all returning/substitution code in objectplace.
+		if (!objectplacing)
 		{
-			if (mthing->options & MTF_OBJECTFLIP)
-				z -= dist;
-			else
-				z += dist;
-
-			mobj = P_SpawnMobj(x, y, z, ringthing);
-
-			if (mthing->options & MTF_OBJECTFLIP)
+			if (!P_AllowMobjSpawn(&dummything, itemtypes[r]))
 			{
-				mobj->eflags |= MFE_VERTICALFLIP;
-				mobj->flags2 |= MF2_OBJECTFLIP;
+				itemtypes[r] = MT_NULL;
+				continue;
 			}
 
-			mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-			if (mthing->options & MTF_AMBUSH)
-				mobj->flags2 |= MF2_AMBUSH;
-
-			if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
-				P_SetMobjState(mobj, mobj->info->seestate);
+			itemtypes[r] = P_GetMobjtypeSubstitute(&dummything, itemtypes[r]);
 		}
 	}
-	// Diagonal rings (handles both types)
-	else if (mthing->type == 602 || mthing->type == 603) // Diagonal rings (5)
-	{
-		INT32 iterations = 5;
-		if (mthing->type == 603)
-			iterations = 10;
-
-		if (ultimatemode)
-			return; // No rings in Ultimate!
-
-		if (nightsreplace)
-			ringthing = MT_NIGHTSSTAR;
-
-		closestangle = FixedAngle(mthing->angle*FRACUNIT);
-		fa = (closestangle >> ANGLETOFINESHIFT);
-
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			z = (
-#ifdef ESLOPE
-				sec->c_slope ? P_GetZAt(sec->c_slope, x, y) :
-#endif
-				sec->ceilingheight) - mobjinfo[ringthing].height;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
-			}
-		else
-		{
-			z = (
-#ifdef ESLOPE
-				sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-				sec->floorheight);
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
-
-		for (r = 1; r <= iterations; r++)
-		{
-			x += FixedMul(64*FRACUNIT, FINECOSINE(fa));
-			y += FixedMul(64*FRACUNIT, FINESINE(fa));
+	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, mthing->options & MTF_OBJECTFLIP);
 
-			if (mthing->options & MTF_OBJECTFLIP)
-				z -= 64*FRACUNIT;
-			else
-				z += 64*FRACUNIT;
+	for (r = 0; r < numitems; r++)
+	{
+		mobjtype_t itemtype = itemtypes[r % numitemtypes];
+		if (itemtype == MT_NULL)
+			continue;
+		dummything.type = mobjinfo[itemtype].doomednum;
 
-			mobj = P_SpawnMobj(x, y, z, ringthing);
+		x += FixedMul(horizontalspacing, FINECOSINE(fineangle));
+		y += FixedMul(horizontalspacing, FINESINE(fineangle));
+		z += (mthing->options & MTF_OBJECTFLIP) ? -verticalspacing : verticalspacing;
 
-			if (mthing->options & MTF_OBJECTFLIP)
-			{
-				mobj->eflags |= MFE_VERTICALFLIP;
-				mobj->flags2 |= MF2_OBJECTFLIP;
-			}
+		mobj = P_SpawnMobjFromMapThing(&dummything, x, y, z, itemtype);
 
-			mobj->angle = closestangle;
-			if (mthing->options & MTF_AMBUSH)
-				mobj->flags2 |= MF2_AMBUSH;
+		if (!mobj)
+			continue;
 
-			if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
-				P_SetMobjState(mobj, mobj->info->seestate);
-		}
+		mobj->spawnpoint = NULL;
+		if (bonustime)
+			P_SetBonusTime(mobj);
 	}
-	// Rings of items (all six of them)
-	else if (mthing->type >= 604 && mthing->type <= 609)
-	{
-		INT32 numitems = 8;
-		INT32 size = 96*FRACUNIT;
-
-		if (mthing->type & 1)
-		{
-			numitems = 16;
-			size = 192*FRACUNIT;
-		}
-
-		z =
-#ifdef ESLOPE
-			sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-			sec->floorheight;
-		if (mthing->options >> ZSHIFT)
-			z += ((mthing->options >> ZSHIFT) << FRACBITS);
+}
 
-		closestangle = FixedAngle(mthing->angle*FRACUNIT);
+static void P_SpawnSingularItemRow(mapthing_t* mthing, mobjtype_t itemtype, INT32 numitems, fixed_t horizontalspacing, fixed_t verticalspacing, INT16 fixedangle, boolean bonustime)
+{
+	mobjtype_t itemtypes[1] = { itemtype };
+	return P_SpawnItemRow(mthing, itemtypes, 1, numitems, horizontalspacing, verticalspacing, fixedangle, bonustime);
+}
 
-		switch (mthing->type)
-		{
-			case 604:
-			case 605:
-				if (ultimatemode)
-					return; // No rings in Ultimate!
-				if (nightsreplace)
-					ringthing = MT_NIGHTSSTAR;
-				break;
-			case 608:
-			case 609:
-				/*ringthing = (i & 1) ? MT_RING : MT_BLUESPHERE; -- i == 0 is bluesphere
-				break;*/
-			case 606:
-			case 607:
-				ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE;
-				break;
-			default:
-				break;
-		}
+static void P_SpawnItemCircle(mapthing_t *mthing, mobjtype_t *itemtypes, UINT8 numitemtypes, INT32 numitems, fixed_t size, boolean bonustime)
+{
+	mapthing_t dummything;
+	mobj_t* mobj = NULL;
+	fixed_t x = mthing->x << FRACBITS;
+	fixed_t y = mthing->y << FRACBITS;
+	fixed_t z = mthing->z << FRACBITS;
+	angle_t angle = FixedAngle(mthing->angle << FRACBITS);
+	angle_t fa;
+	INT32 i;
+	TVector v, *res;
 
-		// Create the hoop!
-		for (i = 0; i < numitems; i++)
+	for (i = 0; i < numitemtypes; i++)
+	{
+		dummything = *mthing;
+		dummything.type = mobjinfo[itemtypes[i]].doomednum;
+		// Skip all returning/substitution code in objectplace.
+		if (!objectplacing)
 		{
-			if (mthing->type == 608 || mthing->type == 609)
-			{
-				if (i & 1)
-				{
-					if (ultimatemode)
-						continue; // No rings in Ultimate!
-					ringthing = (nightsreplace) ? MT_NIGHTSSTAR : MT_RING;
-				}
-				else
-					ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE;
-			}
-
-			fa = i*FINEANGLES/numitems;
-			v[0] = FixedMul(FINECOSINE(fa),size);
-			v[1] = 0;
-			v[2] = FixedMul(FINESINE(fa),size);
-			v[3] = FRACUNIT;
-
-			res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
-			M_Memcpy(&v, res, sizeof (v));
-
-			finalx = x + v[0];
-			finaly = y + v[1];
-			finalz = z + v[2];
-
-			mobj = P_SpawnMobj(finalx, finaly, finalz, ringthing);
-			mobj->z -= mobj->height/2;
-
-			if (mthing->options & MTF_OBJECTFLIP)
+			if (!P_AllowMobjSpawn(&dummything, itemtypes[i]))
 			{
-				mobj->eflags |= MFE_VERTICALFLIP;
-				mobj->flags2 |= MF2_OBJECTFLIP;
+				itemtypes[i] = MT_NULL;
+				continue;
 			}
 
-			mobj->angle = closestangle;
-			if (mthing->options & MTF_AMBUSH)
-				mobj->flags2 |= MF2_AMBUSH;
-
-			if (bonustime && (ringthing == MT_BLUESPHERE || ringthing == MT_NIGHTSCHIP))
-				P_SetMobjState(mobj, mobj->info->raisestate);
-			else if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
-				P_SetMobjState(mobj, mobj->info->seestate);
+			itemtypes[i] = P_GetMobjtypeSubstitute(&dummything, itemtypes[i]);
 		}
 	}
-	// All manners of rings and coins
-	else
-	{
-
-		// Which ringthing to use
-		if (mthing->type == mobjinfo[MT_BLUESPHERE].doomednum)
-			ringthing = (nightsreplace) ? MT_NIGHTSCHIP : MT_BLUESPHERE;
-		else if (mthing->type == mobjinfo[MT_BOMBSPHERE].doomednum)
-			ringthing = MT_BOMBSPHERE;
-		else
-		{
-			if (ultimatemode)
-				return; // No rings in Ultimate!
+	z = P_GetMobjSpawnHeight(itemtypes[0], x, y, z, false);
 
-			if (nightsreplace)
-				ringthing = MT_NIGHTSSTAR;
-			else if (mthing->type == mobjinfo[MT_COIN].doomednum)
-				ringthing = MT_COIN;
-			else if (mthing->type == mobjinfo[MT_REDTEAMRING].doomednum) // No team rings in non-CTF
-				ringthing = (gametype == GT_CTF) ? MT_REDTEAMRING : MT_RING;
-			else if (mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum) // Ditto
-				ringthing = (gametype == GT_CTF) ? MT_BLUETEAMRING : MT_RING;
-		}
-
-		// Set proper height
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			z = (
-#ifdef ESLOPE
-			sec->c_slope ? P_GetZAt(sec->c_slope, x, y) :
-#endif
-			sec->ceilingheight) - mobjinfo[ringthing].height;
-			if (mthing->options >> ZSHIFT)
-				z -= ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
-		else
-		{
-			z =
-#ifdef ESLOPE
-			sec->f_slope ? P_GetZAt(sec->f_slope, x, y) :
-#endif
-			sec->floorheight;
-			if (mthing->options >> ZSHIFT)
-				z += ((mthing->options >> ZSHIFT) << FRACBITS);
-		}
+	for (i = 0; i < numitems; i++)
+	{
+		mobjtype_t itemtype = itemtypes[i % numitemtypes];
+		if (itemtype == MT_NULL)
+			continue;
+		dummything.type = mobjinfo[itemtype].doomednum;
 
-		if (mthing->options & MTF_AMBUSH) // Special flag for rings
-		{
-			if (mthing->options & MTF_OBJECTFLIP)
-				z -= 24*FRACUNIT;
-			else
-				z += 24*FRACUNIT;
-		}
+		fa = i*FINEANGLES/numitems;
+		v[0] = FixedMul(FINECOSINE(fa), size);
+		v[1] = 0;
+		v[2] = FixedMul(FINESINE(fa), size);
+		v[3] = FRACUNIT;
 
-		mthing->z = (INT16)(z>>FRACBITS);
+		res = VectorMatrixMultiply(v, *RotateZMatrix(angle));
+		M_Memcpy(&v, res, sizeof(v));
 
-		mobj = P_SpawnMobj(x, y, z, ringthing);
-		mobj->spawnpoint = mthing;
+		mobj = P_SpawnMobjFromMapThing(&dummything, x + v[0], y + v[1], z + v[2], itemtype);
 
-		if (mthing->options & MTF_OBJECTFLIP)
-		{
-			mobj->eflags |= MFE_VERTICALFLIP;
-			mobj->flags2 |= MF2_OBJECTFLIP;
-		}
+		if (!mobj)
+			continue;
 
-		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-		mthing->mobj = mobj;
-		if (mthing->options & MTF_AMBUSH)
-			mobj->flags2 |= MF2_AMBUSH;
+		mobj->z -= mobj->height/2;
+		mobj->spawnpoint = NULL;
+		if (bonustime)
+			P_SetBonusTime(mobj);
+	}
+}
 
-		if (bonustime && (ringthing == MT_BLUESPHERE || ringthing == MT_NIGHTSCHIP))
-			P_SetMobjState(mobj, mobj->info->raisestate);
-		else if ((maptol & TOL_XMAS) && (ringthing == MT_NIGHTSSTAR))
-			P_SetMobjState(mobj, mobj->info->seestate);
+void P_SpawnItemPattern(mapthing_t *mthing, boolean bonustime)
+{
+	switch (mthing->type)
+	{
+	// Special placement patterns
+	case 600: // 5 vertical rings (yellow spring)
+		P_SpawnSingularItemRow(mthing, MT_RING, 5, 0, 64*FRACUNIT, 0, bonustime);
+		return;
+	case 601: // 5 vertical rings (red spring)
+		P_SpawnSingularItemRow(mthing, MT_RING, 5, 0, 128*FRACUNIT, 0, bonustime);
+		return;
+	case 602: // 5 diagonal rings (yellow spring)
+		P_SpawnSingularItemRow(mthing, MT_RING, 5, 64*FRACUNIT, 64*FRACUNIT, mthing->angle, bonustime);
+		return;
+	case 603: // 10 diagonal rings (red spring)
+		P_SpawnSingularItemRow(mthing, MT_RING, 10, 64*FRACUNIT, 64*FRACUNIT, mthing->angle, bonustime);
+		return;
+	case 604: // Circle of rings (8 items)
+	case 605: // Circle of rings (16 items)
+	case 606: // Circle of blue spheres (8 items)
+	case 607: // Circle of blue spheres (16 items)
+	{
+		INT32 numitems = (mthing->type & 1) ? 16 : 8;
+		fixed_t size = (mthing->type & 1) ? 192*FRACUNIT : 96*FRACUNIT;
+		mobjtype_t itemtypes[1] = { (mthing->type < 606) ? MT_RING : MT_BLUESPHERE };
+		P_SpawnItemCircle(mthing, itemtypes, 1, numitems, size, bonustime);
+		return;
+	}
+	case 608: // Circle of rings and blue spheres (8 items)
+	case 609: // Circle of rings and blue spheres (16 items)
+	{
+		INT32 numitems = (mthing->type & 1) ? 16 : 8;
+		fixed_t size = (mthing->type & 1) ? 192*FRACUNIT : 96*FRACUNIT;
+		mobjtype_t itemtypes[2] = { MT_RING, MT_BLUESPHERE };
+		P_SpawnItemCircle(mthing, itemtypes, 2, numitems, size, bonustime);
+		return;
+	}
+	default:
+		return;
 	}
 }
 
diff --git a/src/p_mobj.h b/src/p_mobj.h
index de8d2909322549edd7eb84fd06d436383156352f..92160d9e2d70f4634d90d47feed67cfa5940b1a9 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -279,9 +279,7 @@ typedef struct mobj_s
 
 	// More drawing info: to determine current sprite.
 	angle_t angle;  // orientation
-#ifdef ROTSPRITE
 	angle_t rollangle;
-#endif
 	spritenum_t sprite; // used to find patch_t and flip value
 	UINT32 frame; // frame number, plus bits see p_pspr.h
 	UINT8 sprite2; // player sprites
@@ -402,9 +400,7 @@ typedef struct precipmobj_s
 
 	// More drawing info: to determine current sprite.
 	angle_t angle;  // orientation
-#ifdef ROTSPRITE
 	angle_t rollangle;
-#endif
 	spritenum_t sprite; // used to find patch_t and flip value
 	UINT32 frame; // frame number, plus bits see p_pspr.h
 	UINT8 sprite2; // player sprites
@@ -456,8 +452,10 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing);
 void P_MovePlayerToStarpost(INT32 playernum);
 void P_AfterPlayerSpawn(INT32 playernum);
 
-void P_SpawnMapThing(mapthing_t *mthing);
-void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime);
+mobj_t *P_SpawnMapThing(mapthing_t *mthing);
+void P_SpawnHoop(mapthing_t *mthing);
+void P_SetBonusTime(mobj_t *mobj);
+void P_SpawnItemPattern(mapthing_t *mthing, boolean bonustime);
 void P_SpawnHoopOfSomething(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle);
 void P_SpawnPrecipitation(void);
 void P_SpawnParaloop(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, statenum_t nstate, angle_t rotangle, boolean spawncenter);
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index 040bdca2a92143b6353476701d5cb8c527427cba..36a9ba61356a07aec6ab7e118890ec33183c39a7 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by James Haley
-// Copyright (C) 2006-2018 by Sonic Team Junior.
+// Copyright (C) 2006-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -400,6 +400,8 @@ static void Polyobj_findSegs(polyobj_t *po, seg_t *seg)
 		// Find backfacings
 		for (s = 0;  s < numsegs; s++)
 		{
+			if (segs[s].glseg)
+				continue;
 			if (segs[s].linedef == seg->linedef
 				&& segs[s].side == 1)
 			{
@@ -436,6 +438,8 @@ newseg:
 	// seg's ending vertex.
 	for (i = 0; i < numsegs; ++i)
 	{
+		if (segs[i].glseg)
+			continue;
 		if (segs[i].side != 0) // needs to be frontfacing
 			continue;
 		if (segs[i].v1->x == seg->v2->x && segs[i].v1->y == seg->v2->y)
@@ -460,6 +464,9 @@ newseg:
 				// Find backfacings
 				for (q = 0;  q < numsegs; q++)
 				{
+					if (segs[q].glseg)
+						continue;
+
 					if (segs[q].linedef == segs[i].linedef
 						&& segs[q].side == 1)
 					{
@@ -606,6 +613,9 @@ static void Polyobj_spawnPolyObj(INT32 num, mobj_t *spawnSpot, INT32 id)
 		INT32 poflags = POF_SOLID|POF_TESTHEIGHT|POF_RENDERSIDES;
 		INT32 parentID = 0, potrans = 0;
 
+		if (seg->glseg)
+			continue;
+
 		if (seg->side != 0) // needs to be frontfacing
 			continue;
 
@@ -1815,6 +1825,7 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
 	if (po->thinker == NULL)
 		po->thinker = &th->thinker;
 
+/*
 	// Find out target first.
 	// We redo this each tic to make savegame compatibility easier.
 	for (wp = thlist[THINK_MOBJ].next; wp != &thlist[THINK_MOBJ]; wp = wp->next)
@@ -1833,6 +1844,9 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
 			break;
 		}
 	}
+*/
+
+	target = th->target;
 
 	if (!target)
 	{
@@ -2015,6 +2029,8 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
 
 			target = waypoint;
 			th->pointnum = target->health;
+			// Set the mobj as your target! -- Monster Iestyn 27/12/19
+			P_SetTarget(&th->target, target);
 
 			// calculate MOMX/MOMY/MOMZ for next waypoint
 			// change slope
@@ -2641,6 +2657,9 @@ INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *pwdata)
 
 	// Set pointnum
 	th->pointnum = target->health;
+	th->target = NULL; // set to NULL first so the below doesn't go wrong
+	// Set the mobj as your target! -- Monster Iestyn 27/12/19
+	P_SetTarget(&th->target, target);
 
 	// We don't deal with the mirror crap here, we'll
 	// handle that in the T_Thinker function.
diff --git a/src/p_polyobj.h b/src/p_polyobj.h
index 73a11d237fcaf611005c95154b3be6e671d728c8..339390c0af8eaa9a3f2f3bbda918d3bcf577bba7 100644
--- a/src/p_polyobj.h
+++ b/src/p_polyobj.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by James Haley
-// Copyright (C) 2006-2018 by Sonic Team Junior.
+// Copyright (C) 2006-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -161,6 +161,8 @@ typedef struct polywaypoint_s
 	fixed_t diffx;
 	fixed_t diffy;
 	fixed_t diffz;
+
+	mobj_t *target; // next waypoint mobj
 } polywaypoint_t;
 
 typedef struct polyslidedoor_s
diff --git a/src/p_pspr.h b/src/p_pspr.h
index c9c66b96133f3d86bf5bce442b3a6ee6657c2d5a..a7545bc3e32812979d8c424a201a1becd6ebf118 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_saveg.c b/src/p_saveg.c
index ff486eadbce942079c945b5fcb7d3b576f96d10f..2b6a474bf70999317e2567bf70cbfb0a39abce48 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -777,14 +777,13 @@ static void P_NetArchiveWorld(void)
 	size_t i;
 	INT32 statsec = 0, statline = 0;
 	const line_t *li = lines;
+	const line_t *spawnli = spawnlines;
 	const side_t *si;
+	const side_t *spawnsi;
 	UINT8 *put;
 
-	// reload the map just to see difference
-	mapsector_t *ms;
-	mapsidedef_t *msd;
-	maplinedef_t *mld;
 	const sector_t *ss = sectors;
+	const sector_t *spawnss = spawnsectors;
 	UINT8 diff, diff2, diff3;
 
 	// initialize colormap vars because paranoia
@@ -793,65 +792,45 @@ static void P_NetArchiveWorld(void)
 	WRITEUINT32(save_p, ARCHIVEBLOCK_WORLD);
 	put = save_p;
 
-	if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
-	{ // HACK: Open wad file rather quickly so we can get the data from the relevant lumps
-		UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
-		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
-#define retrieve_mapdata(d, f)\
-		d = Z_Malloc((f)->size, PU_CACHE, NULL); \
-		M_Memcpy(d, wadData + (f)->filepos, (f)->size)
-		retrieve_mapdata(ms, fileinfo + ML_SECTORS);
-		retrieve_mapdata(mld, fileinfo + ML_LINEDEFS);
-		retrieve_mapdata(msd, fileinfo + ML_SIDEDEFS);
-#undef retrieve_mapdata
-		Z_Free(wadData); // we're done with this now
-	}
-	else // phew it's just a WAD
-	{
-			ms = W_CacheLumpNum(lastloadedmaplumpnum+ML_SECTORS, PU_CACHE);
-			mld = W_CacheLumpNum(lastloadedmaplumpnum+ML_LINEDEFS, PU_CACHE);
-			msd = W_CacheLumpNum(lastloadedmaplumpnum+ML_SIDEDEFS, PU_CACHE);
-	}
-
-	for (i = 0; i < numsectors; i++, ss++, ms++)
+	for (i = 0; i < numsectors; i++, ss++, spawnss++)
 	{
 		diff = diff2 = diff3 = 0;
-		if (ss->floorheight != SHORT(ms->floorheight)<<FRACBITS)
+		if (ss->floorheight != spawnss->floorheight)
 			diff |= SD_FLOORHT;
-		if (ss->ceilingheight != SHORT(ms->ceilingheight)<<FRACBITS)
+		if (ss->ceilingheight != spawnss->ceilingheight)
 			diff |= SD_CEILHT;
 		//
 		// flats
 		//
-		if (ss->floorpic != P_CheckLevelFlat(ms->floorpic))
+		if (ss->floorpic != spawnss->floorpic)
 			diff |= SD_FLOORPIC;
-		if (ss->ceilingpic != P_CheckLevelFlat(ms->ceilingpic))
+		if (ss->ceilingpic != spawnss->ceilingpic)
 			diff |= SD_CEILPIC;
 
-		if (ss->lightlevel != SHORT(ms->lightlevel))
+		if (ss->lightlevel != spawnss->lightlevel)
 			diff |= SD_LIGHT;
-		if (ss->special != SHORT(ms->special))
+		if (ss->special != spawnss->special)
 			diff |= SD_SPECIAL;
 
-		if (ss->floor_xoffs != ss->spawn_flr_xoffs)
+		if (ss->floor_xoffs != spawnss->floor_xoffs)
 			diff2 |= SD_FXOFFS;
-		if (ss->floor_yoffs != ss->spawn_flr_yoffs)
+		if (ss->floor_yoffs != spawnss->floor_yoffs)
 			diff2 |= SD_FYOFFS;
-		if (ss->ceiling_xoffs != ss->spawn_ceil_xoffs)
+		if (ss->ceiling_xoffs != spawnss->ceiling_xoffs)
 			diff2 |= SD_CXOFFS;
-		if (ss->ceiling_yoffs != ss->spawn_ceil_yoffs)
+		if (ss->ceiling_yoffs != spawnss->ceiling_yoffs)
 			diff2 |= SD_CYOFFS;
-		if (ss->floorpic_angle != ss->spawn_flrpic_angle)
+		if (ss->floorpic_angle != spawnss->floorpic_angle)
 			diff2 |= SD_FLOORANG;
-		if (ss->ceilingpic_angle != ss->spawn_flrpic_angle)
+		if (ss->ceilingpic_angle != spawnss->ceilingpic_angle)
 			diff2 |= SD_CEILANG;
 
-		if (ss->tag != SHORT(ms->tag))
+		if (ss->tag != spawnss->tag)
 			diff2 |= SD_TAG;
-		if (ss->nexttag != ss->spawn_nexttag || ss->firsttag != ss->spawn_firsttag)
+		if (ss->nexttag != spawnss->nexttag || ss->firsttag != spawnss->firsttag)
 			diff3 |= SD_TAGLIST;
 
-		if (ss->extra_colormap != ss->spawn_extra_colormap)
+		if (ss->extra_colormap != spawnss->extra_colormap)
 			diff3 |= SD_COLORMAP;
 
 		// Check if any of the sector's FOFs differ from how they spawned
@@ -955,45 +934,41 @@ static void P_NetArchiveWorld(void)
 	WRITEUINT16(put, 0xffff);
 
 	// do lines
-	for (i = 0; i < numlines; i++, mld++, li++)
+	for (i = 0; i < numlines; i++, spawnli++, li++)
 	{
 		diff = diff2 = diff3 = 0;
 
-		if (li->special != SHORT(mld->special))
+		if (li->special != spawnli->special)
 			diff |= LD_SPECIAL;
 
-		if (SHORT(mld->special) == 321 || SHORT(mld->special) == 322) // only reason li->callcount would be non-zero is if either of these are involved
+		if (spawnli->special == 321 || spawnli->special == 322) // only reason li->callcount would be non-zero is if either of these are involved
 			diff |= LD_CLLCOUNT;
 
 		if (li->sidenum[0] != 0xffff)
 		{
 			si = &sides[li->sidenum[0]];
-			if (si->textureoffset != SHORT(msd[li->sidenum[0]].textureoffset)<<FRACBITS)
+			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 (R_CheckTextureNumForName(msd[li->sidenum[0]].toptexture) != -1
-				&& si->toptexture != R_TextureNumForName(msd[li->sidenum[0]].toptexture))
+			if (si->toptexture != spawnsi->toptexture)
 				diff |= LD_S1TOPTEX;
-			if (R_CheckTextureNumForName(msd[li->sidenum[0]].bottomtexture) != -1
-				&& si->bottomtexture != R_TextureNumForName(msd[li->sidenum[0]].bottomtexture))
+			if (si->bottomtexture != spawnsi->bottomtexture)
 				diff |= LD_S1BOTTEX;
-			if (R_CheckTextureNumForName(msd[li->sidenum[0]].midtexture) != -1
-				&& si->midtexture != R_TextureNumForName(msd[li->sidenum[0]].midtexture))
+			if (si->midtexture != spawnsi->midtexture)
 				diff |= LD_S1MIDTEX;
 		}
 		if (li->sidenum[1] != 0xffff)
 		{
 			si = &sides[li->sidenum[1]];
-			if (si->textureoffset != SHORT(msd[li->sidenum[1]].textureoffset)<<FRACBITS)
+			spawnsi = &spawnsides[li->sidenum[1]];
+			if (si->textureoffset != spawnsi->textureoffset)
 				diff2 |= LD_S2TEXOFF;
-			if (R_CheckTextureNumForName(msd[li->sidenum[1]].toptexture) != -1
-				&& si->toptexture != R_TextureNumForName(msd[li->sidenum[1]].toptexture))
+			if (si->toptexture != spawnsi->toptexture)
 				diff2 |= LD_S2TOPTEX;
-			if (R_CheckTextureNumForName(msd[li->sidenum[1]].bottomtexture) != -1
-				&& si->bottomtexture != R_TextureNumForName(msd[li->sidenum[1]].bottomtexture))
+			if (si->bottomtexture != spawnsi->bottomtexture)
 				diff2 |= LD_S2BOTTEX;
-			if (R_CheckTextureNumForName(msd[li->sidenum[1]].midtexture) != -1
-				&& si->midtexture != R_TextureNumForName(msd[li->sidenum[1]].midtexture))
+			if (si->midtexture != spawnsi->midtexture)
 				diff2 |= LD_S2MIDTEX;
 			if (diff2)
 				diff |= LD_DIFF2;
@@ -1277,9 +1252,7 @@ typedef enum
 	MD2_SLOPE        = 1<<11,
 #endif
 	MD2_COLORIZED    = 1<<12,
-#ifdef ROTSPRITE
 	MD2_ROLLANGLE    = 1<<13,
-#endif
 } mobj_diff2_t;
 
 typedef enum
@@ -1499,10 +1472,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 #endif
 	if (mobj->colorized)
 		diff2 |= MD2_COLORIZED;
-#ifdef ROTSPRITE
 	if (mobj->rollangle)
 		diff2 |= MD2_ROLLANGLE;
-#endif
 	if (diff2 != 0)
 		diff |= MD_MORE;
 
@@ -1667,10 +1638,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 #endif
 	if (diff2 & MD2_COLORIZED)
 		WRITEUINT8(save_p, mobj->colorized);
-#ifdef ROTSPRITE
 	if (diff2 & MD2_ROLLANGLE)
 		WRITEANGLE(save_p, mobj->rollangle);
-#endif
 
 	WRITEUINT32(save_p, mobj->mobjnum);
 }
@@ -2086,6 +2055,7 @@ static void SavePolywaypointThinker(const thinker_t *th, UINT8 type)
 	WRITEFIXED(save_p, ht->diffx);
 	WRITEFIXED(save_p, ht->diffy);
 	WRITEFIXED(save_p, ht->diffz);
+	WRITEUINT32(save_p, SaveMobjnum(ht->target));
 }
 
 //
@@ -2563,7 +2533,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 
 		if (mapthings[spawnpointnum].type == 1705 || mapthings[spawnpointnum].type == 1713) // NiGHTS Hoop special case
 		{
-			P_SpawnHoopsAndRings(&mapthings[spawnpointnum], false);
+			P_SpawnHoop(&mapthings[spawnpointnum]);
 			return NULL;
 		}
 
@@ -2747,12 +2717,8 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 #endif
 	if (diff2 & MD2_COLORIZED)
 		mobj->colorized = READUINT8(save_p);
-#ifdef ROTSPRITE
 	if (diff2 & MD2_ROLLANGLE)
 		mobj->rollangle = READANGLE(save_p);
-	else
-		mobj->rollangle = 0;
-#endif
 
 	if (diff & MD_REDFLAG)
 	{
@@ -3279,6 +3245,7 @@ static inline thinker_t* LoadPolywaypointThinker(actionf_p1 thinker)
 	ht->diffx = READFIXED(save_p);
 	ht->diffy = READFIXED(save_p);
 	ht->diffz = READFIXED(save_p);
+	ht->target = LoadMobj(READUINT32(save_p));
 	return &ht->thinker;
 }
 
@@ -3573,6 +3540,7 @@ static void P_NetUnArchiveThinkers(void)
 
 				case tc_polywaypoint:
 					th = LoadPolywaypointThinker((actionf_p1)T_PolyObjWaypoint);
+					restoreNum = true;
 					break;
 
 				case tc_polyslidedoor:
@@ -3634,9 +3602,9 @@ static void P_NetUnArchiveThinkers(void)
 	if (restoreNum)
 	{
 		executor_t *delay = NULL;
+		polywaypoint_t *polywp = NULL;
 		UINT32 mobjnum;
-		for (currentthinker = thlist[THINK_MAIN].next; currentthinker != &thlist[THINK_MAIN];
-		currentthinker = currentthinker->next)
+		for (currentthinker = thlist[THINK_MAIN].next; currentthinker != &thlist[THINK_MAIN]; currentthinker = currentthinker->next)
 		{
 			if (currentthinker->function.acp1 != (actionf_p1)T_ExecutorDelay)
 				continue;
@@ -3645,6 +3613,15 @@ static void P_NetUnArchiveThinkers(void)
 				continue;
 			delay->caller = P_FindNewPosition(mobjnum);
 		}
+		for (currentthinker = thlist[THINK_POLYOBJ].next; currentthinker != &thlist[THINK_POLYOBJ]; currentthinker = currentthinker->next)
+		{
+			if (currentthinker->function.acp1 != (actionf_p1)T_PolyObjWaypoint)
+				continue;
+			polywp = (void *)currentthinker;
+			if (!(mobjnum = (UINT32)(size_t)polywp->target))
+				continue;
+			polywp->target = P_FindNewPosition(mobjnum);
+		}
 	}
 }
 
@@ -4099,7 +4076,7 @@ static inline boolean P_NetUnArchiveMisc(void)
 
 	tokenlist = READUINT32(save_p);
 
-	if (!P_SetupLevel(true))
+	if (!P_LoadLevel(true))
 		return false;
 
 	// get the time
diff --git a/src/p_saveg.h b/src/p_saveg.h
index 2f5e88046a85d22aa84083d92444f2d7996e85ef..16d47bea84f6d665d9a6d73d66640e627fdff3d7 100644
--- a/src/p_saveg.h
+++ b/src/p_saveg.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_setup.c b/src/p_setup.c
index 5c792c73cec89ae2c928cf2eddc75eded1bb072d..c61f97d92c6885e5460d606dc2d9450d3a7a66ac 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -83,6 +83,8 @@
 #include "p_slopes.h"
 #endif
 
+#include "fastcmp.h" // textmap parsing
+
 //
 // Map MD5, calculated on level load.
 // Sent to clients in PT_SERVERINFO.
@@ -103,6 +105,9 @@ node_t *nodes;
 line_t *lines;
 side_t *sides;
 mapthing_t *mapthings;
+sector_t *spawnsectors;
+line_t *spawnlines;
+side_t *spawnsides;
 INT32 numstarposts;
 UINT16 bossdisabled;
 boolean stoppedclock;
@@ -210,6 +215,9 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->lvlttl[0] = '\0';
 	mapheaderinfo[num]->selectheading[0] = '\0';
 	mapheaderinfo[num]->subttl[0] = '\0';
+	mapheaderinfo[num]->ltzzpatch[0] = '\0';
+	mapheaderinfo[num]->ltzztext[0] = '\0';
+	mapheaderinfo[num]->ltactdiamond[0] = '\0';
 	mapheaderinfo[num]->actnum = 0;
 	mapheaderinfo[num]->typeoflevel = 0;
 	mapheaderinfo[num]->nextlevel = (INT16)(i + 1);
@@ -367,171 +375,6 @@ UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade)
 	return mapheaderinfo[map-1]->grades[mare].grade[grade-1];
 }
 
-/** Loads the vertexes for a level.
-  *
-  * \param lump VERTEXES lump number.
-  * \sa ML_VERTEXES
-  */
-
-static inline void P_LoadRawVertexes(UINT8 *data, size_t i)
-{
-	mapvertex_t *ml;
-	vertex_t *li;
-
-	numvertexes = i / sizeof (mapvertex_t);
-
-	if (numvertexes <= 0)
-		I_Error("Level has no vertices"); // instead of crashing
-
-	// Allocate zone memory for buffer.
-	vertexes = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
-
-	ml = (mapvertex_t *)data;
-	li = vertexes;
-
-	// Copy and convert vertex coordinates, internal representation as fixed.
-	for (i = 0; i < numvertexes; i++, li++, ml++)
-	{
-		li->x = SHORT(ml->x)<<FRACBITS;
-		li->y = SHORT(ml->y)<<FRACBITS;
-	}
-}
-
-static inline void P_LoadVertexes(lumpnum_t lumpnum)
-{
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_LoadRawVertexes(data, W_LumpLength(lumpnum));
-	Z_Free(data);
-}
-
-/** Computes the length of a seg in fracunits.
-  *
-  * \param seg Seg to compute length for.
-  * \return Length in fracunits.
-  */
-fixed_t P_SegLength(seg_t *seg)
-{
-	INT64 dx = (seg->v2->x - seg->v1->x)>>1;
-	INT64 dy = (seg->v2->y - seg->v1->y)>>1;
-	return FixedHypot(dx, dy)<<1;
-}
-
-#ifdef HWRENDER
-/** Computes the length of a seg as a float.
-  * This is needed for OpenGL.
-  *
-  * \param seg Seg to compute length for.
-  * \return Length as a float.
-  */
-static inline float P_SegLengthFloat(seg_t *seg)
-{
-	float dx, dy;
-
-	// make a vector (start at origin)
-	dx = FIXED_TO_FLOAT(seg->v2->x - seg->v1->x);
-	dy = FIXED_TO_FLOAT(seg->v2->y - seg->v1->y);
-
-	return (float)hypot(dx, dy);
-}
-#endif
-
-/** Loads the SEGS resource from a level.
-  *
-  * \param lump Lump number of the SEGS resource.
-  * \sa ::ML_SEGS
-  */
-static void P_LoadRawSegs(UINT8 *data, size_t i)
-{
-	INT32 linedef, side;
-	mapseg_t *ml;
-	seg_t *li;
-	line_t *ldef;
-
-	numsegs = i / sizeof (mapseg_t);
-	if (numsegs <= 0)
-		I_Error("Level has no segs"); // instead of crashing
-	segs = Z_Calloc(numsegs * sizeof (*segs), PU_LEVEL, NULL);
-
-	ml = (mapseg_t *)data;
-	li = segs;
-	for (i = 0; i < numsegs; i++, li++, ml++)
-	{
-		li->v1 = &vertexes[SHORT(ml->v1)];
-		li->v2 = &vertexes[SHORT(ml->v2)];
-
-		li->length = P_SegLength(li);
-#ifdef HWRENDER
-		if (rendermode == render_opengl)
-		{
-			li->flength = P_SegLengthFloat(li);
-			//Hurdler: 04/12/2000: for now, only used in hardware mode
-			li->lightmaps = NULL; // list of static lightmap for this seg
-		}
-		li->pv1 = li->pv2 = NULL;
-#endif
-
-		li->angle = (SHORT(ml->angle))<<FRACBITS;
-		li->offset = (SHORT(ml->offset))<<FRACBITS;
-		linedef = SHORT(ml->linedef);
-		ldef = &lines[linedef];
-		li->linedef = ldef;
-		li->side = side = SHORT(ml->side);
-		li->sidedef = &sides[ldef->sidenum[side]];
-		li->frontsector = sides[ldef->sidenum[side]].sector;
-		if (ldef-> flags & ML_TWOSIDED)
-			li->backsector = sides[ldef->sidenum[side^1]].sector;
-		else
-			li->backsector = 0;
-
-		li->numlights = 0;
-		li->rlights = NULL;
-	}
-}
-
-static void P_LoadSegs(lumpnum_t lumpnum)
-{
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_LoadRawSegs(data, W_LumpLength(lumpnum));
-	Z_Free(data);
-}
-
-
-/** Loads the SSECTORS resource from a level.
-  *
-  * \param lump Lump number of the SSECTORS resource.
-  * \sa ::ML_SSECTORS
-  */
-static inline void P_LoadRawSubsectors(void *data, size_t i)
-{
-	mapsubsector_t *ms;
-	subsector_t *ss;
-
-	numsubsectors = i / sizeof (mapsubsector_t);
-	if (numsubsectors <= 0)
-		I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
-	ss = subsectors = Z_Calloc(numsubsectors * sizeof (*subsectors), PU_LEVEL, NULL);
-
-	ms = (mapsubsector_t *)data;
-
-	for (i = 0; i < numsubsectors; i++, ss++, ms++)
-	{
-		ss->sector = NULL;
-		ss->numlines = SHORT(ms->numsegs);
-		ss->firstline = SHORT(ms->firstseg);
-#ifdef FLOORSPLATS
-		ss->splats = NULL;
-#endif
-		ss->validcount = 0;
-	}
-}
-
-static void P_LoadSubsectors(lumpnum_t lumpnum)
-{
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_LoadRawSubsectors(data, W_LumpLength(lumpnum));
-	Z_Free(data);
-}
-
 //
 // levelflats
 //
@@ -539,6 +382,7 @@ static void P_LoadSubsectors(lumpnum_t lumpnum)
 
 size_t numlevelflats;
 levelflat_t *levelflats;
+levelflat_t *foundflats;
 
 //SoM: Other files want this info.
 size_t P_PrecacheLevelFlats(void)
@@ -568,7 +412,9 @@ or NULL if we want to allocate it now.
 static INT32
 Ploadflat (levelflat_t *levelflat, const char *flatname)
 {
+#ifndef NO_PNG_LUMPS
 	UINT8         buffer[8];
+#endif
 
 	lumpnum_t    flatnum;
 	int       texturenum;
@@ -670,7 +516,7 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat)
 //
 INT32 P_AddLevelFlatRuntime(const char *flatname)
 {
-	return Ploadflat(0, flatname);
+	return Ploadflat(levelflats, flatname);
 }
 
 // help function for $$$.sav checking
@@ -695,163 +541,9 @@ INT32 P_CheckLevelFlat(const char *flatname)
 	return (INT32)i;
 }
 
-// Sets up the ingame sectors structures.
-// Lumpnum is the lumpnum of a SECTORS lump.
-static void P_LoadRawSectors(UINT8 *data, size_t i)
-{
-	mapsector_t *ms;
-	sector_t *ss;
-	levelflat_t *foundflats;
-
-	// We count how many sectors we got.
-	numsectors = i / sizeof (mapsector_t);
-	if (numsectors <= 0)
-		I_Error("Level has no sectors");
-
-	// Allocate as much memory as we need into the global sectors table.
-	sectors = Z_Calloc(numsectors*sizeof (*sectors), PU_LEVEL, NULL);
-
-	// Allocate a big chunk of memory as big as our MAXLEVELFLATS limit.
-	//Fab : FIXME: allocate for whatever number of flats - 512 different flats per level should be plenty
-	foundflats = calloc(MAXLEVELFLATS, sizeof (*foundflats));
-	if (foundflats == NULL)
-		I_Error("Ran out of memory while loading sectors\n");
-
-	numlevelflats = 0;
-
-	// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
-	ms = (mapsector_t *)data;
-	ss = sectors;
-	for (i = 0; i < numsectors; i++, ss++, ms++)
-	{
-		ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
-		ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
-
-		ss->floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
-		ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
-
-		ss->lightlevel = SHORT(ms->lightlevel);
-		ss->spawn_lightlevel = SHORT(ms->lightlevel);
-		ss->special = SHORT(ms->special);
-		ss->tag = SHORT(ms->tag);
-		ss->nexttag = ss->firsttag = -1;
-		ss->spawn_nexttag = ss->spawn_firsttag = -1;
-
-		memset(&ss->soundorg, 0, sizeof(ss->soundorg));
-		ss->validcount = 0;
-
-		ss->thinglist = NULL;
-		ss->touching_thinglist = NULL;
-		ss->preciplist = NULL;
-		ss->touching_preciplist = NULL;
-
-		ss->floordata = NULL;
-		ss->ceilingdata = NULL;
-		ss->lightingdata = NULL;
-
-		ss->linecount = 0;
-		ss->lines = NULL;
-
-		ss->heightsec = -1;
-		ss->camsec = -1;
-		ss->floorlightsec = -1;
-		ss->ceilinglightsec = -1;
-		ss->crumblestate = 0;
-		ss->ffloors = NULL;
-		ss->lightlist = NULL;
-		ss->numlights = 0;
-		ss->attached = NULL;
-		ss->attachedsolid = NULL;
-		ss->numattached = 0;
-		ss->maxattached = 1;
-		ss->moved = true;
-
-		ss->extra_colormap = NULL;
-		ss->spawn_extra_colormap = NULL;
-
-		ss->floor_xoffs = ss->ceiling_xoffs = ss->floor_yoffs = ss->ceiling_yoffs = 0;
-		ss->spawn_flr_xoffs = ss->spawn_ceil_xoffs = ss->spawn_flr_yoffs = ss->spawn_ceil_yoffs = 0;
-		ss->floorpic_angle = ss->ceilingpic_angle = 0;
-		ss->spawn_flrpic_angle = ss->spawn_ceilpic_angle = 0;
-		ss->gravity = NULL;
-		ss->cullheight = NULL;
-		ss->verticalflip = false;
-		ss->flags = 0;
-		ss->flags |= SF_FLIPSPECIAL_FLOOR;
-
-		ss->floorspeed = 0;
-		ss->ceilspeed = 0;
-
-#ifdef HWRENDER // ----- for special tricks with HW renderer -----
-		ss->pseudoSector = false;
-		ss->virtualFloor = false;
-		ss->virtualCeiling = false;
-		ss->sectorLines = NULL;
-		ss->stackList = NULL;
-		ss->lineoutLength = -1.0l;
-#endif // ----- end special tricks -----
-	}
-
-	// set the sky flat num
-	skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
-
-	// copy table for global usage
-	levelflats = M_Memcpy(Z_Calloc(numlevelflats * sizeof (*levelflats), PU_LEVEL, NULL), foundflats, numlevelflats * sizeof (levelflat_t));
-	free(foundflats);
-
-	// search for animated flats and set up
-	P_SetupLevelFlatAnims();
-}
-
-static void P_LoadSectors(lumpnum_t lumpnum)
-{
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_LoadRawSectors(data, W_LumpLength(lumpnum));
-	Z_Free(data);
-}
-
-//
-// P_LoadNodes
-//
-static void P_LoadRawNodes(UINT8 *data, size_t i)
-{
-	UINT8 j, k;
-	mapnode_t *mn;
-	node_t *no;
-
-	numnodes = i / sizeof (mapnode_t);
-	if (numnodes <= 0)
-		I_Error("Level has no nodes");
-	nodes = Z_Calloc(numnodes * sizeof (*nodes), PU_LEVEL, NULL);
-
-	mn = (mapnode_t *)data;
-	no = nodes;
-
-	for (i = 0; i < numnodes; i++, no++, mn++)
-	{
-		no->x = SHORT(mn->x)<<FRACBITS;
-		no->y = SHORT(mn->y)<<FRACBITS;
-		no->dx = SHORT(mn->dx)<<FRACBITS;
-		no->dy = SHORT(mn->dy)<<FRACBITS;
-		for (j = 0; j < 2; j++)
-		{
-			no->children[j] = SHORT(mn->children[j]);
-			for (k = 0; k < 4; k++)
-				no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
-		}
-	}
-}
-
-static void P_LoadNodes(lumpnum_t lumpnum)
-{
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_LoadRawNodes(data, W_LumpLength(lumpnum));
-	Z_Free(data);
-}
-
 //
 // P_ReloadRings
-// Used by NiGHTS, clears all ring/wing/etc items and respawns them
+// Used by NiGHTS, clears all ring/sphere/hoop/etc items and respawns them
 //
 void P_ReloadRings(void)
 {
@@ -898,22 +590,21 @@ void P_ReloadRings(void)
 	{
 		// Notice an omission? We handle hoops differently.
 		if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum
-		 || mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
-		 || mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum
-		 || (mt->type >= 600 && mt->type <= 609)) // circles and diagonals
+			|| mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
+			|| mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum)
 		{
 			mt->mobj = NULL;
-
-			// Z for objects Tails 05-26-2002
-			mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)
-				->sector->floorheight>>FRACBITS);
-
-			P_SpawnHoopsAndRings(mt, true);
+			P_SetBonusTime(P_SpawnMapThing(mt));
+		}
+		else if (mt->type >= 600 && mt->type <= 609) // Item patterns
+		{
+			mt->mobj = NULL;
+			P_SpawnItemPattern(mt, true);
 		}
 	}
 	for (i = 0; i < numHoops; i++)
 	{
-		P_SpawnHoopsAndRings(hoopsToRespawn[i], false);
+		P_SpawnHoop(hoopsToRespawn[i]);
 	}
 }
 
@@ -1004,27 +695,59 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
 }
 #endif
 
-static void P_PrepareRawThings(UINT8 *data, size_t i)
+static void P_SpawnEmeraldHunt(void)
 {
-	mapthing_t *mt;
+	INT32 emer1, emer2, emer3;
+	INT32 timeout = 0; // keeps from getting stuck
 
-	nummapthings = i / (5 * sizeof (INT16));
-	mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), PU_LEVEL, NULL);
+	emer1 = emer2 = emer3 = 0;
 
-	// Spawn axis points first so they are
-	// at the front of the list for fast searching.
-	mt = mapthings;
-	for (i = 0; i < nummapthings; i++, mt++)
+	//increment spawn numbers because zero is valid.
+	emer1 = (P_RandomKey(numhuntemeralds)) + 1;
+	while (timeout++ < 100)
 	{
-		mt->x = READINT16(data);
-		mt->y = READINT16(data);
-		mt->angle = READINT16(data);
-		mt->type = READUINT16(data);
-		mt->options = READUINT16(data);
-		mt->extrainfo = (UINT8)(mt->type >> 12);
+		emer2 = (P_RandomKey(numhuntemeralds)) + 1;
 
-		mt->type &= 4095;
+		if (emer2 != emer1)
+			break;
+	}
+
+	timeout = 0;
+	while (timeout++ < 100)
+	{
+		emer3 = (P_RandomKey(numhuntemeralds)) + 1;
+
+		if (emer3 != emer2 && emer3 != emer1)
+			break;
+	}
 
+	//decrement spawn values to the actual number because zero is valid.
+	if (emer1--)
+		P_SpawnMobj(huntemeralds[emer1]->x<<FRACBITS,
+			huntemeralds[emer1]->y<<FRACBITS,
+			huntemeralds[emer1]->z<<FRACBITS, MT_EMERHUNT);
+
+	if (emer2--)
+		P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer2]->x<<FRACBITS,
+			huntemeralds[emer2]->y<<FRACBITS,
+			huntemeralds[emer2]->z<<FRACBITS, MT_EMERHUNT),
+		mobjinfo[MT_EMERHUNT].spawnstate+1);
+
+	if (emer3--)
+		P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer3]->x<<FRACBITS,
+			huntemeralds[emer3]->y<<FRACBITS,
+			huntemeralds[emer3]->z<<FRACBITS, MT_EMERHUNT),
+		mobjinfo[MT_EMERHUNT].spawnstate+2);
+}
+
+static void P_SpawnMapThings(boolean spawnemblems)
+{
+	size_t i;
+	mapthing_t *mt;
+
+        // Spawn axis points first so they are at the front of the list for fast searching.
+	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
+	{
 		switch (mt->type)
 		{
 			case 1700: // MT_AXIS
@@ -1037,115 +760,32 @@ static void P_PrepareRawThings(UINT8 *data, size_t i)
 				break;
 		}
 	}
-}
-
-static void P_PrepareThings(lumpnum_t lumpnum)
-{
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_PrepareRawThings(data, W_LumpLength(lumpnum));
-	Z_Free(data);
-}
-
-static void P_LoadThings(boolean loademblems)
-{
-	size_t i;
-	mapthing_t *mt;
 
-	// Loading the things lump itself into memory is now handled in P_PrepareThings, above
-
-	mt = mapthings;
 	numhuntemeralds = 0;
-	for (i = 0; i < nummapthings; i++, mt++)
-	{
-		sector_t *mtsector = R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)->sector;
-
-		// Z for objects
-		mt->z = (INT16)(
-#ifdef ESLOPE
-				mtsector->f_slope ? P_GetZAt(mtsector->f_slope, mt->x << FRACBITS, mt->y << FRACBITS) :
-#endif
-				mtsector->floorheight)>>FRACBITS;
 
+	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
+	{
 		if (mt->type == 1700 // MT_AXIS
 			|| mt->type == 1701 // MT_AXISTRANSFER
 			|| mt->type == 1702) // MT_AXISTRANSFERLINE
 			continue; // These were already spawned
 
-		if (!loademblems && mt->type == mobjinfo[MT_EMBLEM].doomednum)
+		if (!spawnemblems && mt->type == mobjinfo[MT_EMBLEM].doomednum)
 			continue;
 
 		mt->mobj = NULL;
-		P_SpawnMapThing(mt);
+
+		if (mt->type >= 600 && mt->type <= 609) // item patterns
+			P_SpawnItemPattern(mt, false);
+		else if (mt->type == 1705 || mt->type == 1713) // hoops
+			P_SpawnHoop(mt);
+		else // Everything else
+			P_SpawnMapThing(mt);
 	}
 
 	// random emeralds for hunt
 	if (numhuntemeralds)
-	{
-		INT32 emer1, emer2, emer3;
-		INT32 timeout = 0; // keeps from getting stuck
-
-		emer1 = emer2 = emer3 = 0;
-
-		//increment spawn numbers because zero is valid.
-		emer1 = (P_RandomKey(numhuntemeralds)) + 1;
-		while (timeout++ < 100)
-		{
-			emer2 = (P_RandomKey(numhuntemeralds)) + 1;
-
-			if (emer2 != emer1)
-				break;
-		}
-
-		timeout = 0;
-		while (timeout++ < 100)
-		{
-			emer3 = (P_RandomKey(numhuntemeralds)) + 1;
-
-			if (emer3 != emer2 && emer3 != emer1)
-				break;
-		}
-
-		//decrement spawn values to the actual number because zero is valid.
-		if (emer1--)
-			P_SpawnMobj(huntemeralds[emer1]->x<<FRACBITS,
-				huntemeralds[emer1]->y<<FRACBITS,
-				huntemeralds[emer1]->z<<FRACBITS, MT_EMERHUNT);
-
-		if (emer2--)
-			P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer2]->x<<FRACBITS,
-				huntemeralds[emer2]->y<<FRACBITS,
-				huntemeralds[emer2]->z<<FRACBITS, MT_EMERHUNT),
-			mobjinfo[MT_EMERHUNT].spawnstate+1);
-
-		if (emer3--)
-			P_SetMobjStateNF(P_SpawnMobj(huntemeralds[emer3]->x<<FRACBITS,
-				huntemeralds[emer3]->y<<FRACBITS,
-				huntemeralds[emer3]->z<<FRACBITS, MT_EMERHUNT),
-			mobjinfo[MT_EMERHUNT].spawnstate+2);
-	}
-
-	if (metalrecording) // Metal Sonic gets no rings to distract him.
-		return;
-
-	// Run through the list of mapthings again to spawn hoops and rings
-	mt = mapthings;
-	for (i = 0; i < nummapthings; i++, mt++)
-	{
-		if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum
-		 || mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
-		 || mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum
-		 || (mt->type >= 600 && mt->type <= 609) // circles and diagonals
-		 || mt->type == 1705 || mt->type == 1713) // hoops
-		{
-			mt->mobj = NULL;
-
-			// Z for objects Tails 05-26-2002
-			mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)
-				->sector->floorheight>>FRACBITS);
-
-			P_SpawnHoopsAndRings(mt, false);
-		}
-	}
+		P_SpawnEmeraldHunt();
 }
 
 // Experimental groovy write function!
@@ -1191,270 +831,296 @@ void P_WriteThings(lumpnum_t lumpnum)
 	CONS_Printf(M_GetText("newthings%d.lmp saved.\n"), gamemap);
 }
 
-static void P_LoadRawLineDefs(UINT8 *data, size_t i)
-{
-	maplinedef_t *mld;
-	line_t *ld;
-	vertex_t *v1, *v2;
+//
+// MAP LOADING FUNCTIONS
+//
 
-	numlines = i / sizeof (maplinedef_t);
-	if (numlines <= 0)
-		I_Error("Level has no linedefs");
-	lines = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
+static void P_LoadVertices(UINT8 *data)
+{
+	mapvertex_t *mv = (mapvertex_t *)data;
+	vertex_t *v = vertexes;
+	size_t i;
 
-	mld = (maplinedef_t *)data;
-	ld = lines;
-	for (i = 0; i < numlines; i++, mld++, ld++)
+	// Copy and convert vertex coordinates, internal representation as fixed.
+	for (i = 0; i < numvertexes; i++, v++, mv++)
 	{
-		ld->flags = SHORT(mld->flags);
-		ld->special = SHORT(mld->special);
-		ld->tag = SHORT(mld->tag);
-		v1 = ld->v1 = &vertexes[SHORT(mld->v1)];
-		v2 = ld->v2 = &vertexes[SHORT(mld->v2)];
-		ld->dx = v2->x - v1->x;
-		ld->dy = v2->y - v1->y;
+		v->x = SHORT(mv->x)<<FRACBITS;
+		v->y = SHORT(mv->y)<<FRACBITS;
+	}
+}
 
-#ifdef WALLSPLATS
-		ld->splats = NULL;
-#endif
+static void P_InitializeSector(sector_t *ss)
+{
+	ss->nexttag = ss->firsttag = -1;
 
-		if (!ld->dx)
-			ld->slopetype = ST_VERTICAL;
-		else if (!ld->dy)
-			ld->slopetype = ST_HORIZONTAL;
-		else if ((ld->dy > 0) == (ld->dx > 0))
-			ld->slopetype = ST_POSITIVE;
-		else
-			ld->slopetype = ST_NEGATIVE;
+	memset(&ss->soundorg, 0, sizeof(ss->soundorg));
 
-		if (v1->x < v2->x)
-		{
-			ld->bbox[BOXLEFT] = v1->x;
-			ld->bbox[BOXRIGHT] = v2->x;
-		}
-		else
-		{
-			ld->bbox[BOXLEFT] = v2->x;
-			ld->bbox[BOXRIGHT] = v1->x;
-		}
+	ss->validcount = 0;
 
-		if (v1->y < v2->y)
-		{
-			ld->bbox[BOXBOTTOM] = v1->y;
-			ld->bbox[BOXTOP] = v2->y;
-		}
-		else
-		{
-			ld->bbox[BOXBOTTOM] = v2->y;
-			ld->bbox[BOXTOP] = v1->y;
-		}
+	ss->thinglist = NULL;
 
-		ld->sidenum[0] = SHORT(mld->sidenum[0]);
-		ld->sidenum[1] = SHORT(mld->sidenum[1]);
+	ss->floordata = NULL;
+	ss->ceilingdata = NULL;
+	ss->lightingdata = NULL;
+	ss->fadecolormapdata = NULL;
 
-		{
-			// cph 2006/09/30 - fix sidedef errors right away.
-			// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
-			UINT8 j;
+	ss->heightsec = -1;
+	ss->camsec = -1;
 
-			for (j=0; j < 2; j++)
-			{
-				if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
-				{
-					ld->sidenum[j] = 0xffff;
-					CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s has out-of-range sidedef number\n", sizeu1(numlines-i-1));
-				}
-			}
-		}
+	ss->floorlightsec = ss->ceilinglightsec = -1;
+	ss->crumblestate = 0;
 
-		ld->frontsector = ld->backsector = NULL;
-		ld->validcount = 0;
-		ld->firsttag = ld->nexttag = -1;
-		ld->callcount = 0;
-		// killough 11/98: fix common wad errors (missing sidedefs):
+	ss->touching_thinglist = NULL;
 
-		if (ld->sidenum[0] == 0xffff)
-		{
-			ld->sidenum[0] = 0;  // Substitute dummy sidedef for missing right side
-			// cph - print a warning about the bug
-			CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s missing first sidedef\n", sizeu1(numlines-i-1));
-		}
+	ss->linecount = 0;
+	ss->lines = NULL;
+	ss->tagline = NULL;
 
-		if ((ld->sidenum[1] == 0xffff) && (ld->flags & ML_TWOSIDED))
-		{
-			ld->flags &= ~ML_TWOSIDED;  // Clear 2s flag for missing left side
-			// cph - print a warning about the bug
-			CONS_Debug(DBG_SETUP, "P_LoadRawLineDefs: linedef %s has two-sided flag set, but no second sidedef\n", sizeu1(numlines-i-1));
-		}
+	ss->ffloors = NULL;
+	ss->attached = NULL;
+	ss->attachedsolid = NULL;
+	ss->numattached = 0;
+	ss->maxattached = 1;
+	ss->lightlist = NULL;
+	ss->numlights = 0;
+	ss->moved = true;
 
-		if (ld->sidenum[0] != 0xffff && ld->special)
-			sides[ld->sidenum[0]].special = ld->special;
-		if (ld->sidenum[1] != 0xffff && ld->special)
-			sides[ld->sidenum[1]].special = ld->special;
+	ss->extra_colormap = NULL;
 
-#ifdef POLYOBJECTS
-		ld->polyobj = NULL;
+#ifdef HWRENDER // ----- for special tricks with HW renderer -----
+	ss->pseudoSector = false;
+	ss->virtualFloor = false;
+	ss->virtualFloorheight = 0;
+	ss->virtualCeiling = false;
+	ss->virtualCeilingheight = 0;
+	ss->sectorLines = NULL;
+	ss->stackList = NULL;
+	ss->lineoutLength = -1.0l;
+#endif // ----- end special tricks -----
+
+	ss->gravity = NULL;
+	ss->verticalflip = false;
+	ss->flags = SF_FLIPSPECIAL_FLOOR;
+
+	ss->cullheight = NULL;
+
+	ss->floorspeed = ss->ceilspeed = 0;
+
+	ss->preciplist = NULL;
+	ss->touching_preciplist = NULL;
+
+#ifdef ESLOPE
+	ss->f_slope = NULL;
+	ss->c_slope = NULL;
+	ss->hasslope = false;
 #endif
-	}
-}
 
-static void P_LoadLineDefs(lumpnum_t lumpnum)
-{
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_LoadRawLineDefs(data, W_LumpLength(lumpnum));
-	Z_Free(data);
+	ss->spawn_lightlevel = ss->lightlevel;
+
+	ss->spawn_extra_colormap = NULL;
 }
 
-static void P_LoadLineDefs2(void)
+static void P_LoadSectors(UINT8 *data)
 {
-	size_t i = numlines;
-	register line_t *ld = lines;
-	for (;i--;ld++)
+	mapsector_t *ms = (mapsector_t *)data;
+	sector_t *ss = sectors;
+	size_t i;
+
+	// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
+	for (i = 0; i < numsectors; i++, ss++, ms++)
 	{
-		ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
-		ld->backsector  = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
+		ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
+		ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
 
-		// Repeat count for midtexture
-		if ((ld->flags & ML_EFFECT5) && (ld->sidenum[1] != 0xffff)
-			&& !(ld->special >= 300 && ld->special < 500)) // exempt linedef exec specials
-		{
-			sides[ld->sidenum[0]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) >> 12);
-			sides[ld->sidenum[0]].textureoffset = (((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
-			sides[ld->sidenum[1]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) >> 12);
-			sides[ld->sidenum[1]].textureoffset = (((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
-		}
+		ss->floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
+		ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
 
-		// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
-		switch(ld->special)
-		{
-		case 331: // Trigger linedef executor: Skin - Continuous
-		case 332: // Trigger linedef executor: Skin - Each time
-		case 333: // Trigger linedef executor: Skin - Once
-		case 443: // Calls a named Lua function
-			if (sides[ld->sidenum[0]].text)
-			{
-				size_t len = strlen(sides[ld->sidenum[0]].text)+1;
-				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
-					len += strlen(sides[ld->sidenum[1]].text);
-				ld->text = Z_Malloc(len, PU_LEVEL, NULL);
-				M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text)+1);
-				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
-					M_Memcpy(ld->text+strlen(ld->text)+1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text)+1);
-			}
-			break;
-		}
-	}
+		ss->lightlevel = SHORT(ms->lightlevel);
+		ss->special = SHORT(ms->special);
+		ss->tag = SHORT(ms->tag);
 
-	// Optimize sidedefs
-	if (M_CheckParm("-compress"))
-	{
-		side_t *newsides;
-		size_t numnewsides = 0;
-		size_t z;
+		ss->floor_xoffs = ss->floor_yoffs = 0;
+		ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
 
-		for (i = 0; i < numsides; i++)
-		{
-			size_t j, k;
-			if (sides[i].sector == NULL)
-				continue;
+		ss->floorpic_angle = ss->ceilingpic_angle = 0;
 
-			for (k = numlines, ld = lines; k--; ld++)
-			{
-				if (ld->sidenum[0] == i)
-					ld->sidenum[0] = (UINT16)numnewsides;
+		P_InitializeSector(ss);
+	}
+}
 
-				if (ld->sidenum[1] == i)
-					ld->sidenum[1] = (UINT16)numnewsides;
-			}
+static void P_InitializeLinedef(line_t *ld)
+{
+	vertex_t *v1 = ld->v1;
+	vertex_t *v2 = ld->v2;
+	UINT8 j;
 
-			for (j = i+1; j < numsides; j++)
-			{
-				if (sides[j].sector == NULL)
-					continue;
+	ld->dx = v2->x - v1->x;
+	ld->dy = v2->y - v1->y;
 
-				if (!memcmp(&sides[i], &sides[j], sizeof(side_t)))
-				{
-					// Find the linedefs that belong to this one
-					for (k = numlines, ld = lines; k--; ld++)
-					{
-						if (ld->sidenum[0] == j)
-							ld->sidenum[0] = (UINT16)numnewsides;
+	ld->bbox[BOXLEFT] = min(v1->x, v2->x);
+	ld->bbox[BOXRIGHT] = max(v1->x, v2->x);
+	ld->bbox[BOXBOTTOM] = min(v1->y, v2->y);
+	ld->bbox[BOXTOP] = max(v1->y, v2->y);
 
-						if (ld->sidenum[1] == j)
-							ld->sidenum[1] = (UINT16)numnewsides;
-					}
-					sides[j].sector = NULL; // Flag for deletion
-				}
-			}
-			numnewsides++;
-		}
+	if (!ld->dx)
+		ld->slopetype = ST_VERTICAL;
+	else if (!ld->dy)
+		ld->slopetype = ST_HORIZONTAL;
+	else if ((ld->dy > 0) == (ld->dx > 0))
+		ld->slopetype = ST_POSITIVE;
+	else
+		ld->slopetype = ST_NEGATIVE;
+
+	ld->frontsector = ld->backsector = NULL;
+
+	ld->validcount = 0;
+#ifdef WALLSPLATS
+	ld->splats = NULL;
+#endif
+	ld->firsttag = ld->nexttag = -1;
+#ifdef POLYOBJECTS
+	ld->polyobj = NULL;
+#endif
 
-		// We're loading crap into this block anyhow, so no point in zeroing it out.
-		newsides = Z_Malloc(numnewsides * sizeof(*newsides), PU_LEVEL, NULL);
+	ld->text = NULL;
+	ld->callcount = 0;
 
-		// Copy the sides to their new block of memory.
-		for (i = 0, z = 0; i < numsides; i++)
+	// cph 2006/09/30 - fix sidedef errors right away.
+	// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
+	for (j = 0; j < 2; j++)
+		if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
 		{
-			if (sides[i].sector != NULL)
-				M_Memcpy(&newsides[z++], &sides[i], sizeof(side_t));
+			ld->sidenum[j] = 0xffff;
+			CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has out-of-range sidedef number\n", sizeu1((size_t)(ld - lines)));
 		}
 
-		CONS_Debug(DBG_SETUP, "Old sides is %s, new sides is %s\n", sizeu1(numsides), sizeu1(numnewsides));
+	// killough 11/98: fix common wad errors (missing sidedefs):
+	if (ld->sidenum[0] == 0xffff)
+	{
+		ld->sidenum[0] = 0;  // Substitute dummy sidedef for missing right side
+		// cph - print a warning about the bug
+		CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s missing first sidedef\n", sizeu1((size_t)(ld - lines)));
+	}
+
+	if ((ld->sidenum[1] == 0xffff) && (ld->flags & ML_TWOSIDED))
+	{
+		ld->flags &= ~ML_TWOSIDED;  // Clear 2s flag for missing left side
+		// cph - print a warning about the bug
+		CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has two-sided flag set, but no second sidedef\n", sizeu1((size_t)(ld - lines)));
+	}
 
-		Z_Free(sides);
-		sides = newsides;
-		numsides = numnewsides;
+	if (ld->sidenum[0] != 0xffff)
+	{
+		sides[ld->sidenum[0]].special = ld->special;
+		sides[ld->sidenum[0]].line = ld;
+	}
+	if (ld->sidenum[1] != 0xffff)
+	{
+		sides[ld->sidenum[1]].special = ld->special;
+		sides[ld->sidenum[1]].line = ld;
 	}
 }
 
+static void P_SetLinedefV1(size_t i, UINT16 vertex_num)
+{
+	if (vertex_num >= numvertexes)
+	{
+		CONS_Debug(DBG_SETUP, "P_SetLinedefV1: linedef %s has out-of-range v1 num %u\n", sizeu1(i), vertex_num);
+		vertex_num = 0;
+	}
+	lines[i].v1 = &vertexes[vertex_num];
+}
 
+static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
+{
+	if (vertex_num >= numvertexes)
+	{
+		CONS_Debug(DBG_SETUP, "P_SetLinedefV2: linedef %s has out-of-range v2 num %u\n", sizeu1(i), vertex_num);
+		vertex_num = 0;
+	}
+	lines[i].v2 = &vertexes[vertex_num];
+}
 
-static inline void P_LoadRawSideDefs(size_t i)
+static void P_LoadLinedefs(UINT8 *data)
 {
-	numsides = i / sizeof (mapsidedef_t);
-	if (numsides <= 0)
-		I_Error("Level has no sidedefs");
-	sides = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
+	maplinedef_t *mld = (maplinedef_t *)data;
+	line_t *ld = lines;
+	size_t i;
+
+	for (i = 0; i < numlines; i++, mld++, ld++)
+	{
+		ld->flags = SHORT(mld->flags);
+		ld->special = SHORT(mld->special);
+		ld->tag = SHORT(mld->tag);
+		P_SetLinedefV1(i, SHORT(mld->v1));
+		P_SetLinedefV2(i, SHORT(mld->v2));
+
+		ld->sidenum[0] = SHORT(mld->sidenum[0]);
+		ld->sidenum[1] = SHORT(mld->sidenum[1]);
+
+		P_InitializeLinedef(ld);
+	}
 }
 
-static inline void P_LoadSideDefs(lumpnum_t lumpnum)
+static void P_SetSidedefSector(size_t i, UINT16 sector_num)
 {
-	P_LoadRawSideDefs(W_LumpLength(lumpnum));
+	// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
+	if (sector_num >= numsectors)
+	{
+		CONS_Debug(DBG_SETUP, "P_SetSidedefSector: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
+		sector_num = 0;
+	}
+	sides[i].sector = &sectors[sector_num];
 }
 
+static void P_InitializeSidedef(side_t *sd)
+{
+	if (!sd->line)
+	{
+		CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1((size_t)(sd - sides)));
+		sd->line = &lines[0];
+		sd->special = sd->line->special;
+	}
+
+	sd->text = NULL;
+	sd->colormap_data = NULL;
+}
 
-static void P_LoadRawSideDefs2(void *data)
+static void P_LoadSidedefs(UINT8 *data)
 {
-	UINT16 i;
+	mapsidedef_t *msd = (mapsidedef_t*)data;
+	side_t *sd = sides;
+	size_t i;
 
-	for (i = 0; i < numsides; i++)
+	for (i = 0; i < numsides; i++, sd++, msd++)
 	{
-		register mapsidedef_t *msd = (mapsidedef_t *)data + i;
-		register side_t *sd = sides + i;
-		register sector_t *sec;
+		INT16 textureoffset = SHORT(msd->textureoffset);
+		boolean isfrontside;
 
-		sd->textureoffset = SHORT(msd->textureoffset)<<FRACBITS;
-		sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
+		P_InitializeSidedef(sd);
 
-		{ /* cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead */
-			UINT16 sector_num = SHORT(msd->sector);
+		isfrontside = sd->line->sidenum[0] == i;
 
-			if (sector_num >= numsectors)
-			{
-				CONS_Debug(DBG_SETUP, "P_LoadRawSideDefs2: sidedef %u has out-of-range sector num %u\n", i, sector_num);
-				sector_num = 0;
-			}
-			sd->sector = sec = &sectors[sector_num];
+		// Repeat count for midtexture
+		if (((sd->line->flags & (ML_TWOSIDED|ML_EFFECT5)) == (ML_TWOSIDED|ML_EFFECT5))
+			&& !(sd->special >= 300 && sd->special < 500)) // exempt linedef exec specials
+		{
+			sd->repeatcnt = (INT16)(((unsigned)textureoffset) >> 12);
+			sd->textureoffset = (((unsigned)textureoffset) & 2047) << FRACBITS;
 		}
+		else
+		{
+			sd->repeatcnt = 0;
+			sd->textureoffset = textureoffset << FRACBITS;
+		}
+		sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
 
-		sd->sector = sec = &sectors[SHORT(msd->sector)];
-
-		sd->colormap_data = NULL;
+		P_SetSidedefSector(i, SHORT(msd->sector));
 
-		// Colormaps!
+		// Special info stored in texture fields!
 		switch (sd->special)
 		{
-			case 63: // variable colormap via 242 linedef
+			case 63: // Fake floor/ceiling planes
 			case 606: //SoM: 4/4/2000: Just colormap transfer
 			case 447: // Change colormap of tagged sectors! -- Monster Iestyn 14/06/18
 			case 455: // Fade colormaps! mazmazz 9/12/2018 (:flag_us:)
@@ -1484,9 +1150,8 @@ static void P_LoadRawSideDefs2(void *data)
 					sd->midtexture = get_number(process);
 				}
 
-				// always process if back sidedef, because we need that - symbol
  				sd->text = Z_Malloc(7, PU_LEVEL, NULL);
-				if (i == 1 || msd->toptexture[0] != '-' || msd->toptexture[1] != '\0')
+				if (isfrontside && !(msd->toptexture[0] == '-' && msd->toptexture[1] == '\0'))
 				{
 					M_Memcpy(process,msd->toptexture,8);
 					process[8] = '\0';
@@ -1563,34 +1228,1030 @@ static void P_LoadRawSideDefs2(void *data)
 				break;
 			}
 
-			default: // normal cases
-				if (msd->toptexture[0] == '#')
-				{
-					char *col = msd->toptexture;
-					sd->toptexture = sd->bottomtexture =
-						((col[1]-'0')*100 + (col[2]-'0')*10 + col[3]-'0') + 1;
-					sd->midtexture = R_TextureNumForName(msd->midtexture);
-				}
-				else
-				{
-					sd->midtexture = R_TextureNumForName(msd->midtexture);
-					sd->toptexture = R_TextureNumForName(msd->toptexture);
-					sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
-				}
-				break;
+			case 259: // Custom FOF
+				if (!isfrontside)
+				{
+					if ((msd->toptexture[0] >= '0' && msd->toptexture[0] <= '9')
+						|| (msd->toptexture[0] >= 'A' && msd->toptexture[0] <= 'F'))
+						sd->toptexture = axtoi(msd->toptexture);
+					else
+						I_Error("Custom FOF (tag %d) needs a value in the linedef's back side upper texture field.", sd->line->tag);
+
+					sd->midtexture = R_TextureNumForName(msd->midtexture);
+					sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
+					break;
+				}
+				// FALLTHRU
+			default: // normal cases
+				if (msd->toptexture[0] == '#')
+				{
+					char *col = msd->toptexture;
+					sd->toptexture = sd->bottomtexture =
+						((col[1]-'0')*100 + (col[2]-'0')*10 + col[3]-'0') + 1;
+					sd->midtexture = R_TextureNumForName(msd->midtexture);
+				}
+				else
+				{
+					sd->midtexture = R_TextureNumForName(msd->midtexture);
+					sd->toptexture = R_TextureNumForName(msd->toptexture);
+					sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
+				}
+				break;
+		}
+	}
+}
+
+static void P_LoadThings(UINT8 *data)
+{
+	mapthing_t *mt;
+	size_t i;
+
+	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
+	{
+		mt->x = READINT16(data);
+		mt->y = READINT16(data);
+
+		mt->angle = READINT16(data);
+		mt->type = READUINT16(data);
+		mt->options = READUINT16(data);
+		mt->extrainfo = (UINT8)(mt->type >> 12);
+
+		mt->type &= 4095;
+
+		if (mt->type == 1705 || (mt->type == 750 && mt->extrainfo))
+			mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
+		else
+			mt->z = mt->options >> ZSHIFT;
+
+		mt->mobj = NULL;
+	}
+}
+
+// Stores positions for relevant map data spread through a TEXTMAP.
+UINT32 mapthingsPos[UINT16_MAX];
+UINT32 linesPos[UINT16_MAX];
+UINT32 sidesPos[UINT16_MAX];
+UINT32 vertexesPos[UINT16_MAX];
+UINT32 sectorsPos[UINT16_MAX];
+
+// Determine total amount of map data in TEXTMAP.
+static boolean TextmapCount(UINT8 *data, size_t size)
+{
+	char *tkn = M_GetToken((char *)data);
+	UINT8 brackets = 0;
+
+	nummapthings = 0;
+	numlines = 0;
+	numsides = 0;
+	numvertexes = 0;
+	numsectors = 0;
+
+	// Look for namespace at the beginning.
+	if (!fastcmp(tkn, "namespace"))
+	{
+		Z_Free(tkn);
+		CONS_Alert(CONS_ERROR, "No namespace at beginning of lump!\n");
+		return false;
+	}
+	Z_Free(tkn);
+
+	// Check if namespace is valid.
+	tkn = M_GetToken(NULL);
+	if (!fastcmp(tkn, "srb2"))
+		CONS_Alert(CONS_WARNING, "Invalid namespace '%s', only 'srb2' is supported.\n", tkn);
+	Z_Free(tkn);
+
+	tkn = M_GetToken(NULL);
+	while (tkn && M_GetTokenPos() < size)
+	{
+		// Avoid anything inside bracketed stuff, only look for external keywords.
+		if (brackets)
+		{
+			if (fastcmp(tkn, "}"))
+				brackets--;
+		}
+		else if (fastcmp(tkn, "{"))
+			brackets++;
+		// Check for valid fields.
+		else if (fastcmp(tkn, "thing"))
+			mapthingsPos[nummapthings++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "linedef"))
+			linesPos[numlines++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "sidedef"))
+			sidesPos[numsides++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "vertex"))
+			vertexesPos[numvertexes++] = M_GetTokenPos();
+		else if (fastcmp(tkn, "sector"))
+			sectorsPos[numsectors++] = M_GetTokenPos();
+		else
+			CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
+
+		Z_Free(tkn);
+		tkn = M_GetToken(NULL);
+	}
+
+	Z_Free(tkn);
+
+	if (brackets)
+	{
+		CONS_Alert(CONS_ERROR, "Unclosed brackets detected in textmap lump.\n");
+		return false;
+	}
+
+	return true;
+}
+
+static void ParseTextmapVertexParameter(UINT32 i, char *param, char *val)
+{
+	if (fastcmp(param, "x"))
+		vertexes[i].x = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "y"))
+		vertexes[i].y = FLOAT_TO_FIXED(atof(val));
+}
+
+static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
+{
+	if (fastcmp(param, "heightfloor"))
+		sectors[i].floorheight = atol(val) << FRACBITS;
+	else if (fastcmp(param, "heightceiling"))
+		sectors[i].ceilingheight = atol(val) << FRACBITS;
+	if (fastcmp(param, "texturefloor"))
+		sectors[i].floorpic = P_AddLevelFlat(val, foundflats);
+	else if (fastcmp(param, "textureceiling"))
+		sectors[i].ceilingpic = P_AddLevelFlat(val, foundflats);
+	else if (fastcmp(param, "lightlevel"))
+		sectors[i].lightlevel = atol(val);
+	else if (fastcmp(param, "special"))
+		sectors[i].special = atol(val);
+	else if (fastcmp(param, "id"))
+		sectors[i].tag = atol(val);
+	else if (fastcmp(param, "xpanningfloor"))
+		sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "ypanningfloor"))
+		sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "xpanningceiling"))
+		sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "ypanningceiling"))
+		sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "rotationfloor"))
+		sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
+	else if (fastcmp(param, "rotationceiling"))
+		sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
+}
+
+static void ParseTextmapSidedefParameter(UINT32 i, char *param, char *val)
+{
+	if (fastcmp(param, "offsetx"))
+		sides[i].textureoffset = atol(val)<<FRACBITS;
+	else if (fastcmp(param, "offsety"))
+		sides[i].rowoffset = atol(val)<<FRACBITS;
+	else if (fastcmp(param, "texturetop"))
+		sides[i].toptexture = R_TextureNumForName(val);
+	else if (fastcmp(param, "texturebottom"))
+		sides[i].bottomtexture = R_TextureNumForName(val);
+	else if (fastcmp(param, "texturemiddle"))
+		sides[i].midtexture = R_TextureNumForName(val);
+	else if (fastcmp(param, "sector"))
+		P_SetSidedefSector(i, atol(val));
+	else if (fastcmp(param, "repeatcnt"))
+		sides[i].repeatcnt = atol(val);
+}
+
+static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
+{
+	if (fastcmp(param, "id"))
+		lines[i].tag = atol(val);
+	else if (fastcmp(param, "special"))
+		lines[i].special = atol(val);
+	else if (fastcmp(param, "v1"))
+		P_SetLinedefV1(i, atol(val));
+	else if (fastcmp(param, "v2"))
+		P_SetLinedefV2(i, atol(val));
+	else if (fastcmp(param, "sidefront"))
+		lines[i].sidenum[0] = atol(val);
+	else if (fastcmp(param, "sideback"))
+		lines[i].sidenum[1] = atol(val);
+
+	// Flags
+	else if (fastcmp(param, "blocking") && fastcmp("true", val))
+		lines[i].flags |= ML_IMPASSIBLE;
+	else if (fastcmp(param, "blockmonsters") && fastcmp("true", val))
+		lines[i].flags |= ML_BLOCKMONSTERS;
+	else if (fastcmp(param, "twosided") && fastcmp("true", val))
+		lines[i].flags |= ML_TWOSIDED;
+	else if (fastcmp(param, "dontpegtop") && fastcmp("true", val))
+		lines[i].flags |= ML_DONTPEGTOP;
+	else if (fastcmp(param, "dontpegbottom") && fastcmp("true", val))
+		lines[i].flags |= ML_DONTPEGBOTTOM;
+	else if (fastcmp(param, "skewtd") && fastcmp("true", val))
+		lines[i].flags |= ML_EFFECT1;
+	else if (fastcmp(param, "noclimb") && fastcmp("true", val))
+		lines[i].flags |= ML_NOCLIMB;
+	else if (fastcmp(param, "noskew") && fastcmp("true", val))
+		lines[i].flags |= ML_EFFECT2;
+	else if (fastcmp(param, "midpeg") && fastcmp("true", val))
+		lines[i].flags |= ML_EFFECT3;
+	else if (fastcmp(param, "midsolid") && fastcmp("true", val))
+		lines[i].flags |= ML_EFFECT4;
+	else if (fastcmp(param, "wrapmidtex") && fastcmp("true", val))
+		lines[i].flags |= ML_EFFECT5;
+	else if (fastcmp(param, "effect6") && fastcmp("true", val))
+		lines[i].flags |= ML_EFFECT6;
+	else if (fastcmp(param, "nonet") && fastcmp("true", val))
+		lines[i].flags |= ML_NONET;
+	else if (fastcmp(param, "netonly") && fastcmp("true", val))
+		lines[i].flags |= ML_NETONLY;
+	else if (fastcmp(param, "bouncy") && fastcmp("true", val))
+		lines[i].flags |= ML_BOUNCY;
+	else if (fastcmp(param, "transfer") && fastcmp("true", val))
+		lines[i].flags |= ML_TFERLINE;
+}
+
+static void ParseTextmapThingParameter(UINT32 i, char *param, char *val)
+{
+	if (fastcmp(param, "x"))
+		mapthings[i].x = atol(val);
+	else if (fastcmp(param, "y"))
+		mapthings[i].y = atol(val);
+	else if (fastcmp(param, "height"))
+		mapthings[i].z = atol(val);
+	else if (fastcmp(param, "angle"))
+		mapthings[i].angle = atol(val);
+	else if (fastcmp(param, "type"))
+		mapthings[i].type = atol(val);
+
+	// Flags
+	else if (fastcmp(param, "extra") && fastcmp("true", val))
+		mapthings[i].options |= MTF_EXTRA;
+	else if (fastcmp(param, "flip") && fastcmp("true", val))
+		mapthings[i].options |= MTF_OBJECTFLIP;
+	else if (fastcmp(param, "special") && fastcmp("true", val))
+		mapthings[i].options |= MTF_OBJECTSPECIAL;
+	else if (fastcmp(param, "ambush") && fastcmp("true", val))
+		mapthings[i].options |= MTF_AMBUSH;
+}
+
+/** From a given position table, run a specified parser function through a {}-encapsuled text.
+  *
+  * \param Position of the data to parse, in the textmap.
+  * \param Structure number (mapthings, sectors, ...).
+  * \param Parser function pointer.
+  */
+static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *, char *))
+{
+	char *param, *val;
+
+	M_SetTokenPos(dataPos);
+	param = M_GetToken(NULL);
+	if (!fastcmp(param, "{"))
+	{
+		Z_Free(param);
+		CONS_Alert(CONS_WARNING, "Invalid UDMF data capsule!\n");
+		return;
+	}
+	Z_Free(param);
+
+	while (true)
+	{
+		param = M_GetToken(NULL);
+		if (fastcmp(param, "}"))
+		{
+			Z_Free(param);
+			break;
+		}
+		val = M_GetToken(NULL);
+		parser(num, param, val);
+		Z_Free(param);
+		Z_Free(val);
+	}
+}
+
+/** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
+ */
+static void TextmapFixFlatOffsets(sector_t *sec)
+{
+	if (sec->floorpic_angle)
+	{
+		fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->floor_xoffs;
+		fixed_t yoffs = sec->floor_yoffs;
+		sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+	}
+
+	if (sec->ceilingpic_angle)
+	{
+		fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+		fixed_t ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
+		fixed_t xoffs = sec->ceiling_xoffs;
+		fixed_t yoffs = sec->ceiling_yoffs;
+		sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
+		sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
+	}
+}
+
+/** Loads the textmap data, after obtaining the elements count and allocating their respective space.
+  */
+static void P_LoadTextmap(void)
+{
+	UINT32 i;
+
+	vertex_t   *vt;
+	sector_t   *sc;
+	line_t     *ld;
+	side_t     *sd;
+	mapthing_t *mt;
+
+	CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
+
+	/// Given the UDMF specs, some fields are given a default value.
+	/// If an element's field has a default value set, it is omitted
+	/// from the textmap, and therefore we have to account for it by
+	/// preemptively setting that value beforehand.
+
+	for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
+	{
+		// Defaults.
+		vt->x = vt->y = INT32_MAX;
+
+		TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
+
+		if (vt->x == INT32_MAX)
+			I_Error("P_LoadTextmap: vertex %s has no x value set!\n", sizeu1(i));
+		if (vt->y == INT32_MAX)
+			I_Error("P_LoadTextmap: vertex %s has no y value set!\n", sizeu1(i));
+	}
+
+	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
+	{
+		// Defaults.
+		sc->floorheight = 0;
+		sc->ceilingheight = 0;
+
+		sc->floorpic = 0;
+		sc->ceilingpic = 0;
+
+		sc->lightlevel = 255;
+
+		sc->special = 0;
+		sc->tag = 0;
+
+		sc->floor_xoffs = sc->floor_yoffs = 0;
+		sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
+
+		sc->floorpic_angle = sc->ceilingpic_angle = 0;
+
+		TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
+		P_InitializeSector(sc);
+		TextmapFixFlatOffsets(sc);
+	}
+
+	for (i = 0, ld = lines; i < numlines; i++, ld++)
+	{
+		// Defaults.
+		ld->v1 = ld->v2 = NULL;
+		ld->flags = 0;
+		ld->special = 0;
+		ld->tag = 0;
+		ld->sidenum[0] = 0xffff;
+		ld->sidenum[1] = 0xffff;
+
+		TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
+
+		if (!ld->v1)
+			I_Error("P_LoadTextmap: linedef %s has no v1 value set!\n", sizeu1(i));
+		if (!ld->v2)
+			I_Error("P_LoadTextmap: linedef %s has no v2 value set!\n", sizeu1(i));
+		if (ld->sidenum[0] == 0xffff)
+			I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i));
+
+		P_InitializeLinedef(ld);
+	}
+
+	for (i = 0, sd = sides; i < numsides; i++, sd++)
+	{
+		// Defaults.
+		sd->textureoffset = 0;
+		sd->rowoffset = 0;
+		sd->toptexture = R_TextureNumForName("-");
+		sd->midtexture = R_TextureNumForName("-");
+		sd->bottomtexture = R_TextureNumForName("-");
+		sd->sector = NULL;
+		sd->repeatcnt = 0;
+
+		TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
+
+		if (!sd->sector)
+			I_Error("P_LoadTextmap: sidedef %s has no sector value set!\n", sizeu1(i));
+
+		P_InitializeSidedef(sd);
+	}
+
+	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
+	{
+		// Defaults.
+		mt->x = mt->y = 0;
+		mt->angle = 0;
+		mt->type = 0;
+		mt->options = 0;
+		mt->z = 0;
+		mt->extrainfo = 0;
+		mt->mobj = NULL;
+
+		TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
+	}
+}
+
+static void P_ProcessLinedefsAfterSidedefs(void)
+{
+	size_t i = numlines;
+	register line_t *ld = lines;
+	for (; i--; ld++)
+	{
+		ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
+		ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
+
+		// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
+		switch (ld->special)
+		{
+		case 331: // Trigger linedef executor: Skin - Continuous
+		case 332: // Trigger linedef executor: Skin - Each time
+		case 333: // Trigger linedef executor: Skin - Once
+		case 443: // Calls a named Lua function
+			if (sides[ld->sidenum[0]].text)
+			{
+				size_t len = strlen(sides[ld->sidenum[0]].text) + 1;
+				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
+					len += strlen(sides[ld->sidenum[1]].text);
+				ld->text = Z_Malloc(len, PU_LEVEL, NULL);
+				M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text) + 1);
+				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
+					M_Memcpy(ld->text + strlen(ld->text) + 1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text) + 1);
+			}
+			break;
+		}
+	}
+}
+
+static boolean P_LoadMapData(const virtres_t *virt)
+{
+	virtlump_t *virtvertexes = NULL, *virtsectors = NULL, *virtsidedefs = NULL, *virtlinedefs = NULL, *virtthings = NULL;
+	virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
+
+	// Count map data.
+	if (textmap) // Count how many entries for each type we got in textmap.
+	{
+		if (!TextmapCount(textmap->data, textmap->size))
+			return false;
+	}
+	else
+	{
+		virtthings   = vres_Find(virt, "THINGS");
+		virtvertexes = vres_Find(virt, "VERTEXES");
+		virtsectors  = vres_Find(virt, "SECTORS");
+		virtsidedefs = vres_Find(virt, "SIDEDEFS");
+		virtlinedefs = vres_Find(virt, "LINEDEFS");
+
+		if (!virtthings)
+			I_Error("THINGS lump not found");
+		if (!virtvertexes)
+			I_Error("VERTEXES lump not found");
+		if (!virtsectors)
+			I_Error("SECTORS lump not found");
+		if (!virtsidedefs)
+			I_Error("SIDEDEFS lump not found");
+		if (!virtlinedefs)
+			I_Error("LINEDEFS lump not found");
+
+		// Traditional doom map format just assumes the number of elements from the lump sizes.
+		numvertexes  = virtvertexes->size / sizeof (mapvertex_t);
+		numsectors   = virtsectors->size  / sizeof (mapsector_t);
+		numsides     = virtsidedefs->size / sizeof (mapsidedef_t);
+		numlines     = virtlinedefs->size / sizeof (maplinedef_t);
+		nummapthings = virtthings->size   / (5 * sizeof (INT16));
+	}
+
+	if (numvertexes <= 0)
+		I_Error("Level has no vertices");
+	if (numsectors <= 0)
+		I_Error("Level has no sectors");
+	if (numsides <= 0)
+		I_Error("Level has no sidedefs");
+	if (numlines <= 0)
+		I_Error("Level has no linedefs");
+
+	vertexes  = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
+	sectors   = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
+	sides     = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
+	lines     = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
+	mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), PU_LEVEL, NULL);
+
+	// Allocate a big chunk of memory as big as our MAXLEVELFLATS limit.
+	//Fab : FIXME: allocate for whatever number of flats - 512 different flats per level should be plenty
+	foundflats = calloc(MAXLEVELFLATS, sizeof (*foundflats));
+	if (foundflats == NULL)
+		I_Error("Ran out of memory while loading sectors\n");
+
+	numlevelflats = 0;
+
+	// Load map data.
+	if (textmap)
+		P_LoadTextmap();
+	else
+	{
+		P_LoadVertices(virtvertexes->data);
+		P_LoadSectors(virtsectors->data);
+		P_LoadLinedefs(virtlinedefs->data);
+		P_LoadSidedefs(virtsidedefs->data);
+		P_LoadThings(virtthings->data);
+	}
+
+	P_ProcessLinedefsAfterSidedefs();
+
+	R_ClearTextureNumCache(true);
+
+	// set the sky flat num
+	skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
+
+	// copy table for global usage
+	levelflats = M_Memcpy(Z_Calloc(numlevelflats * sizeof (*levelflats), PU_LEVEL, NULL), foundflats, numlevelflats * sizeof (levelflat_t));
+	free(foundflats);
+
+	// search for animated flats and set up
+	P_SetupLevelFlatAnims();
+
+	return true;
+}
+
+static void P_InitializeSubsector(subsector_t *ss)
+{
+	ss->sector = NULL;
+#ifdef FLOORSPLATS
+	ss->splats = NULL;
+#endif
+	ss->validcount = 0;
+}
+
+static inline void P_LoadSubsectors(UINT8 *data)
+{
+	mapsubsector_t *ms = (mapsubsector_t*)data;
+	subsector_t *ss = subsectors;
+	size_t i;
+
+	for (i = 0; i < numsubsectors; i++, ss++, ms++)
+	{
+		ss->numlines = SHORT(ms->numsegs);
+		ss->firstline = SHORT(ms->firstseg);
+		P_InitializeSubsector(ss);
+	}
+}
+
+static void P_LoadNodes(UINT8 *data)
+{
+	UINT8 j, k;
+	mapnode_t *mn = (mapnode_t*)data;
+	node_t *no = nodes;
+	size_t i;
+
+	for (i = 0; i < numnodes; i++, no++, mn++)
+	{
+		no->x = SHORT(mn->x)<<FRACBITS;
+		no->y = SHORT(mn->y)<<FRACBITS;
+		no->dx = SHORT(mn->dx)<<FRACBITS;
+		no->dy = SHORT(mn->dy)<<FRACBITS;
+		for (j = 0; j < 2; j++)
+		{
+			no->children[j] = SHORT(mn->children[j]);
+			for (k = 0; k < 4; k++)
+				no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
+		}
+	}
+}
+
+/** Computes the length of a seg in fracunits.
+  *
+  * \param seg Seg to compute length for.
+  * \return Length in fracunits.
+  */
+fixed_t P_SegLength(seg_t *seg)
+{
+	INT64 dx = (seg->v2->x - seg->v1->x)>>1;
+	INT64 dy = (seg->v2->y - seg->v1->y)>>1;
+	return FixedHypot(dx, dy)<<1;
+}
+
+#ifdef HWRENDER
+/** Computes the length of a seg as a float.
+  * This is needed for OpenGL.
+  *
+  * \param seg Seg to compute length for.
+  * \return Length as a float.
+  */
+static inline float P_SegLengthFloat(seg_t *seg)
+{
+	float dx, dy;
+
+	// make a vector (start at origin)
+	dx = FIXED_TO_FLOAT(seg->v2->x - seg->v1->x);
+	dy = FIXED_TO_FLOAT(seg->v2->y - seg->v1->y);
+
+	return (float)hypot(dx, dy);
+}
+#endif
+
+static void P_InitializeSeg(seg_t *seg)
+{
+	if (seg->linedef)
+	{
+		seg->sidedef = &sides[seg->linedef->sidenum[seg->side]];
+
+		seg->frontsector = seg->sidedef->sector;
+		seg->backsector = (seg->linedef->flags & ML_TWOSIDED) ? sides[seg->linedef->sidenum[seg->side ^ 1]].sector : NULL;
+	}
+
+#ifdef HWRENDER
+	seg->pv1 = seg->pv2 = NULL;
+
+	//Hurdler: 04/12/2000: for now, only used in hardware mode
+	seg->lightmaps = NULL; // list of static lightmap for this seg
+#endif
+
+	seg->numlights = 0;
+	seg->rlights = NULL;
+#ifdef POLYOBJECTS
+	seg->polyseg = NULL;
+	seg->dontrenderme = false;
+#endif
+}
+
+static void P_LoadSegs(UINT8 *data)
+{
+	mapseg_t *ms = (mapseg_t*)data;
+	seg_t *seg = segs;
+	size_t i;
+
+	for (i = 0; i < numsegs; i++, seg++, ms++)
+	{
+		seg->v1 = &vertexes[SHORT(ms->v1)];
+		seg->v2 = &vertexes[SHORT(ms->v2)];
+
+		seg->side = SHORT(ms->side);
+
+		seg->offset = (SHORT(ms->offset)) << FRACBITS;
+
+		seg->angle = (SHORT(ms->angle)) << FRACBITS;
+
+		seg->linedef = &lines[SHORT(ms->linedef)];
+
+		seg->length = P_SegLength(seg);
+#ifdef HWRENDER
+		seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0;
+#endif
+
+		seg->glseg = false;
+		P_InitializeSeg(seg);
+	}
+}
+
+typedef enum {
+	NT_DOOM,
+	NT_XNOD,
+	NT_ZNOD,
+	NT_XGLN,
+	NT_ZGLN,
+	NT_XGL2,
+	NT_ZGL2,
+	NT_XGL3,
+	NT_ZGL3,
+	NT_UNSUPPORTED,
+	NUMNODETYPES
+} nodetype_t;
+
+// Find out the BSP format.
+static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
+{
+	boolean supported[NUMNODETYPES] = {0};
+	nodetype_t nodetype = NT_UNSUPPORTED;
+	char signature[4 + 1];
+
+	if (vres_Find(virt, "TEXTMAP"))
+	{
+		*nodedata = vres_Find(virt, "ZNODES")->data;
+		supported[NT_XGLN] = supported[NT_XGL3] = true;
+	}
+	else
+	{
+		virtlump_t *virtsegs = vres_Find(virt, "SEGS");
+		virtlump_t *virtssectors;
+
+		if (virtsegs && virtsegs->size)
+		{
+			*nodedata = vres_Find(virt, "NODES")->data;
+			return NT_DOOM; // Traditional map format BSP tree.
+		}
+
+		virtssectors = vres_Find(virt, "SSECTORS");
+
+		if (virtssectors && virtssectors->size)
+		{ // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature.
+			*nodedata = virtssectors->data;
+			supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true;
+		}
+		else
+		{ // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature.
+			*nodedata = vres_Find(virt, "NODES")->data;
+			supported[NT_XNOD] = supported[NT_ZNOD] = true;
+		}
+	}
+
+	M_Memcpy(signature, *nodedata, 4);
+	signature[4] = '\0';
+	(*nodedata) += 4;
+
+	if (!strcmp(signature, "XNOD"))
+		nodetype = NT_XNOD;
+	else if (!strcmp(signature, "ZNOD"))
+		nodetype = NT_ZNOD;
+	else if (!strcmp(signature, "XGLN"))
+		nodetype = NT_XGLN;
+	else if (!strcmp(signature, "ZGLN"))
+		nodetype = NT_ZGLN;
+	else if (!strcmp(signature, "XGL3"))
+		nodetype = NT_XGL3;
+
+	return supported[nodetype] ? nodetype : NT_UNSUPPORTED;
+}
+
+// Extended node formats feature additional vertices; useful for OpenGL, but totally useless in gamelogic.
+static boolean P_LoadExtraVertices(UINT8 **data)
+{
+	UINT32 origvrtx = READUINT32((*data));
+	UINT32 xtrvrtx = READUINT32((*data));
+	line_t* ld = lines;
+	vertex_t *oldpos = vertexes;
+	ssize_t offset;
+	size_t i;
+
+	if (numvertexes != origvrtx) // If native vertex count doesn't match node original vertex count, bail out (broken data?).
+	{
+		CONS_Alert(CONS_WARNING, "Vertex count in map data and nodes differ!\n");
+		return false;
+	}
+
+	if (!xtrvrtx)
+		return true;
+
+	// If extra vertexes were generated, reallocate the vertex array and fix the pointers.
+	numvertexes += xtrvrtx;
+	vertexes = Z_Realloc(vertexes, numvertexes*sizeof(*vertexes), PU_LEVEL, NULL);
+	offset = (size_t)(vertexes - oldpos);
+
+	for (i = 0, ld = lines; i < numlines; i++, ld++)
+	{
+		ld->v1 += offset;
+		ld->v2 += offset;
+	}
+
+	// Read extra vertex data.
+	for (i = origvrtx; i < numvertexes; i++)
+	{
+		vertexes[i].x = READFIXED((*data));
+		vertexes[i].y = READFIXED((*data));
+	}
+
+	return true;
+}
+
+static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype)
+{
+	size_t i, k;
+	INT16 m;
+	seg_t *seg;
+
+	// Subsectors
+	numsubsectors = READUINT32((*data));
+	subsectors = Z_Calloc(numsubsectors*sizeof(*subsectors), PU_LEVEL, NULL);
+
+	for (i = 0; i < numsubsectors; i++)
+		subsectors[i].numlines = READUINT32((*data));
+
+	// Segs
+	numsegs = READUINT32((*data));
+	segs = Z_Calloc(numsegs*sizeof(*segs), PU_LEVEL, NULL);
+
+	for (i = 0, k = 0; i < numsubsectors; i++)
+	{
+		subsectors[i].firstline = k;
+		P_InitializeSubsector(&subsectors[i]);
+
+		switch (nodetype)
+		{
+		case NT_XGLN:
+		case NT_XGL3:
+			for (m = 0; m < subsectors[i].numlines; m++, k++)
+			{
+				UINT32 vertexnum = READUINT32((*data));
+				UINT16 linenum;
+
+				if (vertexnum >= numvertexes)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid vertex %d!\n", sizeu1(k), m, vertexnum);
+
+				segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
+
+				READUINT32((*data)); // partner, can be ignored by software renderer
+				if (nodetype == NT_XGL3)
+					READUINT16((*data)); // Line number is 32-bit in XGL3, but we're limited to 16 bits.
+
+				linenum = READUINT16((*data));
+				if (linenum != 0xFFFF && linenum >= numlines)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
+				segs[k].glseg = (linenum == 0xFFFF);
+				segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum];
+				segs[k].side = READUINT8((*data));
+			}
+			break;
+
+		case NT_XNOD:
+			for (m = 0; m < subsectors[i].numlines; m++, k++)
+			{
+				UINT32 v1num = READUINT32((*data));
+				UINT32 v2num = READUINT32((*data));
+				UINT16 linenum = READUINT16((*data));
+
+				if (v1num >= numvertexes)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v1 %d!\n", sizeu1(k), m, v1num);
+				if (v2num >= numvertexes)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v2 %d!\n", sizeu1(k), m, v2num);
+				if (linenum >= numlines)
+					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
+
+				segs[k].v1 = &vertexes[v1num];
+				segs[k].v2 = &vertexes[v2num];
+				segs[k].linedef = &lines[linenum];
+				segs[k].side = READUINT8((*data));
+				segs[k].glseg = false;
+			}
+			break;
+
+		default:
+			return false;
 		}
 	}
-	R_ClearTextureNumCache(true);
+
+	for (i = 0, seg = segs; i < numsegs; i++, seg++)
+	{
+		vertex_t *v1 = seg->v1;
+		vertex_t *v2 = seg->v2;
+		P_InitializeSeg(seg);
+		seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
+		if (seg->linedef)
+			segs[i].offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
+	}
+
+	return true;
 }
 
-// Delay loading texture names until after loaded linedefs.
-static void P_LoadSideDefs2(lumpnum_t lumpnum)
+// Auxiliary function: Shrink node ID from 32-bit to 16-bit.
+static UINT16 ShrinkNodeID(UINT32 x) {
+	UINT16 mask = (x >> 16) & 0xC000;
+	UINT16 result = x;
+	return result | mask;
+}
+
+static void P_LoadExtendedNodes(UINT8 **data, nodetype_t nodetype)
 {
-	UINT8 *data = W_CacheLumpNum(lumpnum, PU_STATIC);
-	P_LoadRawSideDefs2(data);
-	Z_Free(data);
+	node_t *mn;
+	size_t i, j, k;
+	boolean xgl3 = (nodetype == NT_XGL3);
+
+	numnodes = READINT32((*data));
+	nodes = Z_Calloc(numnodes*sizeof(*nodes), PU_LEVEL, NULL);
+
+	for (i = 0, mn = nodes; i < numnodes; i++, mn++)
+	{
+		// Splitter
+		mn->x = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
+		mn->y = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
+		mn->dx = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
+		mn->dy = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
+
+		// Bounding boxes
+		for (j = 0; j < 2; j++)
+			for (k = 0; k < 4; k++)
+				mn->bbox[j][k] = READINT16((*data)) << FRACBITS;
+
+		//Children
+		mn->children[0] = ShrinkNodeID(READUINT32((*data))); /// \todo Use UINT32 for node children in a future, instead?
+		mn->children[1] = ShrinkNodeID(READUINT32((*data)));
+	}
+}
+
+static void P_LoadMapBSP(const virtres_t *virt)
+{
+	UINT8 *nodedata = NULL;
+	nodetype_t nodetype = P_GetNodetype(virt, &nodedata);
+
+	switch (nodetype)
+	{
+	case NT_DOOM:
+	{
+		virtlump_t *virtssectors = vres_Find(virt, "SSECTORS");
+		virtlump_t* virtnodes = vres_Find(virt, "NODES");
+		virtlump_t *virtsegs = vres_Find(virt, "SEGS");
+
+		numsubsectors = virtssectors->size / sizeof(mapsubsector_t);
+		numnodes      = virtnodes->size    / sizeof(mapnode_t);
+		numsegs       = virtsegs->size     / sizeof(mapseg_t);
+
+		if (numsubsectors <= 0)
+			I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
+		if (numnodes <= 0)
+			I_Error("Level has no nodes");
+		if (numsegs <= 0)
+			I_Error("Level has no segs");
+
+		subsectors = Z_Calloc(numsubsectors * sizeof(*subsectors), PU_LEVEL, NULL);
+		nodes      = Z_Calloc(numnodes * sizeof(*nodes), PU_LEVEL, NULL);
+		segs       = Z_Calloc(numsegs * sizeof(*segs), PU_LEVEL, NULL);
+
+		P_LoadSubsectors(virtssectors->data);
+		P_LoadNodes(virtnodes->data);
+		P_LoadSegs(virtsegs->data);
+		break;
+	}
+	case NT_XNOD:
+	case NT_XGLN:
+	case NT_XGL3:
+		if (!P_LoadExtraVertices(&nodedata))
+			return;
+		if (!P_LoadExtendedSubsectorsAndSegs(&nodedata, nodetype))
+			return;
+		P_LoadExtendedNodes(&nodedata, nodetype);
+		break;
+	default:
+		CONS_Alert(CONS_WARNING, "Unsupported BSP format detected.\n");
+		return;
+	}
+	return;
+}
+
+// Split from P_LoadBlockMap for convenience
+// -- Monster Iestyn 08/01/18
+static void P_ReadBlockMapLump(INT16 *wadblockmaplump, size_t count)
+{
+	size_t i;
+	blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
+
+	// killough 3/1/98: Expand wad blockmap into larger internal one,
+	// by treating all offsets except -1 as unsigned and zero-extending
+	// them. This potentially doubles the size of blockmaps allowed,
+	// because Doom originally considered the offsets as always signed.
+
+	blockmaplump[0] = SHORT(wadblockmaplump[0]);
+	blockmaplump[1] = SHORT(wadblockmaplump[1]);
+	blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
+	blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
+
+	for (i = 4; i < count; i++)
+	{
+		INT16 t = SHORT(wadblockmaplump[i]);          // killough 3/1/98
+		blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
+	}
 }
 
+// This needs to be a separate function
+// because making both the WAD and PK3 loading code use
+// the same functions is trickier than it looks for blockmap
+// -- Monster Iestyn 09/01/18
+static boolean P_LoadBlockMap(UINT8 *data, size_t count)
+{
+	if (!count || count >= 0x20000)
+		return false;
+
+	//CONS_Printf("Reading blockmap lump for pk3...\n");
+
+	// no need to malloc anything, assume the data is uncompressed for now
+	count /= 2;
+	P_ReadBlockMapLump((INT16 *)data, count);
+
+	bmaporgx = blockmaplump[0]<<FRACBITS;
+	bmaporgy = blockmaplump[1]<<FRACBITS;
+	bmapwidth = blockmaplump[2];
+	bmapheight = blockmaplump[3];
+
+	// clear out mobj chains
+	count = sizeof (*blocklinks)* bmapwidth*bmapheight;
+	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
+	blockmap = blockmaplump+4;
+
+#ifdef POLYOBJECTS
+	// haleyjd 2/22/06: setup polyobject blockmap
+	count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
+	polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
+#endif
+	return true;
+}
 
 static boolean LineInBlock(fixed_t cx1, fixed_t cy1, fixed_t cx2, fixed_t cy2, fixed_t bx1, fixed_t by1)
 {
@@ -1847,156 +2508,43 @@ static void P_CreateBlockMap(void)
 	}
 }
 
-// Split from P_LoadBlockMap for convenience
-// -- Monster Iestyn 08/01/18
-static void P_ReadBlockMapLump(INT16 *wadblockmaplump, size_t count)
-{
-	size_t i;
-	blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
-
-	// killough 3/1/98: Expand wad blockmap into larger internal one,
-	// by treating all offsets except -1 as unsigned and zero-extending
-	// them. This potentially doubles the size of blockmaps allowed,
-	// because Doom originally considered the offsets as always signed.
-
-	blockmaplump[0] = SHORT(wadblockmaplump[0]);
-	blockmaplump[1] = SHORT(wadblockmaplump[1]);
-	blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
-	blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
-
-	for (i = 4; i < count; i++)
-	{
-		INT16 t = SHORT(wadblockmaplump[i]);          // killough 3/1/98
-		blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
-	}
-}
-
-//
-// P_LoadBlockMap
-//
-// Levels might not have a blockmap, so if one does not exist
-// this should return false.
-static boolean P_LoadBlockMap(lumpnum_t lumpnum)
+// PK3 version
+// -- Monster Iestyn 09/01/18
+static void P_LoadReject(UINT8 *data, size_t count)
 {
-#if 0
-	(void)lumpnum;
-	return false;
-#else
-	size_t count;
-	const char *lumpname = W_CheckNameForNum(lumpnum);
-
-	// Check if the lump exists, and if it's named "BLOCKMAP"
-	if (!lumpname || memcmp(lumpname, "BLOCKMAP", 8) != 0)
+	if (!count) // zero length, someone probably used ZDBSP
 	{
-		return false;
+		rejectmatrix = NULL;
+		CONS_Debug(DBG_SETUP, "P_LoadReject: REJECT lump has size 0, will not be loaded\n");
 	}
-
-	count = W_LumpLength(lumpnum);
-
-	if (!count || count >= 0x20000)
-		return false;
-
+	else
 	{
-		INT16 *wadblockmaplump = malloc(count); //INT16 *wadblockmaplump = W_CacheLumpNum (lump, PU_LEVEL);
-		if (!wadblockmaplump)
-			return false;
-		W_ReadLump(lumpnum, wadblockmaplump);
-		count /= 2;
-		P_ReadBlockMapLump(wadblockmaplump, count);
-		free(wadblockmaplump);
-	}
-
-	bmaporgx = blockmaplump[0]<<FRACBITS;
-	bmaporgy = blockmaplump[1]<<FRACBITS;
-	bmapwidth = blockmaplump[2];
-	bmapheight = blockmaplump[3];
-
-	// clear out mobj chains
-	count = sizeof (*blocklinks)* bmapwidth*bmapheight;
-	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
-	blockmap = blockmaplump+4;
-
-#ifdef POLYOBJECTS
-	// haleyjd 2/22/06: setup polyobject blockmap
-	count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
-	polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
-#endif
-	return true;
-/* Original
-		blockmaplump = W_CacheLumpNum(lump, PU_LEVEL);
-		blockmap = blockmaplump+4;
-		count = W_LumpLength (lump)/2;
-
-		for (i = 0; i < count; i++)
-			blockmaplump[i] = SHORT(blockmaplump[i]);
-
-		bmaporgx = blockmaplump[0]<<FRACBITS;
-		bmaporgy = blockmaplump[1]<<FRACBITS;
-		bmapwidth = blockmaplump[2];
-		bmapheight = blockmaplump[3];
+		rejectmatrix = Z_Malloc(count, PU_LEVEL, NULL); // allocate memory for the reject matrix
+		M_Memcpy(rejectmatrix, data, count); // copy the data into it
 	}
-
-	// clear out mobj chains
-	count = sizeof (*blocklinks)*bmapwidth*bmapheight;
-	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
-	return true;
-	*/
-#endif
 }
 
-// This needs to be a separate function
-// because making both the WAD and PK3 loading code use
-// the same functions is trickier than it looks for blockmap
-// -- Monster Iestyn 09/01/18
-static boolean P_LoadRawBlockMap(UINT8 *data, size_t count, const char *lumpname)
+static void P_LoadMapLUT(const virtres_t *virt)
 {
-#if 0
-	(void)data;
-	(void)count;
-	(void)lumpname;
-	return false;
-#else
-	// Check if the lump is named "BLOCKMAP"
-	if (!lumpname || memcmp(lumpname, "BLOCKMAP", 8) != 0)
-	{
-		CONS_Printf("No blockmap lump found for pk3!\n");
-		return false;
-	}
-
-	if (!count || count >= 0x20000)
-		return false;
-
-	//CONS_Printf("Reading blockmap lump for pk3...\n");
-
-	// no need to malloc anything, assume the data is uncompressed for now
-	count /= 2;
-	P_ReadBlockMapLump((INT16 *)data, count);
+	virtlump_t* virtblockmap = vres_Find(virt, "BLOCKMAP");
+	virtlump_t* virtreject   = vres_Find(virt, "REJECT");
 
-	bmaporgx = blockmaplump[0]<<FRACBITS;
-	bmaporgy = blockmaplump[1]<<FRACBITS;
-	bmapwidth = blockmaplump[2];
-	bmapheight = blockmaplump[3];
-
-	// clear out mobj chains
-	count = sizeof (*blocklinks)* bmapwidth*bmapheight;
-	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
-	blockmap = blockmaplump+4;
+	// Lookup tables
+	if (virtreject)
+		P_LoadReject(virtreject->data, virtreject->size);
+	else
+		rejectmatrix = NULL;
 
-#ifdef POLYOBJECTS
-	// haleyjd 2/22/06: setup polyobject blockmap
-	count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
-	polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
-#endif
-	return true;
-#endif
+	if (!(virtblockmap && P_LoadBlockMap(virtblockmap->data, virtblockmap->size)))
+		P_CreateBlockMap();
 }
 
 //
-// P_GroupLines
+// P_LinkMapData
 // Builds sector line lists and subsector sector numbers.
 // Finds block bounding boxes for sectors.
 //
-static void P_GroupLines(void)
+static void P_LinkMapData(void)
 {
 	size_t i, j;
 	line_t *li;
@@ -2010,20 +2558,20 @@ static void P_GroupLines(void)
 	for (i = 0; i < numsubsectors; i++, ss++)
 	{
 		if (ss->firstline >= numsegs)
-			CorruptMapError(va("P_GroupLines: ss->firstline invalid "
+			CorruptMapError(va("P_LinkMapData: ss->firstline invalid "
 				"(subsector %s, firstline refers to %d of %s)", sizeu1(i), ss->firstline,
 				sizeu2(numsegs)));
 		seg = &segs[ss->firstline];
 		sidei = (size_t)(seg->sidedef - sides);
 		if (!seg->sidedef)
-			CorruptMapError(va("P_GroupLines: seg->sidedef is NULL "
+			CorruptMapError(va("P_LinkMapData: seg->sidedef is NULL "
 				"(subsector %s, firstline is %d)", sizeu1(i), ss->firstline));
 		if (seg->sidedef - sides < 0 || seg->sidedef - sides > (UINT16)numsides)
-			CorruptMapError(va("P_GroupLines: seg->sidedef refers to sidedef %s of %s "
+			CorruptMapError(va("P_LinkMapData: seg->sidedef refers to sidedef %s of %s "
 				"(subsector %s, firstline is %d)", sizeu1(sidei), sizeu2(numsides),
 				sizeu3(i), ss->firstline));
 		if (!seg->sidedef->sector)
-			CorruptMapError(va("P_GroupLines: seg->sidedef->sector is NULL "
+			CorruptMapError(va("P_LinkMapData: seg->sidedef->sector is NULL "
 				"(subsector %s, firstline is %d, sidedef is %s)", sizeu1(i), ss->firstline,
 				sizeu1(sidei)));
 		ss->sector = seg->sidedef->sector;
@@ -2044,7 +2592,7 @@ static void P_GroupLines(void)
 		if (sector->linecount == 0) // no lines found?
 		{
 			sector->lines = NULL;
-			CONS_Debug(DBG_SETUP, "P_GroupLines: sector %s has no lines\n", sizeu1(i));
+			CONS_Debug(DBG_SETUP, "P_LinkMapData: sector %s has no lines\n", sizeu1(i));
 		}
 		else
 		{
@@ -2087,95 +2635,95 @@ static void P_GroupLines(void)
 	}
 }
 
-//
-// P_LoadReject
-//
-// Detect if the REJECT lump is valid,
-// if not, rejectmatrix will be NULL
-static void P_LoadReject(lumpnum_t lumpnum)
+/** Compute MD5 message digest for bytes read from memory source
+  *
+  * The resulting message digest number will be written into the 16 bytes
+  * beginning at RESBLOCK.
+  *
+  * \param filename path of file
+  * \param resblock resulting MD5 checksum
+  * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
+  */
+static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
 {
-	size_t count;
-	const char *lumpname = W_CheckNameForNum(lumpnum);
-
-	// Check if the lump exists, and if it's named "REJECT"
-	if (!lumpname || memcmp(lumpname, "REJECT\0\0", 8) != 0)
-	{
-		rejectmatrix = NULL;
-		CONS_Debug(DBG_SETUP, "P_LoadReject: No valid REJECT lump found\n");
-		return;
-	}
-
-	count = W_LumpLength(lumpnum);
-
-	if (!count) // zero length, someone probably used ZDBSP
-	{
-		rejectmatrix = NULL;
-		CONS_Debug(DBG_SETUP, "P_LoadReject: REJECT lump has size 0, will not be loaded\n");
-	}
-	else
-		rejectmatrix = W_CacheLumpNum(lumpnum, PU_LEVEL);
+#ifdef NOMD5
+	(void)buffer;
+	(void)len;
+	memset(resblock, 0x00, 16);
+	return 1;
+#else
+	tic_t t = I_GetTime();
+	CONS_Debug(DBG_SETUP, "Making MD5\n");
+	if (md5_buffer(buffer, len, resblock) == NULL)
+		return 1;
+	CONS_Debug(DBG_SETUP, "MD5 calc took %f seconds\n", (float)(I_GetTime() - t)/NEWTICRATE);
+	return 0;
+#endif
 }
 
-// PK3 version
-// -- Monster Iestyn 09/01/18
-static void P_LoadRawReject(UINT8 *data, size_t count, const char *lumpname)
+static void P_MakeMapMD5(virtres_t *virt, void *dest)
 {
-	// Check if the lump is named "REJECT"
-	if (!lumpname || memcmp(lumpname, "REJECT\0\0", 8) != 0)
-	{
-		rejectmatrix = NULL;
-		CONS_Debug(DBG_SETUP, "P_LoadRawReject: No valid REJECT lump found\n");
-		return;
-	}
+	virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
+	unsigned char resmd5[16];
 
-	if (!count) // zero length, someone probably used ZDBSP
-	{
-		rejectmatrix = NULL;
-		CONS_Debug(DBG_SETUP, "P_LoadRawReject: REJECT lump has size 0, will not be loaded\n");
-	}
+	if (textmap)
+		P_MakeBufferMD5((char*)textmap->data, textmap->size, resmd5);
 	else
 	{
-		rejectmatrix = Z_Malloc(count, PU_LEVEL, NULL); // allocate memory for the reject matrix
-		M_Memcpy(rejectmatrix, data, count); // copy the data into it
+		unsigned char linemd5[16];
+		unsigned char sectormd5[16];
+		unsigned char thingmd5[16];
+		unsigned char sidedefmd5[16];
+		UINT8 i;
+
+		// Create a hash for the current map
+		// get the actual lumps!
+		virtlump_t* virtlines   = vres_Find(virt, "LINEDEFS");
+		virtlump_t* virtsectors = vres_Find(virt, "SECTORS");
+		virtlump_t* virtmthings = vres_Find(virt, "THINGS");
+		virtlump_t* virtsides   = vres_Find(virt, "SIDEDEFS");
+
+		P_MakeBufferMD5((char*)virtlines->data,   virtlines->size, linemd5);
+		P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size,  sectormd5);
+		P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size,   thingmd5);
+		P_MakeBufferMD5((char*)virtsides->data,   virtsides->size, sidedefmd5);
+
+		for (i = 0; i < 16; i++)
+			resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
 	}
-}
 
-#if 0
-static char *levellumps[] =
-{
-	"label",        // ML_LABEL,    A separator, name, MAPxx
-	"THINGS",       // ML_THINGS,   Enemies, items..
-	"LINEDEFS",     // ML_LINEDEFS, Linedefs, from editing
-	"SIDEDEFS",     // ML_SIDEDEFS, Sidedefs, from editing
-	"VERTEXES",     // ML_VERTEXES, Vertices, edited and BSP splits generated
-	"SEGS",         // ML_SEGS,     Linesegs, from linedefs split by BSP
-	"SSECTORS",     // ML_SSECTORS, Subsectors, list of linesegs
-	"NODES",        // ML_NODES,    BSP nodes
-	"SECTORS",      // ML_SECTORS,  Sectors, from editing
-	"REJECT",       // ML_REJECT,   LUT, sector-sector visibility
-};
+	M_Memcpy(dest, &resmd5, 16);
+}
 
-/** Checks a lump and returns whether it is a valid start-of-level marker.
-  *
-  * \param lumpnum Lump number to check.
-  * \return True if the lump is a valid level marker, false if not.
-  */
-static inline boolean P_CheckLevel(lumpnum_t lumpnum)
+static boolean P_LoadMapFromFile(void)
 {
-	UINT16 file, lump;
-	size_t i;
+	virtres_t *virt = vres_GetMap(lastloadedmaplumpnum);
 
-	for (i = ML_THINGS; i <= ML_REJECT; i++)
-	{
-		file = WADFILENUM(lumpnum);
-		lump = LUMPNUM(lumpnum+1);
-		if (file > numwadfiles || lump < LUMPNUM(lumpnum) || lump > wadfiles[file]->numlumps ||
-			memcmp(wadfiles[file]->lumpinfo[lump].name, levellumps[i], 8) != 0)
+	if (!P_LoadMapData(virt))
 		return false;
-	}
-	return true; // all right
+	P_LoadMapBSP(virt);
+	P_LoadMapLUT(virt);
+
+	P_LinkMapData();
+
+	// Copy relevant map data for NetArchive purposes.
+	spawnsectors = Z_Calloc(numsectors * sizeof(*sectors), PU_LEVEL, NULL);
+	spawnlines = Z_Calloc(numlines * sizeof(*lines), PU_LEVEL, NULL);
+	spawnsides = Z_Calloc(numsides * sizeof(*sides), PU_LEVEL, NULL);
+
+	memcpy(spawnsectors, sectors, numsectors * sizeof(*sectors));
+	memcpy(spawnlines, lines, numlines * sizeof(*lines));
+	memcpy(spawnsides, sides, numsides * sizeof(*sides));
+
+	P_MakeMapMD5(virt, &mapmd5);
+
+	vres_Free(virt);
+	return true;
 }
-#endif
+
+//
+// LEVEL INITIALIZATION FUNCTIONS
+//
 
 /** Sets up a sky texture to use for the level.
   * The sky texture is used instead of F_SKY1.
@@ -2208,7 +2756,7 @@ lumpnum_t lastloadedmaplumpnum; // for comparative savegame
 //
 // Some player initialization for map start.
 //
-static void P_LevelInitStuff(void)
+static void P_InitLevelSettings(void)
 {
 	INT32 i;
 	boolean canresetlives = true;
@@ -2266,7 +2814,7 @@ static void P_LevelInitStuff(void)
 	// earthquake camera
 	memset(&quake,0,sizeof(struct quake));
 
-	if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2)
+	if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
 	{
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
@@ -2284,7 +2832,7 @@ static void P_LevelInitStuff(void)
 	{
 		G_PlayerReborn(i, true);
 
-		if (canresetlives && (netgame || multiplayer) && playeringame[i] && (gametype == GT_COMPETITION || players[i].lives <= 0))
+		if (canresetlives && (netgame || multiplayer) && playeringame[i] && (G_CompetitionGametype() || players[i].lives <= 0))
 		{
 			// In Co-Op, replenish a user's lives if they are depleted.
 			players[i].lives = cv_startinglives.value;
@@ -2305,12 +2853,8 @@ static void P_LevelInitStuff(void)
 		CV_SetValue(&cv_analog2, true);
 }
 
-//
-// P_LoadThingsOnly
-//
-// "Reloads" a level, but only reloads all of the mobjs.
-//
-void P_LoadThingsOnly(void)
+// Respawns all the mapthings and mobjs in the map from the already loaded map data.
+void P_RespawnThings(void)
 {
 	// Search through all the thinkers.
 	thinker_t *think;
@@ -2326,7 +2870,6 @@ void P_LoadThingsOnly(void)
 				centerid = i; // save id just in case
 		}
 
-
 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
 	{
 		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
@@ -2334,83 +2877,15 @@ void P_LoadThingsOnly(void)
 		P_RemoveMobj((mobj_t *)think);
 	}
 
-	P_LevelInitStuff();
+	P_InitLevelSettings();
 
-	if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
-	{ // HACK: Open wad file rather quickly so we can use the things lump
-		UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
-		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
-		fileinfo += ML_THINGS; // we only need the THINGS lump
-		P_PrepareRawThings(wadData + fileinfo->filepos, fileinfo->size);
-		Z_Free(wadData); // we're done with this now
-	}
-	else // phew it's just a WAD
-		P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
-	P_LoadThings(true);
+	P_SpawnMapThings(true);
 
 	// restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that
 	skyboxmo[0] = skyboxviewpnts[(viewid >= 0) ? viewid : 0];
 	skyboxmo[1] = skyboxcenterpnts[(centerid >= 0) ? centerid : 0];
 }
 
-/** Compute MD5 message digest for bytes read from memory source
-  *
-  * The resulting message digest number will be written into the 16 bytes
-  * beginning at RESBLOCK.
-  *
-  * \param filename path of file
-  * \param resblock resulting MD5 checksum
-  * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
-  */
-static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
-{
-#ifdef NOMD5
-	(void)buffer;
-	(void)len;
-	memset(resblock, 0x00, 16);
-	return 1;
-#else
-	tic_t t = I_GetTime();
-	CONS_Debug(DBG_SETUP, "Making MD5\n");
-	if (md5_buffer(buffer, len, resblock) == NULL)
-		return 1;
-	CONS_Debug(DBG_SETUP, "MD5 calc took %f seconds\n", (float)(I_GetTime() - t)/NEWTICRATE);
-	return 0;
-#endif
-}
-
-static void P_MakeMapMD5(lumpnum_t maplumpnum, void *dest)
-{
-	unsigned char linemd5[16];
-	unsigned char sectormd5[16];
-	unsigned char thingmd5[16];
-	unsigned char sidedefmd5[16];
-	unsigned char resmd5[16];
-	UINT8 i;
-
-	// Create a hash for the current map
-	// get the actual lumps!
-	UINT8 *datalines   = W_CacheLumpNum(maplumpnum + ML_LINEDEFS, PU_CACHE);
-	UINT8 *datasectors = W_CacheLumpNum(maplumpnum + ML_SECTORS, PU_CACHE);
-	UINT8 *datathings  = W_CacheLumpNum(maplumpnum + ML_THINGS, PU_CACHE);
-	UINT8 *datasides   = W_CacheLumpNum(maplumpnum + ML_SIDEDEFS, PU_CACHE);
-
-	P_MakeBufferMD5((char*)datalines,   W_LumpLength(maplumpnum + ML_LINEDEFS), linemd5);
-	P_MakeBufferMD5((char*)datasectors, W_LumpLength(maplumpnum + ML_SECTORS),  sectormd5);
-	P_MakeBufferMD5((char*)datathings,  W_LumpLength(maplumpnum + ML_THINGS),   thingmd5);
-	P_MakeBufferMD5((char*)datasides,   W_LumpLength(maplumpnum + ML_SIDEDEFS), sidedefmd5);
-
-	Z_Free(datalines);
-	Z_Free(datasectors);
-	Z_Free(datathings);
-	Z_Free(datasides);
-
-	for (i = 0; i < 16; i++)
-		resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
-
-	M_Memcpy(dest, &resmd5, 16);
-}
-
 static void P_RunLevelScript(const char *scriptname)
 {
 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SCRIPTISFILE))
@@ -2475,6 +2950,26 @@ static void P_ForceCharacter(const char *forcecharskin)
 	}
 }
 
+static void P_ResetSpawnpoints(void)
+{
+	UINT8 i;
+
+	numdmstarts = numredctfstarts = numbluectfstarts = 0;
+
+	// reset the player starts
+	for (i = 0; i < MAXPLAYERS; i++)
+		playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
+
+	for (i = 0; i < MAX_DM_STARTS; i++)
+		deathmatchstarts[i] = NULL;
+
+	for (i = 0; i < 2; i++)
+		skyboxmo[i] = NULL;
+
+	for (i = 0; i < 16; i++)
+		skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
+}
+
 static void P_LoadRecordGhosts(void)
 {
 	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
@@ -2559,93 +3054,337 @@ static void P_LoadNightsGhosts(void)
 	if (cv_ghost_bestscore.value && FIL_FileExists(va("%s-score-best.lmp", gpath)))
 			G_AddGhost(va("%s-score-best.lmp", gpath));
 
-	// Best Time ghost
-	if (cv_ghost_besttime.value && FIL_FileExists(va("%s-time-best.lmp", gpath)))
-			G_AddGhost(va("%s-time-best.lmp", gpath));
+	// Best Time ghost
+	if (cv_ghost_besttime.value && FIL_FileExists(va("%s-time-best.lmp", gpath)))
+			G_AddGhost(va("%s-time-best.lmp", gpath));
+
+	// Last ghost
+	if (cv_ghost_last.value && FIL_FileExists(va("%s-last.lmp", gpath)))
+		G_AddGhost(va("%s-last.lmp", gpath));
+
+	// Guest ghost
+	if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath)))
+		G_AddGhost(va("%s-guest.lmp", gpath));
+
+	free(gpath);
+}
+
+static void P_InitTagGametype(void)
+{
+	UINT8 i;
+	INT32 realnumplayers = 0;
+	INT32 playersactive[MAXPLAYERS];
+
+	//I just realized how problematic this code can be.
+	//D_NumPlayers() will not always cover the scope of the netgame.
+	//What if one player is node 0 and the other node 31?
+	//The solution? Make a temp array of all players that are currently playing and pick from them.
+	//Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended?
+	//Also, you'd never have to loop through all 32 players slots to find anything ever again.
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && !players[i].spectator)
+		{
+			playersactive[realnumplayers] = i; //stores the player's node in the array.
+			realnumplayers++;
+		}
+	}
+
+	if (!realnumplayers) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked.
+	{
+		CONS_Printf(M_GetText("No player currently available to become IT. Awaiting available players.\n"));
+		return;
+	}
+
+	i = P_RandomKey(realnumplayers);
+	players[playersactive[i]].pflags |= PF_TAGIT; //choose our initial tagger before map starts.
+
+	// Taken and modified from G_DoReborn()
+	// Remove the player so he can respawn elsewhere.
+	// first disassociate the corpse
+	if (players[playersactive[i]].mo)
+		P_RemoveMobj(players[playersactive[i]].mo);
+
+	G_SpawnPlayer(playersactive[i], false); //respawn the lucky player in his dedicated spawn location.
+}
+
+static void P_SetupCamera(void)
+{
+	if (players[displayplayer].mo && (server || addedtogame))
+	{
+		camera.x = players[displayplayer].mo->x;
+		camera.y = players[displayplayer].mo->y;
+		camera.z = players[displayplayer].mo->z;
+		camera.angle = players[displayplayer].mo->angle;
+		camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
+	}
+	else
+	{
+		mapthing_t *thing;
+
+		if (gametyperules & GTR_DEATHMATCHSTARTS)
+			thing = deathmatchstarts[0];
+		else
+			thing = playerstarts[0];
+
+		if (thing)
+		{
+			camera.x = thing->x;
+			camera.y = thing->y;
+			camera.z = thing->z;
+			camera.angle = FixedAngle((fixed_t)thing->angle << FRACBITS);
+			camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
+		}
+	}
+}
+
+static void P_InitCamera(void)
+{
+	if (!dedicated)
+	{
+		P_SetupCamera();
+
+		// Salt: CV_ClearChangedFlags() messes with your settings :(
+		/*if (!cv_cam_height.changed)
+			CV_Set(&cv_cam_height, cv_cam_height.defaultvalue);
+		if (!cv_cam2_height.changed)
+			CV_Set(&cv_cam2_height, cv_cam2_height.defaultvalue);
+
+		if (!cv_cam_dist.changed)
+			CV_Set(&cv_cam_dist, cv_cam_dist.defaultvalue);
+		if (!cv_cam2_dist.changed)
+			CV_Set(&cv_cam2_dist, cv_cam2_dist.defaultvalue);*/
+
+			// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
+		if (!cv_cam_rotate.changed)
+			CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
+		if (!cv_cam2_rotate.changed)
+			CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
+
+		if (!cv_analog.changed)
+			CV_SetValue(&cv_analog, 0);
+		if (!cv_analog2.changed)
+			CV_SetValue(&cv_analog2, 0);
+
+		displayplayer = consoleplayer; // Start with your OWN view, please!
+	}
+
+	if (twodlevel)
+	{
+		CV_SetValue(&cv_analog, false);
+		CV_SetValue(&cv_analog2, false);
+	}
+	else
+	{
+		if (cv_useranalog.value)
+			CV_SetValue(&cv_analog, true);
+
+		if ((splitscreen && cv_useranalog2.value) || botingame)
+			CV_SetValue(&cv_analog2, true);
+	}
+}
+
+static boolean CanSaveLevel(INT32 mapnum)
+{
+	if (ultimatemode) // never save in ultimate (probably redundant with cursaveslot also being checked)
+		return false;
+
+	if (G_IsSpecialStage(mapnum) // don't save in special stages
+		|| mapnum == lastmaploaded) // don't save if the last map loaded was this one
+		return false;
+
+	// Any levels that have the savegame flag can save normally.
+	// If the game is complete for this save slot, then any level can save!
+	// On the other side of the spectrum, if lastmaploaded is 0, then the save file has only just been created and needs to save ASAP!
+	return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || gamecomplete || !lastmaploaded);
+}
+
+static void P_RunSpecialStageWipe(void)
+{
+	tic_t starttime = I_GetTime();
+	tic_t endtime = starttime + (3*TICRATE)/2;
+	tic_t nowtime;
+
+	S_StartSound(NULL, sfx_s3kaf);
+
+	// Fade music! Time it to S3KAF: 0.25 seconds is snappy.
+	if (RESETMUSIC ||
+		strnicmp(S_MusicName(),
+		(mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap - 1]->musname : mapmusname, 7))
+		S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
+
+	F_WipeStartScreen();
+	wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
+
+#ifdef HWRENDER
+	// uh..........
+	if (rendermode == render_opengl)
+		F_WipeColorFill(0);
+#endif
+
+	F_WipeEndScreen();
+	F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
+
+	I_OsPolling();
+	I_FinishUpdate(); // page flip or blit buffer
+	if (moviemode)
+		M_SaveFrame();
+
+	nowtime = lastwipetic;
+
+	// Hold on white for extra effect.
+	while (nowtime < endtime)
+	{
+		// wait loop
+		while (!((nowtime = I_GetTime()) - lastwipetic))
+			I_Sleep();
+		lastwipetic = nowtime;
+		if (moviemode) // make sure we save frames for the white hold too
+			M_SaveFrame();
+	}
+}
+
+static void P_RunLevelWipe(void)
+{
+	F_WipeStartScreen();
+	wipestyleflags |= WSF_FADEOUT;
+
+#ifdef HWRENDER
+	// uh..........
+	if (rendermode == render_opengl)
+		F_WipeColorFill(31);
+#endif
+
+	F_WipeEndScreen();
+	// for titlemap: run a specific wipe if specified
+	// needed for exiting time attack
+	if (wipetypepre != INT16_MAX)
+		F_RunWipe(
+		(wipetypepre >= 0 && F_WipeExists(wipetypepre)) ? wipetypepre : wipedefs[wipe_level_toblack],
+			false);
+	wipetypepre = -1;
+}
+
+static void P_InitPlayers(void)
+{
+	UINT8 i;
+
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+			continue;
+
+		// Start players with pity shields if possible
+		players[i].pity = -1;
+
+		players[i].mo = NULL;
+
+		if (!G_PlatformGametype())
+			G_DoReborn(i);
+		else // gametype is GT_COOP or GT_RACE
+		{
+			G_SpawnPlayer(i, players[i].starposttime);
+			if (players[i].starposttime)
+				P_ClearStarPost(players[i].starpostnum);
+		}
+	}
+}
+
+static void P_WriteLetter(void)
+{
+	char *buf, *b;
+
+	if (!unlockables[27].unlocked) // pandora's box
+		return;
+
+	if (modeattacking)
+		return;
+
+#ifndef DEVELOP
+	if (modifiedgame)
+		return;
+#endif
+
+	if (netgame || multiplayer)
+		return;
+
+	if (gamemap != 0x1d35 - 016464)
+		return;
 
-	// Last ghost
-	if (cv_ghost_last.value && FIL_FileExists(va("%s-last.lmp", gpath)))
-		G_AddGhost(va("%s-last.lmp", gpath));
+	P_SpawnMobj(0640370000, 0x11000000, 0x3180000, MT_LETTER)->angle = ANGLE_90;
 
-	// Guest ghost
-	if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath)))
-		G_AddGhost(va("%s-guest.lmp", gpath));
+	if (textprompts[199]->page[1].backcolor == 259)
+		return;
 
-	free(gpath);
-}
+	buf = W_CacheLumpName("WATERMAP", PU_STATIC);
+	b = buf;
 
-static void P_SetupCamera(void)
-{
-	if (players[displayplayer].mo && (server || addedtogame))
+	while ((*b != 65) && (b - buf < 256))
 	{
-		camera.x = players[displayplayer].mo->x;
-		camera.y = players[displayplayer].mo->y;
-		camera.z = players[displayplayer].mo->z;
-		camera.angle = players[displayplayer].mo->angle;
-		camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
+		*b = (*b - 65) & 255;
+		b++;
 	}
-	else
-	{
-		mapthing_t *thing;
+	*b = '\0';
 
-		switch (gametype)
-		{
-		case GT_MATCH:
-		case GT_TAG:
-			thing = deathmatchstarts[0];
-			break;
+	Z_Free(textprompts[199]->page[1].text);
+	textprompts[199]->page[1].text = Z_StrDup(buf);
+	textprompts[199]->page[1].lines = 4;
+	textprompts[199]->page[1].backcolor = 259;
+	Z_Free(buf);
+}
 
-		default:
-			thing = playerstarts[0];
-			break;
-		}
+static void P_InitGametype(void)
+{
+	UINT8 i;
 
-		if (thing)
+	P_InitPlayers();
+
+	// restore time in netgame (see also g_game.c)
+	if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
+	{
+		// is this a hack? maybe
+		tic_t maxstarposttime = 0;
+		for (i = 0; i < MAXPLAYERS; i++)
 		{
-			camera.x = thing->x;
-			camera.y = thing->y;
-			camera.z = thing->z;
-			camera.angle = FixedAngle((fixed_t)thing->angle << FRACBITS);
-			camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
+			if (playeringame[i] && players[i].starposttime > maxstarposttime)
+				maxstarposttime = players[i].starposttime;
 		}
+		leveltime = maxstarposttime;
 	}
-}
 
-static boolean CanSaveLevel(INT32 mapnum)
-{
-	if (ultimatemode) // never save in ultimate (probably redundant with cursaveslot also being checked)
-		return false;
+	P_WriteLetter();
 
-	if (G_IsSpecialStage(mapnum) // don't save in special stages
-		|| mapnum == lastmaploaded) // don't save if the last map loaded was this one
-		return false;
+	if (modeattacking == ATTACKING_RECORD && !demoplayback)
+		P_LoadRecordGhosts();
+	else if (modeattacking == ATTACKING_NIGHTS && !demoplayback)
+		P_LoadNightsGhosts();
 
-	// Any levels that have the savegame flag can save normally.
-	// If the game is complete for this save slot, then any level can save!
-	// On the other side of the spectrum, if lastmaploaded is 0, then the save file has only just been created and needs to save ASAP!
-	return (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME || gamecomplete || !lastmaploaded);
+	if (G_TagGametype())
+		P_InitTagGametype();
+	else if (gametype == GT_RACE && server)
+		CV_StealthSetValue(&cv_numlaps,
+		(cv_basenumlaps.value)
+			? cv_basenumlaps.value
+			: mapheaderinfo[gamemap - 1]->numlaps);
 }
 
 /** Loads a level from a lump or external wad.
   *
-  * \param skipprecip If true, don't spawn precipitation.
+  * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot.
   * \todo Clean up, refactor, split up; get rid of the bloat.
   */
-boolean P_SetupLevel(boolean skipprecip)
+boolean P_LoadLevel(boolean fromnetsave)
 {
 	// use gamemap to get map number.
 	// 99% of the things already did, so.
 	// Map header should always be in place at this point
-	INT32 i, loadprecip = 1, ranspecialwipe = 0;
-	INT32 loademblems = 1;
-	INT32 fromnetsave = 0;
-	boolean loadedbm = false;
+	INT32 i, ranspecialwipe = 0;
 	sector_t *ss;
-	boolean chase;
 	levelloading = true;
 
 	// This is needed. Don't touch.
 	maptol = mapheaderinfo[gamemap-1]->typeoflevel;
+	gametyperules = gametypedefaultrules[gametype];
 
 	CON_Drawer(); // let the user know what we are going to do
 	I_FinishUpdate(); // page flip or blit buffer
@@ -2672,20 +3411,18 @@ boolean P_SetupLevel(boolean skipprecip)
 	if (cv_runscripts.value && mapheaderinfo[gamemap-1]->scriptname[0] != '#')
 		P_RunLevelScript(mapheaderinfo[gamemap-1]->scriptname);
 
-	P_LevelInitStuff();
+	P_InitLevelSettings();
 
 	postimgtype = postimgtype2 = postimg_none;
 
 	if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')
 		P_ForceCharacter(mapheaderinfo[gamemap-1]->forcecharacter);
 
-	// chasecam on in chaos, race, coop
-	// chasecam off in match, tag, capture the flag
-	chase = (gametype == GT_RACE || gametype == GT_COMPETITION || gametype == GT_COOP)
-		|| (maptol & TOL_2D);
-
 	if (!dedicated)
 	{
+		// chasecam on in first-person gametypes and 2D
+		boolean chase = (!(gametyperules & GTR_FIRSTPERSON)) || (maptol & TOL_2D);
+
 		// Salt: CV_ClearChangedFlags() messes with your settings :(
 		/*if (!cv_cam_speed.changed)
 			CV_Set(&cv_cam_speed, cv_cam_speed.defaultvalue);*/
@@ -2712,48 +3449,7 @@ boolean P_SetupLevel(boolean skipprecip)
 		ranspecialwipe = 2;
 	else if (rendermode != render_none && G_IsSpecialStage(gamemap))
 	{
-		tic_t starttime = I_GetTime();
-		tic_t endtime = starttime + (3*TICRATE)/2;
-		tic_t nowtime;
-
-		S_StartSound(NULL, sfx_s3kaf);
-
-		// Fade music! Time it to S3KAF: 0.25 seconds is snappy.
-		if (RESETMUSIC ||
-			strnicmp(S_MusicName(),
-				(mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap-1]->musname : mapmusname, 7))
-			S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
-
-		F_WipeStartScreen();
-		wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
-
-#ifdef HWRENDER
-		// uh..........
-		if (rendermode == render_opengl)
-			F_WipeColorFill(0);
-#endif
-
-		F_WipeEndScreen();
-		F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
-
-		I_OsPolling();
-		I_FinishUpdate(); // page flip or blit buffer
-		if (moviemode)
-			M_SaveFrame();
-
-		nowtime = lastwipetic;
-
-		// Hold on white for extra effect.
-		while (nowtime < endtime)
-		{
-			// wait loop
-			while (!((nowtime = I_GetTime()) - lastwipetic))
-				I_Sleep();
-			lastwipetic = nowtime;
-			if (moviemode) // make sure we save frames for the white hold too
-				M_SaveFrame();
-		}
-
+		P_RunSpecialStageWipe();
 		ranspecialwipe = 1;
 	}
 
@@ -2779,25 +3475,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	// Let's fade to black here
 	// But only if we didn't do the special stage wipe
 	if (rendermode != render_none && !ranspecialwipe)
-	{
-		F_WipeStartScreen();
-		wipestyleflags |= WSF_FADEOUT;
-
-#ifdef HWRENDER
-		// uh..........
-		if (rendermode == render_opengl)
-			F_WipeColorFill(31);
-#endif
-
-		F_WipeEndScreen();
-		// for titlemap: run a specific wipe if specified
-		// needed for exiting time attack
-		if (wipetypepre != INT16_MAX)
-			F_RunWipe(
-				(wipetypepre >= 0 && F_WipeExists(wipetypepre)) ? wipetypepre : wipedefs[wipe_level_toblack],
-				false);
-		wipetypepre = -1;
-	}
+		P_RunLevelWipe();
 
 	if (!titlemapinaction)
 	{
@@ -2812,12 +3490,12 @@ boolean P_SetupLevel(boolean skipprecip)
 		{
 			// Don't include these in the fade!
 			char tx[64];
-			V_DrawSmallString(1, 191, V_ALLOWLOWERCASE|V_TRANSLUCENT, M_GetText("Speeding off to..."));
+			V_DrawSmallString(1, 191, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Speeding off to..."));
 			snprintf(tx, 63, "%s%s%s",
 				mapheaderinfo[gamemap-1]->lvlttl,
 				(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone",
 				(mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : "");
-			V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT, tx);
+			V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, tx);
 			I_UpdateNoVsync();
 		}
 
@@ -2855,20 +3533,22 @@ boolean P_SetupLevel(boolean skipprecip)
 	P_InitThinkers();
 	P_InitCachedActions();
 
-	/// \note for not spawning precipitation, etc. when loading netgame snapshots
-	if (skipprecip)
+	if (!fromnetsave && savedata.lives > 0)
 	{
-		fromnetsave = 1;
-		loadprecip = 0;
-		loademblems = 0;
+		numgameovers = savedata.numgameovers;
+		players[consoleplayer].continues = savedata.continues;
+		players[consoleplayer].lives = savedata.lives;
+		players[consoleplayer].score = savedata.score;
+		if ((botingame = ((botskin = savedata.botskin) != 0)))
+			botcolor = skins[botskin-1].prefcolor;
+		emeralds = savedata.emeralds;
+		savedata.lives = 0;
 	}
 
 	// internal game map
 	maplumpname = G_BuildMapName(gamemap);
-	//lastloadedmaplumpnum = LUMPERROR;
 	lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
-
-	if (lastloadedmaplumpnum == INT16_MAX)
+	if (lastloadedmaplumpnum == LUMPERROR)
 		I_Error("Map %s not found.\n", maplumpname);
 
 	R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
@@ -2877,111 +3557,12 @@ boolean P_SetupLevel(boolean skipprecip)
 	// SRB2 determines the sky texture to be used depending on the map header.
 	P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true);
 
-	P_MakeMapMD5(lastloadedmaplumpnum, &mapmd5);
-
-	// HACK ALERT: Cache the WAD, get the map data into the tables, free memory.
-	// As it is implemented right now, we're assuming an uncompressed WAD.
-	// (As in, a normal PWAD, not ZWAD or anything. The lump itself can be compressed.)
-	// We're not accounting for extra lumps and scrambled lump positions. Any additional data will cause an error.
-	if (W_IsLumpWad(lastloadedmaplumpnum))
-	{
-		// Remember that we're assuming that the WAD will have a specific set of lumps in a specific order.
-		UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
-		//filelump_t *fileinfo = wadData + ((wadinfo_t *)wadData)->infotableofs;
-		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
-		UINT32 numlumps = ((wadinfo_t *)wadData)->numlumps;
-
-		if (numlumps < ML_REJECT) // at least 9 lumps should be in the wad for a map to be loaded
-		{
-			I_Error("Bad WAD file for map %s!\n", maplumpname);
-		}
-
-		if (numlumps > ML_BLOCKMAP) // enough room for a BLOCKMAP lump at least
-		{
-			loadedbm = P_LoadRawBlockMap(
-							wadData + (fileinfo + ML_BLOCKMAP)->filepos,
-							(fileinfo + ML_BLOCKMAP)->size,
-							(fileinfo + ML_BLOCKMAP)->name);
-		}
-		P_LoadRawVertexes(wadData + (fileinfo + ML_VERTEXES)->filepos, (fileinfo + ML_VERTEXES)->size);
-		P_LoadRawSectors(wadData + (fileinfo + ML_SECTORS)->filepos, (fileinfo + ML_SECTORS)->size);
-		P_LoadRawSideDefs((fileinfo + ML_SIDEDEFS)->size);
-		P_LoadRawLineDefs(wadData + (fileinfo + ML_LINEDEFS)->filepos, (fileinfo + ML_LINEDEFS)->size);
-		P_LoadRawSideDefs2(wadData + (fileinfo + ML_SIDEDEFS)->filepos);
-		P_LoadRawSubsectors(wadData + (fileinfo + ML_SSECTORS)->filepos, (fileinfo + ML_SSECTORS)->size);
-		P_LoadRawNodes(wadData + (fileinfo + ML_NODES)->filepos, (fileinfo + ML_NODES)->size);
-		P_LoadRawSegs(wadData + (fileinfo + ML_SEGS)->filepos, (fileinfo + ML_SEGS)->size);
-		if (numlumps > ML_REJECT) // enough room for a REJECT lump at least
-		{
-			P_LoadRawReject(
-					wadData + (fileinfo + ML_REJECT)->filepos,
-					(fileinfo + ML_REJECT)->size,
-					(fileinfo + ML_REJECT)->name);
-		}
-
-		// Important: take care of the ordering of the next functions.
-		if (!loadedbm)
-			P_CreateBlockMap(); // Graue 02-29-2004
-		P_LoadLineDefs2();
-		P_GroupLines();
-		numdmstarts = numredctfstarts = numbluectfstarts = 0;
-
-		// reset the player starts
-		for (i = 0; i < MAXPLAYERS; i++)
-			playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
-
-		for (i = 0; i < MAX_DM_STARTS; i++)
-			deathmatchstarts[i] = NULL;
-
-		for (i = 0; i < 2; i++)
-			skyboxmo[i] = NULL;
-
-		for (i = 0; i < 16; i++)
-			skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
-
-		P_MapStart();
-
-		P_PrepareRawThings(wadData + (fileinfo + ML_THINGS)->filepos, (fileinfo + ML_THINGS)->size);
-		Z_Free(wadData);
-	}
-	else
-	{
-		// Important: take care of the ordering of the next functions.
-		loadedbm = P_LoadBlockMap(lastloadedmaplumpnum + ML_BLOCKMAP);
-		P_LoadVertexes(lastloadedmaplumpnum + ML_VERTEXES);
-		P_LoadSectors(lastloadedmaplumpnum + ML_SECTORS);
-		P_LoadSideDefs(lastloadedmaplumpnum + ML_SIDEDEFS);
-		P_LoadLineDefs(lastloadedmaplumpnum + ML_LINEDEFS);
-		P_LoadSideDefs2(lastloadedmaplumpnum + ML_SIDEDEFS);
-		P_LoadSubsectors(lastloadedmaplumpnum + ML_SSECTORS);
-		P_LoadNodes(lastloadedmaplumpnum + ML_NODES);
-		P_LoadSegs(lastloadedmaplumpnum + ML_SEGS);
-		P_LoadReject(lastloadedmaplumpnum + ML_REJECT);
-
-		// Important: take care of the ordering of the next functions.
-		if (!loadedbm)
-			P_CreateBlockMap(); // Graue 02-29-2004
-		P_LoadLineDefs2();
-		P_GroupLines();
-		numdmstarts = numredctfstarts = numbluectfstarts = 0;
-
-		// reset the player starts
-		for (i = 0; i < MAXPLAYERS; i++)
-			playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
-
-		for (i = 0; i < MAX_DM_STARTS; i++)
-			deathmatchstarts[i] = NULL;
-
-		for (i = 0; i < 2; i++)
-			skyboxmo[i] = NULL;
-
-		for (i = 0; i < 16; i++)
-			skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
+	P_ResetSpawnpoints();
 
-		P_MapStart();
+	P_MapStart();
 
-		P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
-	}
+	if (!P_LoadMapFromFile())
+		return false;
 
 	// init gravity, tag lists,
 	// anything that P_ResetDynamicSlopes/P_LoadThings needs to know
@@ -2991,7 +3572,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	P_ResetDynamicSlopes(fromnetsave);
 #endif
 
-	P_LoadThings(loademblems);
+	P_SpawnMapThings(!fromnetsave);
 	skyboxmo[0] = skyboxviewpnts[0];
 	skyboxmo[1] = skyboxcenterpnts[0];
 
@@ -3002,20 +3583,18 @@ boolean P_SetupLevel(boolean skipprecip)
 	// set up world state
 	P_SpawnSpecials(fromnetsave);
 
-	if (loadprecip) //  ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
+	if (!fromnetsave) //  ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
 		P_SpawnPrecipitation();
 
 #ifdef HWRENDER // not win32 only 19990829 by Kin
-	if (rendermode != render_soft && rendermode != render_none)
-	{
-#ifdef ALAM_LIGHTING
-		// BP: reset light between levels (we draw preview frame lights on current frame)
-		HWR_ResetLights();
-#endif
-		// Correct missing sidedefs & deep water trick
-		HWR_CorrectSWTricks();
-		HWR_CreatePlanePolygons((INT32)numnodes - 1);
-	}
+	// Lactozilla: Free extrasubsectors regardless of renderer.
+	// Maybe we're not in OpenGL anymore.
+	if (extrasubsectors)
+		free(extrasubsectors);
+	extrasubsectors = NULL;
+	// stuff like HWR_CreatePlanePolygons is called there
+	if (rendermode == render_opengl)
+		HWR_SetupLevel();
 #endif
 
 	// oh god I hope this helps
@@ -3023,166 +3602,10 @@ boolean P_SetupLevel(boolean skipprecip)
 	//  none of this needs to be done because it's not the beginning of the map when
 	//  a netgame save is being loaded, and could actively be harmful by messing with
 	//  the client's view of the data.)
-	if (fromnetsave)
-		goto netgameskip;
-	// ==========
-
-	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i])
-		{
-			// Start players with pity shields if possible
-			players[i].pity = -1;
-
-			if (!G_PlatformGametype())
-			{
-				players[i].mo = NULL;
-				G_DoReborn(i);
-			}
-			else // gametype is GT_COOP or GT_RACE
-			{
-				players[i].mo = NULL;
-
-				if (players[i].starposttime)
-				{
-					G_SpawnPlayer(i, true);
-					P_ClearStarPost(players[i].starpostnum);
-				}
-				else
-					G_SpawnPlayer(i, false);
-			}
-		}
-
-	// restore time in netgame (see also g_game.c)
-	if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2)
-	{
-		// is this a hack? maybe
-		tic_t maxstarposttime = 0;
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && players[i].starposttime > maxstarposttime)
-				maxstarposttime = players[i].starposttime;
-		}
-		leveltime = maxstarposttime;
-	}
-
-	if (unlockables[27].unlocked && !modeattacking // pandora's box
-#ifndef DEVELOP
-	&& !modifiedgame
-#endif
-	&& !(netgame || multiplayer) && gamemap == 0x1d35-016464)
-	{
-		P_SpawnMobj(0640370000, 0x11000000, 0b11000110000000000000000000, MT_LETTER)->angle = ANGLE_90;
-		if (textprompts[199]->page[1].backcolor != 259)
-		{
-			char *buf = W_CacheLumpName("WATERMAP", PU_STATIC), *b = buf;
-			while ((*b != 65) && (b-buf < 256)) { *b = (*b - 65)&255; b++; } *b = '\0';
-			Z_Free(textprompts[199]->page[1].text);
-			textprompts[199]->page[1].text = Z_StrDup(buf);
-			textprompts[199]->page[1].lines = 4;
-			textprompts[199]->page[1].backcolor = 259;
-			Z_Free(buf);
-		}
-	}
-
-	if (modeattacking == ATTACKING_RECORD && !demoplayback)
-		P_LoadRecordGhosts();
-	else if (modeattacking == ATTACKING_NIGHTS && !demoplayback)
-		P_LoadNightsGhosts();
-
-	if (G_TagGametype())
-	{
-		INT32 realnumplayers = 0;
-		INT32 playersactive[MAXPLAYERS];
-
-		//I just realized how problematic this code can be.
-		//D_NumPlayers() will not always cover the scope of the netgame.
-		//What if one player is node 0 and the other node 31?
-		//The solution? Make a temp array of all players that are currently playing and pick from them.
-		//Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended?
-		//Also, you'd never have to loop through all 32 players slots to find anything ever again.
-		for (i = 0; i < MAXPLAYERS; i++)
-		{
-			if (playeringame[i] && !players[i].spectator)
-			{
-				playersactive[realnumplayers] = i; //stores the player's node in the array.
-				realnumplayers++;
-			}
-		}
-
-		if (realnumplayers) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked.
-		{
-			i = P_RandomKey(realnumplayers);
-			players[playersactive[i]].pflags |= PF_TAGIT; //choose our initial tagger before map starts.
-
-			// Taken and modified from G_DoReborn()
-			// Remove the player so he can respawn elsewhere.
-			// first dissasociate the corpse
-			if (players[playersactive[i]].mo)
-				P_RemoveMobj(players[playersactive[i]].mo);
-
-			G_SpawnPlayer(playersactive[i], false); //respawn the lucky player in his dedicated spawn location.
-		}
-		else
-			CONS_Printf(M_GetText("No player currently available to become IT. Awaiting available players.\n"));
-
-	}
-	else if (gametype == GT_RACE && server)
-		CV_StealthSetValue(&cv_numlaps,
-			(cv_basenumlaps.value)
-			? cv_basenumlaps.value
-			: mapheaderinfo[gamemap - 1]->numlaps);
-
-	// ===========
-	// landing point for netgames.
-	netgameskip:
-
-	if (!dedicated)
-	{
-		P_SetupCamera();
-
-		// Salt: CV_ClearChangedFlags() messes with your settings :(
-		/*if (!cv_cam_height.changed)
-			CV_Set(&cv_cam_height, cv_cam_height.defaultvalue);
-		if (!cv_cam2_height.changed)
-			CV_Set(&cv_cam2_height, cv_cam2_height.defaultvalue);
-
-		if (!cv_cam_dist.changed)
-			CV_Set(&cv_cam_dist, cv_cam_dist.defaultvalue);
-		if (!cv_cam2_dist.changed)
-			CV_Set(&cv_cam2_dist, cv_cam2_dist.defaultvalue);*/
-
-		// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
-		if (!cv_cam_rotate.changed)
-			CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
-		if (!cv_cam2_rotate.changed)
-			CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
-
-		if (!cv_analog.changed)
-			CV_SetValue(&cv_analog, 0);
-		if (!cv_analog2.changed)
-			CV_SetValue(&cv_analog2, 0);
-
-#ifdef HWRENDER
-		if (rendermode != render_soft && rendermode != render_none)
-			CV_Set(&cv_grfov, cv_grfov.defaultvalue);
-#endif
-
-		displayplayer = consoleplayer; // Start with your OWN view, please!
-	}
-
-	if (cv_useranalog.value)
-		CV_SetValue(&cv_analog, true);
-
-	if (splitscreen && cv_useranalog2.value)
-		CV_SetValue(&cv_analog2, true);
-	else if (botingame)
-		CV_SetValue(&cv_analog2, true);
+	if (!fromnetsave)
+		P_InitGametype();
 
-	if (twodlevel)
-	{
-		CV_SetValue(&cv_analog2, false);
-		CV_SetValue(&cv_analog, false);
-	}
+	P_InitCamera();
 
 	// clear special respawning que
 	iquehead = iquetail = 0;
@@ -3190,14 +3613,6 @@ boolean P_SetupLevel(boolean skipprecip)
 	// Fab : 19-07-98 : start cd music for this level (note: can be remapped)
 	I_PlayCD((UINT8)(gamemap), false);
 
-	// preload graphics
-#ifdef HWRENDER // not win32 only 19990829 by Kin
-	if (rendermode != render_soft && rendermode != render_none)
-	{
-		HWR_PrepLevelCache(numtextures);
-	}
-#endif
-
 	P_MapEnd();
 
 	// Remove the loading shit from the screen
@@ -3225,19 +3640,7 @@ boolean P_SetupLevel(boolean skipprecip)
 
 	lastmaploaded = gamemap; // HAS to be set after saving!!
 
-	if (savedata.lives > 0)
-	{
-		numgameovers = savedata.numgameovers;
-		players[consoleplayer].continues = savedata.continues;
-		players[consoleplayer].lives = savedata.lives;
-		players[consoleplayer].score = savedata.score;
-		if ((botingame = ((botskin = savedata.botskin) != 0)))
-			botcolor = skins[botskin-1].prefcolor;
-		emeralds = savedata.emeralds;
-		savedata.lives = 0;
-	}
-
-	if (loadprecip) // uglier hack
+	if (!fromnetsave) // uglier hack
 	{ // to make a newly loaded level start on the second frame.
 		INT32 buf = gametic % BACKUPTICS;
 		for (i = 0; i < MAXPLAYERS; i++)
@@ -3266,11 +3669,31 @@ boolean P_SetupLevel(boolean skipprecip)
 
 	// If so...
 	if ((!(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)) && (*mapheaderinfo[gamemap-1]->lvlttl != '\0'))
-		G_PreLevelTitleCard(lt_ticker, true);
+		G_PreLevelTitleCard();
 
 	return true;
 }
 
+#ifdef HWRENDER
+void HWR_SetupLevel(void)
+{
+	// Lactozilla (December 8, 2019)
+	// Level setup used to free EVERY mipmap from memory.
+	// Even mipmaps that aren't related to level textures.
+	// Presumably, the hardware render code used to store textures as level data.
+	// Meaning, they had memory allocated and marked with the PU_LEVEL tag.
+	// Level textures are only reloaded after R_LoadTextures, which is
+	// when the texture list is loaded.
+#ifdef ALAM_LIGHTING
+	// BP: reset light between levels (we draw preview frame lights on current frame)
+	HWR_ResetLights();
+#endif
+	// Correct missing sidedefs & deep water trick
+	HWR_CorrectSWTricks();
+	HWR_CreatePlanePolygons((INT32)numnodes - 1);
+}
+#endif
+
 //
 // P_RunSOC
 //
@@ -3379,10 +3802,10 @@ boolean P_AddWadFile(const char *wadfilename)
 	// WADs use markers for some resources, but others such as sounds are checked lump-by-lump anyway.
 //	UINT16 luaPos, luaNum = 0;
 //	UINT16 socPos, socNum = 0;
-	UINT16 sfxPos, sfxNum = 0;
+	UINT16 sfxPos = 0, sfxNum = 0;
 	UINT16 musPos = 0, musNum = 0;
 //	UINT16 sprPos, sprNum = 0;
-	UINT16 texPos, texNum = 0;
+	UINT16 texPos = 0, texNum = 0;
 //	UINT16 patPos, patNum = 0;
 //	UINT16 flaPos, flaNum = 0;
 //	UINT16 mapPos, mapNum = 0;
diff --git a/src/p_setup.h b/src/p_setup.h
index d8787073b08c823630e4cc41d1484f3ec8bd2c3b..d7e2d886159b6b71cfe240c13902cb780328b6c4 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -96,8 +96,11 @@ void P_SetupLevelSky(INT32 skynum, boolean global);
 #ifdef SCANTHINGS
 void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum);
 #endif
-void P_LoadThingsOnly(void);
-boolean P_SetupLevel(boolean skipprecip);
+void P_RespawnThings(void);
+boolean P_LoadLevel(boolean fromnetsave);
+#ifdef HWRENDER
+void HWR_SetupLevel(void);
+#endif
 boolean P_AddWadFile(const char *wadfilename);
 boolean P_RunSOC(const char *socfilename);
 void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num);
diff --git a/src/p_sight.c b/src/p_sight.c
index aa59314f19728b6376d6ceddd2a083e44254142f..07dfabbc1ed34831a5d379cbbc1fefe559b1a0ce 100644
--- a/src/p_sight.c
+++ b/src/p_sight.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -222,6 +222,9 @@ static boolean P_CrossSubsector(size_t num, register los_t *los)
 		fixed_t fracx, fracy;
 #endif
 
+		if (seg->glseg)
+			continue;
+
 		// already checked other side?
 		if (line->validcount == validcount)
 			continue;
diff --git a/src/p_slopes.c b/src/p_slopes.c
index b06460084dee76bde47435815b85470cc03d5ab1..2cf2c74ba83e1b3bf07b4a743c521d2ee6cc6652 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2004      by Stephen McGranahan
-// Copyright (C) 2015-2018 by Sonic Team Junior.
+// Copyright (C) 2015-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -50,10 +50,10 @@ static void ReconfigureViaVertexes (pslope_t *slope, const vector3_t v1, const v
 	// Set some defaults for a non-sloped "slope"
 	if (vec1.z == 0 && vec2.z == 0)
 	{
-		/// \todo Fix fully flat cases.
-
 		slope->zangle = slope->xydirection = 0;
 		slope->zdelta = slope->d.x = slope->d.y = 0;
+		slope->normal.x = slope->normal.y = 0;
+		slope->normal.z = FRACUNIT;
 	}
 	else
 	{
@@ -445,10 +445,9 @@ static pslope_t *MakeViaMapthings(INT16 tag1, INT16 tag2, INT16 tag3, UINT8 flag
 			I_Error("MakeViaMapthings: Slope vertex %s (for linedef tag %d) not found!", sizeu1(i), tag1);
 		vx[i].x = mt->x << FRACBITS;
 		vx[i].y = mt->y << FRACBITS;
-		if (mt->extrainfo)
-			vx[i].z = mt->options << FRACBITS;
-		else
-			vx[i].z = (R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS)->sector->floorheight) + ((mt->options >> ZSHIFT) << FRACBITS);
+		vx[i].z = mt->z << FRACBITS;
+		if (!mt->extrainfo)
+			vx[i].z += R_PointInSubsector(vx[i].x, vx[i].y)->sector->floorheight;
 	}
 
 	ReconfigureViaVertexes(ret, vx[0], vx[1], vx[2]);
@@ -553,11 +552,8 @@ pslope_t *P_SlopeById(UINT16 id)
 }
 
 /// Reset slopes and read them from special lines.
-void P_ResetDynamicSlopes(const UINT32 fromsave) {
+void P_ResetDynamicSlopes(const boolean fromsave) {
 	size_t i;
-
-	boolean spawnthinkers = !(boolean)fromsave;
-
 	slopelist = NULL;
 	slopecount = 0;
 
@@ -574,14 +570,14 @@ void P_ResetDynamicSlopes(const UINT32 fromsave) {
 			case 711:
 			case 712:
 			case 713:
-				line_SpawnViaLine(i, spawnthinkers);
+				line_SpawnViaLine(i, !fromsave);
 				break;
 
 			case 704:
 			case 705:
 			case 714:
 			case 715:
-				line_SpawnViaVertexes(i, spawnthinkers);
+				line_SpawnViaVertexes(i, !fromsave);
 				break;
 
 			default:
@@ -657,7 +653,9 @@ void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope)
 // Handles slope ejection for objects
 void P_SlopeLaunch(mobj_t *mo)
 {
-	if (!(mo->standingslope->flags & SL_NOPHYSICS)) // If there's physics, time for launching.
+	if (!(mo->standingslope->flags & SL_NOPHYSICS) // If there's physics, time for launching.
+		&& (mo->standingslope->normal.x != 0
+		||  mo->standingslope->normal.y != 0))
 	{
 		// Double the pre-rotation Z, then halve the post-rotation Z. This reduces the
 		// vertical launch given from slopes while increasing the horizontal launch
@@ -714,8 +712,7 @@ fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope)
 void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
 {
 	vector3_t mom; // Ditto.
-
-	if (slope->flags & SL_NOPHYSICS) { // No physics, no need to make anything complicated.
+	if (slope->flags & SL_NOPHYSICS || (slope->normal.x == 0 && slope->normal.y == 0)) { // No physics, no need to make anything complicated.
 		if (P_MobjFlip(thing)*(thing->momz) < 0) // falling, land on slope
 		{
 			thing->standingslope = slope;
diff --git a/src/p_slopes.h b/src/p_slopes.h
index 8f9e7d61e5bf135e82b42c56593465a0f5fc73e0..96764051b428cf013fb503db03fff1b6de5ff23c 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2004      by Stephen McGranahan
-// Copyright (C) 2015-2018 by Sonic Team Junior.
+// Copyright (C) 2015-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -23,7 +23,7 @@ extern UINT16 slopecount;
 void P_LinkSlopeThinkers (void);
 
 void P_CalculateSlopeNormal(pslope_t *slope);
-void P_ResetDynamicSlopes(const UINT32 fromsave);
+void P_ResetDynamicSlopes(const boolean fromsave);
 
 //
 // P_CopySectorSlope
diff --git a/src/p_spec.c b/src/p_spec.c
index f814e89c66e9a0892e2b94fe6ea680997442985e..00a71602ba21cb94c9a1e179557131e3866930cb 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -52,9 +52,6 @@ mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
 // Amount (dx, dy) vector linedef is shifted right to get scroll amount
 #define SCROLL_SHIFT 5
 
-// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
-#define MAXFLATSIZE (2048<<FRACBITS)
-
 /** Animated texture descriptor
   * This keeps track of an animated texture or an animated flat.
   * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t
@@ -138,6 +135,13 @@ static size_t maxanims;
 
 static animdef_t *animdefs = NULL;
 
+// Increase the size of animdefs to make room for a new animation definition
+static void GrowAnimDefs(void)
+{
+	maxanims++;
+	animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
+}
+
 // A prototype; here instead of p_spec.h, so they're "private"
 void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum);
 void P_ParseAnimationDefintion(SINT8 istexture);
@@ -347,8 +351,7 @@ void P_ParseAnimationDefintion(SINT8 istexture)
 	if (i == maxanims)
 	{
 		// Increase the size to make room for the new animation definition
-		maxanims++;
-		animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
+		GrowAnimDefs();
 		strncpy(animdefs[i].startname, animdefsToken, 9);
 	}
 
@@ -434,8 +437,17 @@ void P_ParseAnimationDefintion(SINT8 istexture)
 	}
 	animdefs[i].speed = animSpeed;
 	Z_Free(animdefsToken);
-}
 
+#ifdef WALLFLATS
+	// hehe... uhh.....
+	if (!istexture)
+	{
+		GrowAnimDefs();
+		M_Memcpy(&animdefs[maxanims-1], &animdefs[i], sizeof(animdef_t));
+		animdefs[maxanims-1].istexture = 1;
+	}
+#endif
+}
 
 /** Checks for flats in levelflats that are part of a flat animation sequence
   * and sets them up for animation.
@@ -476,7 +488,8 @@ static inline void P_FindAnimatedFlat(INT32 animnum)
 					atoi(sizeu1(i)), foundflats->name, foundflats->animseq,
 					foundflats->numpics,foundflats->speed);
 		}
-		else if (foundflats->u.flat.lumpnum >= startflatnum && foundflats->u.flat.lumpnum <= endflatnum)
+		else if ((!anims[animnum].istexture) && (foundflats->type == LEVELFLAT_FLAT)
+			&& (foundflats->u.flat.lumpnum >= startflatnum && foundflats->u.flat.lumpnum <= endflatnum))
 		{
 			foundflats->u.flat.baselumpnum = startflatnum;
 			foundflats->animseq = foundflats->u.flat.lumpnum - startflatnum;
@@ -1587,8 +1600,6 @@ static inline void P_InitTagLists(void)
 		size_t j = (unsigned)sectors[i].tag % numsectors;
 		sectors[i].nexttag = sectors[j].firsttag;
 		sectors[j].firsttag = (INT32)i;
-		sectors[i].spawn_nexttag = sectors[i].nexttag;
-		sectors[j].spawn_firsttag = sectors[j].firsttag;
 	}
 
 	for (i = numlines - 1; i != (size_t)-1; i--)
@@ -2721,6 +2732,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 
 				if (line->tag != 0) // Do special stuff only if a non-zero linedef tag is set
 				{
+					// Play sounds from tagged sectors' origins.
 					if (line->flags & ML_EFFECT5) // Repeat Midtexture
 					{
 						// Additionally play the sound from tagged sectors' soundorgs
@@ -2732,31 +2744,45 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 							S_StartSound(&sec->soundorg, sfxnum);
 						}
 					}
-					else if (mo) // A mobj must have triggered the executor
+
+					// Play the sound without origin for anyone, as long as they're inside tagged areas.
+					else
 					{
-						// Only trigger if mobj is touching the tag
+						UINT8 i = 0;
+						mobj_t* camobj = players[displayplayer].mo;
 						ffloor_t *rover;
 						boolean foundit = false;
 
-						for(rover = mo->subsector->sector->ffloors; rover; rover = rover->next)
+						for (i = 0; i < 2; camobj = players[secondarydisplayplayer].mo, i++)
 						{
-							if (rover->master->frontsector->tag != line->tag)
+							if (!camobj)
 								continue;
 
-							if (mo->z > P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector))
-								continue;
+							if (foundit || (camobj->subsector->sector->tag == line->tag))
+							{
+								foundit = true;
+								break;
+							}
 
-							if (mo->z + mo->height < P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector))
-								continue;
+							// Only trigger if mobj is touching the tag
+							for(rover = camobj->subsector->sector->ffloors; rover; rover = rover->next)
+							{
+								if (rover->master->frontsector->tag != line->tag)
+									continue;
 
-							foundit = true;
-						}
+								if (camobj->z > P_GetSpecialTopZ(camobj, sectors + rover->secnum, camobj->subsector->sector))
+									continue;
 
-						if (mo->subsector->sector->tag == line->tag)
-							foundit = true;
+								if (camobj->z + camobj->height < P_GetSpecialBottomZ(camobj, sectors + rover->secnum, camobj->subsector->sector))
+									continue;
 
-						if (!foundit)
-							return;
+								foundit = true;
+								break;
+							}
+						}
+
+						if (foundit)
+							S_StartSound(NULL, sfxnum);
 					}
 				}
 				else
@@ -4072,8 +4098,10 @@ void P_SetupSignExit(player_t *player)
 		if (thing->type != MT_SIGN)
 			continue;
 
-		if (!player->mo->target || player->mo->target->type != MT_SIGN)
-			P_SetTarget(&player->mo->target, thing);
+		if (!numfound
+			&& !(player->mo->target && player->mo->target->type == MT_SIGN)
+			&& !(gametype == GT_COOP && (netgame || multiplayer) && cv_exitmove.value))
+				P_SetTarget(&player->mo->target, thing);
 
 		if (thing->state != &states[thing->info->spawnstate])
 			continue;
@@ -4101,8 +4129,10 @@ void P_SetupSignExit(player_t *player)
 		if (thing->type != MT_SIGN)
 			continue;
 
-		if (!player->mo->target || player->mo->target->type != MT_SIGN)
-			P_SetTarget(&player->mo->target, thing);
+		if (!numfound
+			&& !(player->mo->target && player->mo->target->type == MT_SIGN)
+			&& !(gametype == GT_COOP && (netgame || multiplayer) && cv_exitmove.value))
+				P_SetTarget(&player->mo->target, thing);
 
 		if (thing->state != &states[thing->info->spawnstate])
 			continue;
@@ -4659,7 +4689,7 @@ DoneSection2:
 		}
 
 		case 2: // Special stage GOAL sector / Exit Sector / CTF Flag Return
-			if (player->bot)
+			if (player->bot || !(gametyperules & GTR_ALLOWEXIT))
 				break;
 			if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && player->nightstime > 6)
 			{
@@ -4694,7 +4724,7 @@ DoneSection2:
 			break;
 
 		case 3: // Red Team's Base
-			if (gametype == GT_CTF && P_IsObjectOnGround(player->mo))
+			if ((gametyperules & GTR_TEAMFLAGS) && P_IsObjectOnGround(player->mo))
 			{
 				if (player->ctfteam == 1 && (player->gotflag & GF_BLUEFLAG))
 				{
@@ -4727,7 +4757,7 @@ DoneSection2:
 			break;
 
 		case 4: // Blue Team's Base
-			if (gametype == GT_CTF && P_IsObjectOnGround(player->mo))
+			if ((gametyperules & GTR_TEAMFLAGS) && P_IsObjectOnGround(player->mo))
 			{
 				if (player->ctfteam == 2 && (player->gotflag & GF_REDFLAG))
 				{
@@ -5008,8 +5038,7 @@ DoneSection2:
 				mobj_t *waypointlow = NULL;
 				mobj_t *mo2;
 				mobj_t *closest = NULL;
-				line_t junk;
-				vertex_t v1, v2, resulthigh, resultlow;
+				vector3_t p, line[2], resulthigh, resultlow;
 				mobj_t *highest = NULL;
 
 				if (player->mo->tracer && player->mo->tracer->type == MT_TUBEWAYPOINT && player->powers[pw_carry] == CR_ROPEHANG)
@@ -5156,38 +5185,34 @@ DoneSection2:
 				// Next, we need to find the closest point on the line between each set, and determine which one we're
 				// closest to.
 
+				p.x = player->mo->x;
+				p.y = player->mo->y;
+				p.z = player->mo->z;
+
 				// Waypointmid and Waypointlow:
 				if (waypointlow)
 				{
-					v1.x = waypointmid->x;
-					v1.y = waypointmid->y;
-					v1.z = waypointmid->z;
-					v2.x = waypointlow->x;
-					v2.y = waypointlow->y;
-					v2.z = waypointlow->z;
-					junk.v1 = &v1;
-					junk.v2 = &v2;
-					junk.dx = v2.x - v1.x;
-					junk.dy = v2.y - v1.y;
+					line[0].x = waypointmid->x;
+					line[0].y = waypointmid->y;
+					line[0].z = waypointmid->z;
+					line[1].x = waypointlow->x;
+					line[1].y = waypointlow->y;
+					line[1].z = waypointlow->z;
 
-					P_ClosestPointOnLine3D(player->mo->x, player->mo->y, player->mo->z, &junk, &resultlow);
+					P_ClosestPointOnLine3D(&p, line, &resultlow);
 				}
 
 				// Waypointmid and Waypointhigh:
 				if (waypointhigh)
 				{
-					v1.x = waypointmid->x;
-					v1.y = waypointmid->y;
-					v1.z = waypointmid->z;
-					v2.x = waypointhigh->x;
-					v2.y = waypointhigh->y;
-					v2.z = waypointhigh->z;
-					junk.v1 = &v1;
-					junk.v2 = &v2;
-					junk.dx = v2.x - v1.x;
-					junk.dy = v2.y - v1.y;
+					line[0].x = waypointmid->x;
+					line[0].y = waypointmid->y;
+					line[0].z = waypointmid->z;
+					line[1].x = waypointhigh->x;
+					line[1].y = waypointhigh->y;
+					line[1].z = waypointhigh->z;
 
-					P_ClosestPointOnLine3D(player->mo->x, player->mo->y, player->mo->z, &junk, &resulthigh);
+					P_ClosestPointOnLine3D(&p, line, &resulthigh);
 				}
 
 				// 3D support now available. Disregard the previous notice here. -Red
@@ -5607,7 +5632,7 @@ void P_UpdateSpecials(void)
 		if (foundflats->speed) // it is an animated flat
 		{
 			// update the levelflat texture number
-			if (foundflats->type == LEVELFLAT_TEXTURE)
+			if ((foundflats->type == LEVELFLAT_TEXTURE) && (foundflats->u.texture.basenum != -1))
 				foundflats->u.texture.num = foundflats->u.texture.basenum + ((leveltime/foundflats->speed + foundflats->animseq) % foundflats->numpics);
 			// update the levelflat lump number
 			else if ((foundflats->type == LEVELFLAT_FLAT) && (foundflats->u.flat.baselumpnum != LUMPERROR))
@@ -6370,22 +6395,16 @@ static void P_ApplyFlatAlignment(line_t *master, sector_t *sector, angle_t flata
 {
 	if (!(master->flags & ML_NETONLY)) // Modify floor flat alignment unless ML_NETONLY flag is set
 	{
-		sector->spawn_flrpic_angle = sector->floorpic_angle = flatangle;
+		sector->floorpic_angle = flatangle;
 		sector->floor_xoffs += xoffs;
 		sector->floor_yoffs += yoffs;
-		// saved for netgames
-		sector->spawn_flr_xoffs = sector->floor_xoffs;
-		sector->spawn_flr_yoffs = sector->floor_yoffs;
 	}
 
 	if (!(master->flags & ML_NONET)) // Modify ceiling flat alignment unless ML_NONET flag is set
 	{
-		sector->spawn_ceilpic_angle = sector->ceilingpic_angle = flatangle;
+		sector->ceilingpic_angle = flatangle;
 		sector->ceiling_xoffs += xoffs;
 		sector->ceiling_yoffs += yoffs;
-		// saved for netgames
-		sector->spawn_ceil_xoffs = sector->ceiling_xoffs;
-		sector->spawn_ceil_yoffs = sector->ceiling_yoffs;
 	}
 
 }
@@ -6399,14 +6418,13 @@ static void P_ApplyFlatAlignment(line_t *master, sector_t *sector, angle_t flata
   *       as they'll just be erased by UnArchiveThinkers.
   * \sa P_SpawnPrecipitation, P_SpawnFriction, P_SpawnPushers, P_SpawnScrollers
   */
-void P_SpawnSpecials(INT32 fromnetsave)
+void P_SpawnSpecials(boolean fromnetsave)
 {
 	sector_t *sector;
 	size_t i;
 	INT32 j;
 	thinkerlist_t *secthinkers;
 	thinker_t *th;
-
 	// This used to be used, and *should* be used in the future,
 	// but currently isn't.
 	(void)fromnetsave;
@@ -7152,46 +7170,14 @@ void P_SpawnSpecials(INT32 fromnetsave)
 					EV_AddLaserThinker(&sectors[s], &sectors[sec], lines + i, secthinkers);
 				break;
 
-			case 259: // Make-Your-Own FOF!
+			case 259: // Custom FOF
 				if (lines[i].sidenum[1] != 0xffff)
 				{
-					UINT8 *data;
-					UINT16 b;
-
-					if (W_IsLumpWad(lastloadedmaplumpnum)) // welp it's a map wad in a pk3
-					{ // HACK: Open wad file rather quickly so we can get the data from the sidedefs lump
-						UINT8 *wadData = W_CacheLumpNum(lastloadedmaplumpnum, PU_STATIC);
-						filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
-						fileinfo += ML_SIDEDEFS; // we only need the SIDEDEFS lump
-						data = Z_Malloc(fileinfo->size, PU_STATIC, NULL);
-						M_Memcpy(data, wadData + fileinfo->filepos, fileinfo->size); // copy data
-						Z_Free(wadData); // we're done with this now
-					}
-					else // phew it's just a WAD
-						data = W_CacheLumpNum(lastloadedmaplumpnum + ML_SIDEDEFS,PU_STATIC);
-
-					for (b = 0; b < (INT16)numsides; b++)
-					{
-						register mapsidedef_t *msd = (mapsidedef_t *)data + b;
-
-						if (b == lines[i].sidenum[1])
-						{
-							if ((msd->toptexture[0] >= '0' && msd->toptexture[0] <= '9')
-								|| (msd->toptexture[0] >= 'A' && msd->toptexture[0] <= 'F'))
-							{
-								ffloortype_e FOF_Flags = axtoi(msd->toptexture);
-
-								P_AddFakeFloorsByLine(i, FOF_Flags, secthinkers);
-								break;
-							}
-							else
-								I_Error("Make-Your-Own-FOF (tag %d) needs a value in the linedef's second side upper texture field.", lines[i].tag);
-						}
-					}
-					Z_Free(data);
+					ffloortype_e fofflags = sides[lines[i].sidenum[1]].toptexture;
+					P_AddFakeFloorsByLine(i, fofflags, secthinkers);
 				}
 				else
-					I_Error("Make-Your-Own FOF (tag %d) found without a 2nd linedef side!", lines[i].tag);
+					I_Error("Custom FOF (tag %d) found without a linedef back side!", lines[i].tag);
 				break;
 
 			case 300: // Linedef executor (combines with sector special 974/975) and commands
@@ -7205,14 +7191,14 @@ void P_SpawnSpecials(INT32 fromnetsave)
 				break;
 
 			case 308: // Race-only linedef executor. Triggers once.
-				if (gametype != GT_RACE && gametype != GT_COMPETITION)
+				if (!(gametyperules & GTR_RACE))
 					lines[i].special = 0;
 				break;
 
 			// Linedef executor triggers for CTF teams.
 			case 309:
 			case 311:
-				if (gametype != GT_CTF)
+				if (!(gametyperules & GTR_TEAMFLAGS))
 					lines[i].special = 0;
 				break;
 
diff --git a/src/p_spec.h b/src/p_spec.h
index d7176afee73e7b4bb3a8356744c6633f60070cdc..4b64fe05d9bed944511f852b84f70cf86db000a9 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -27,6 +27,9 @@ extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
 //
 #define GETSECSPECIAL(i,j) ((i >> ((j-1)*4))&15)
 
+// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
+#define MAXFLATSIZE (2048<<FRACBITS)
+
 // at game start
 void P_InitPicAnims(void);
 
@@ -35,7 +38,7 @@ void P_SetupLevelFlatAnims(void);
 
 // at map load
 void P_InitSpecials(void);
-void P_SpawnSpecials(INT32 fromnetsave);
+void P_SpawnSpecials(boolean fromnetsave);
 
 // every tic
 void P_UpdateSpecials(void);
diff --git a/src/p_telept.c b/src/p_telept.c
index 632b81e0488bc3ebeb1306365f2f7e2a476e248f..69729d208353d646fcde43780711beff7e01936e 100644
--- a/src/p_telept.c
+++ b/src/p_telept.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_tick.c b/src/p_tick.c
index e02b11f4907fd5d87bb722467485b0409f66a28e..9b56ee1c2d2ed51c8c4e91e421751c80fd7f40e7 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -481,6 +481,9 @@ static inline void P_DoSpecialStageStuff(void)
 				tic_t oldnightstime = players[i].nightstime;
 				countspheres += players[i].spheres;
 
+				if (!oldnightstime)
+					continue;
+
 				// If in water, deplete timer 6x as fast.
 				if (players[i].mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER) && !(players[i].powers[pw_shield] & SH_PROTECTWATER))
 					players[i].nightstime -= 5;
@@ -506,12 +509,11 @@ static inline void P_DoSpecialStageStuff(void)
 			{
 				// Halt all the players
 				for (i = 0; i < MAXPLAYERS; i++)
-					if (playeringame[i])
+					if (playeringame[i] && !players[i].exiting)
 					{
 						players[i].mo->momx = players[i].mo->momy = 0;
 						players[i].exiting = (14*TICRATE)/5 + 1;
 					}
-
 				sstimer = 0;
 				P_GiveEmerald(true);
 				P_RestoreMusic(&players[consoleplayer]);
@@ -627,6 +629,10 @@ void P_Ticker(boolean run)
 		if (demoplayback)
 			G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0);
 
+		#ifdef HAVE_BLUA
+		LUAh_PreThinkFrame();
+		#endif
+
 		for (i = 0; i < MAXPLAYERS; i++)
 			if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
 				P_PlayerThink(&players[i]);
@@ -724,6 +730,10 @@ void P_Ticker(boolean run)
 			G_ConsGhostTic();
 		if (modeattacking)
 			G_GhostTicker();
+
+#ifdef HAVE_BLUA
+		LUAh_PostThinkFrame();
+#endif
 	}
 
 	P_MapEnd();
@@ -743,6 +753,9 @@ void P_PreTicker(INT32 frames)
 	{
 		P_MapStart();
 
+#ifdef HAVE_BLUA
+		LUAh_PreThinkFrame();
+#endif
 		for (i = 0; i < MAXPLAYERS; i++)
 			if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
 			{
@@ -777,6 +790,10 @@ void P_PreTicker(INT32 frames)
 		P_UpdateSpecials();
 		P_RespawnSpecials();
 
+#ifdef HAVE_BLUA
+		LUAh_PostThinkFrame();
+#endif
+
 		P_MapEnd();
 	}
 }
diff --git a/src/p_tick.h b/src/p_tick.h
index 8551ccc7606015b895c928c197328f2ee1247289..a9501f62fdec3db74df3e1549d1a80908d687904 100644
--- a/src/p_tick.h
+++ b/src/p_tick.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/p_user.c b/src/p_user.c
index 0838ff80e8dd7e1850b6c689dafea5d70859e167..d0b49000fb61adf01b723dde4a11c7e80a176b36 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -343,13 +343,20 @@ void P_GiveEmerald(boolean spawnObj)
 				continue;
 
 			emmo = P_SpawnMobjFromMobj(players[i].mo, 0, 0, players[i].mo->height, MT_GOTEMERALD);
+			if (!emmo)
+				continue;
 			P_SetTarget(&emmo->target, players[i].mo);
 			P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
+			
+			// Make sure we're not being carried before our tracer is changed
+			if (players[i].powers[pw_carry] != CR_NIGHTSMODE)
+				players[i].powers[pw_carry] = CR_NONE;
+			
 			P_SetTarget(&players[i].mo->tracer, emmo);
 
 			if (pnum == 255)
 			{
-				i = pnum;
+				pnum = i;
 				continue;
 			}
 
@@ -385,7 +392,7 @@ UINT8 P_FindLowestMare(void)
 	mobj_t *mo2;
 	UINT8 mare = UINT8_MAX;
 
-	if (gametype == GT_RACE || gametype == GT_COMPETITION)
+	if (gametyperules & GTR_RACE)
 		return 0;
 
 	// scan the thinkers
@@ -636,9 +643,7 @@ static void P_DeNightserizePlayer(player_t *player)
 	player->marebonuslap = 0;
 	player->flyangle = 0;
 	player->anotherflyangle = 0;
-#ifdef ROTSPRITE
 	player->mo->rollangle = 0;
-#endif
 
 	P_SetTarget(&player->mo->target, NULL);
 	P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL));
@@ -766,9 +771,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 	player->secondjump = 0;
 	player->flyangle = 0;
 	player->anotherflyangle = 0;
-#ifdef ROTSPRITE
 	player->mo->rollangle = 0;
-#endif
 
 	player->powers[pw_shield] = SH_NONE;
 	player->powers[pw_super] = 0;
@@ -791,7 +794,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		P_RestoreMusic(player);
 	}
 
-	if (gametype == GT_RACE || gametype == GT_COMPETITION)
+	if (gametyperules & GTR_RACE)
 	{
 		if (player->drillmeter < 48*20)
 			player->drillmeter = 48*20;
@@ -1223,6 +1226,7 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
 //
 void P_GivePlayerLives(player_t *player, INT32 numlives)
 {
+	UINT8 prevlives = player->lives;
 	if (!player)
 		return;
 
@@ -1231,18 +1235,17 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
 
 	if (gamestate == GS_LEVEL)
 	{
-		if (player->lives == INFLIVES || (gametype != GT_COOP && gametype != GT_COMPETITION))
+		if (player->lives == INFLIVES || !(gametyperules & GTR_LIVES))
 		{
 			P_GivePlayerRings(player, 100*numlives);
 			return;
 		}
 
-		if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0)
+		if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0)
 		{
-			UINT8 prevlives = player->lives;
 			P_GivePlayerRings(player, 100*numlives);
 			if (player->lives - prevlives >= numlives)
-				return;
+				goto docooprespawn;
 
 			numlives = (numlives + prevlives - player->lives);
 		}
@@ -1256,11 +1259,20 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
 		player->lives = 99;
 	else if (player->lives < 1)
 		player->lives = 1;
+
+docooprespawn:
+	if (cv_coopstarposts.value)
+		return;
+	if (prevlives > 0)
+		return;
+	if (!player->spectator)
+		return;
+	P_SpectatorJoinGame(player);
 }
 
 void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound)
 {
-	if (!((netgame || multiplayer) && gametype == GT_COOP))
+	if (!((netgame || multiplayer) && G_GametypeUsesCoopLives()))
 	{
 		P_GivePlayerLives(player, numlives);
 		if (sound)
@@ -1388,7 +1400,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
 		player->score = MAXSCORE;
 
 	// check for extra lives every 50000 pts
-	if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametype == GT_COMPETITION || gametype == GT_COOP))
+	if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametyperules & GTR_LIVES))
 	{
 		P_GivePlayerLives(player, (player->score/50000) - (oldscore/50000));
 		P_PlayLivesJingle(player);
@@ -1589,7 +1601,7 @@ void P_RestoreMusic(player_t *player)
 		P_PlayJingle(player, JT_SUPER);
 
 	// Invulnerability
-	else if (player->powers[pw_invulnerability] > 1)
+	else if (player->powers[pw_invulnerability] > 1 && !player->powers[pw_super])
 	{
 		strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
 		S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
@@ -2153,6 +2165,10 @@ void P_DoPlayerFinish(player_t *player)
 
 	if (netgame)
 		CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);
+
+	player->powers[pw_underwater] = 0;
+	player->powers[pw_spacetime] = 0;
+	P_RestoreMusic(player);
 }
 
 //
@@ -2166,7 +2182,7 @@ void P_DoPlayerExit(player_t *player)
 
 	if (cv_allowexitlevel.value == 0 && !G_PlatformGametype())
 		return;
-	else if (gametype == GT_RACE || gametype == GT_COMPETITION) // If in Race Mode, allow
+	else if (gametyperules & GTR_RACE) // If in Race Mode, allow
 	{
 		if (!countdown) // a 60-second wait ala Sonic 2.
 			countdown = (cv_countdowntime.value - 1)*TICRATE + 1; // Use cv_countdowntime
@@ -2296,7 +2312,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
 				else if (!player->skidtime)
 					player->pflags &= ~PF_GLIDING;
 			}
-			else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & PF_SHIELDABILITY) && player->mo->state-states == S_PLAY_FALL)
+			else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & (PF_JUMPED|PF_SHIELDABILITY)) && player->mo->state-states == S_PLAY_FALL)
 			{
 				if (player->mo->state-states != S_PLAY_GLIDE_LANDING)
 				{
@@ -2872,7 +2888,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
 {
 	tic_t timeleft = (player->powers[pw_spacetime]) ? player->powers[pw_spacetime] : player->powers[pw_underwater];
 
-	if (player->exiting)
+	if (player->exiting || (player->pflags & PF_FINISHED))
 		player->powers[pw_underwater] = player->powers[pw_spacetime] = 0;
 
 	timeleft--; // The original code was all n*TICRATE + 1, so let's remove 1 tic for simplicity
@@ -3095,7 +3111,7 @@ static void P_DoPlayerHeadSigns(player_t *player)
 			}
 		}
 	}
-	else if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) // If you have the flag (duh).
+	else if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) // If you have the flag (duh).
 	{
 		// Spawn a got-flag message over the head of the player that
 		// has it (but not on your own screen if you have the flag).
@@ -3523,7 +3539,7 @@ static void P_DoClimbing(player_t *player)
 			{
 				P_SetObjectMomZ(player->mo, 2*FRACUNIT, true);
 				if (cmd->forwardmove)
-					P_SetObjectMomZ(player->mo, 2*player->mo->momz/3, false);
+					player->mo->momz = 2*player->mo->momz/3;
 			}
 			if (thrust)
 				P_Thrust(player->mo, player->mo->angle, FixedMul(4*FRACUNIT, player->mo->scale)); // Lil' boost up.
@@ -4608,18 +4624,31 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 					player->dashspeed = player->mindash;
 					P_SetPlayerMobjState(player->mo, S_PLAY_SPINDASH);
 					if (!player->spectator)
-						S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
+						S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
 				}
 				 // Revving
 				else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
 				{
-					if (player->dashspeed < player->maxdash)
+					if (player->speed > 5*player->mo->scale)
+					{
+						player->pflags &= ~PF_STARTDASH;
+						P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
+						S_StartSound(player->mo, sfx_spin);
+						break;
+					}
+					if (player->dashspeed < player->mindash)
+						player->dashspeed = player->mindash;
+
+					if (player->dashspeed > player->maxdash)
+						player->dashspeed = player->maxdash;
+					
+					if (player->dashspeed < player->maxdash && player->mindash != player->maxdash)
 					{
 #define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
 						fixed_t soundcalculation = chargecalculation;
 						player->dashspeed += FRACUNIT;
 						if (!player->spectator && soundcalculation != chargecalculation)
-							S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
+							S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
 #undef chargecalculation
 					}
 					if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
@@ -4628,7 +4657,6 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 						G_GhostAddRev();
 					}
 				}
-
 				// If not moving up or down, and travelling faster than a speed of five while not holding
 				// down the spin button and not spinning.
 				// AKA Just go into a spin on the ground, you idiot. ;)
@@ -4649,7 +4677,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 					if (player->powers[pw_carry] == CR_BRAKGOOP)
 						player->dashspeed = 0;
 
-					if (!((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE))
+					if (!((gametyperules & GTR_RACE) && leveltime < 4*TICRATE))
 					{
 						if (player->dashspeed)
 						{
@@ -4717,7 +4745,6 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 							player->drawangle = player->mo->angle;
 #undef zpos
 
-							P_SetTarget(&player->mo->tracer, NULL);
 							player->mo->momx >>= 1;
 							player->mo->momy >>= 1;
 							player->pflags |= PF_USEDOWN;
@@ -4780,10 +4807,10 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 
 	// Rolling normally
 	if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH)
-		&& player->speed < FixedMul(5*FRACUNIT,player->mo->scale) && canstand)
+		&& player->speed < 5*player->mo->scale && canstand)
 	{
 		if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
-			P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale));
+			P_InstaThrust(player->mo, player->mo->angle, 10*player->mo->scale);
 		else
 		{
 			player->skidtime = 0;
@@ -5028,7 +5055,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 	{
 		if (onground || player->climbing || player->powers[pw_carry])
 			;
-		else if (gametype == GT_CTF && player->gotflag)
+		else if ((gametyperules & GTR_TEAMFLAGS) && player->gotflag)
 			;
 		else if (player->pflags & (PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player has used an ability previously
 			;
@@ -5108,11 +5135,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 									boolean elem = ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL);
 									player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
 									if (elem)
-									{
-										player->pflags |= PF_NOJUMPDAMAGE;
-										P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
 										S_StartSound(player->mo, sfx_s3k43);
-									}
 									else
 									{
 										player->pflags &= ~PF_NOJUMPDAMAGE;
@@ -5253,7 +5276,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 			player->secondjump = 0;
 			player->pflags &= ~PF_THOKKED;
 		}
-		else if (player->pflags & PF_SLIDING || (gametype == GT_CTF && player->gotflag))
+		else if (player->pflags & PF_SLIDING || ((gametyperules & GTR_TEAMFLAGS) && player->gotflag) || player->pflags & PF_SHIELDABILITY)
 			;
 		/*else if (P_SuperReady(player))
 		{
@@ -5540,7 +5563,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 	{
 		player->pflags |= PF_JUMPDOWN;
 
-		if ((gametype != GT_CTF || !player->gotflag) && !player->exiting)
+		if ((!(gametyperules & GTR_TEAMFLAGS) || !player->gotflag) && !player->exiting)
 		{
 			if (player->secondjump == 1 && player->charability != CA_DOUBLEJUMP)
 			{
@@ -5550,7 +5573,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 				else
 					potentialmomz = ((player->speed < 10*player->mo->scale)
 					? (player->speed - 10*player->mo->scale)/5
-					: 0);
+					: -1); // Should be 0, but made negative to ensure P_PlayerHitFloor runs upon touching ground
 				if (P_MobjFlip(player->mo)*player->mo->momz < potentialmomz)
 					player->mo->momz = P_MobjFlip(player->mo)*potentialmomz;
 				player->pflags &= ~PF_SPINNING;
@@ -5945,6 +5968,8 @@ static void P_3dMovement(player_t *player)
 	// When sliding, don't allow forward/back
 	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);
 
 	player->aiming = cmd->aiming<<FRACBITS;
 
@@ -6089,7 +6114,7 @@ static void P_3dMovement(player_t *player)
 			// (Why was it so complicated before? ~Red)
 			controldirection = R_PointToAngle2(0, 0, cmd->forwardmove*FRACUNIT, -cmd->sidemove*FRACUNIT)+movepushangle;
 
-			movepushforward = max(abs(cmd->sidemove), abs(cmd->forwardmove)) * (thrustfactor * acceleration);
+			movepushforward = FixedHypot(cmd->sidemove, cmd->forwardmove) * (thrustfactor * acceleration);
 
 			// Allow a bit of movement while spinning
 			if ((player->pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING)
@@ -6802,7 +6827,6 @@ static void P_DoNiGHTSCapsule(player_t *player)
 			P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
 	}
 
-#ifdef ROTSPRITE
 	if (!(player->charflags & SF_NONIGHTSROTATION))
 	{
 		if ((player->mo->state == &states[S_PLAY_NIGHTS_PULL])
@@ -6811,7 +6835,6 @@ static void P_DoNiGHTSCapsule(player_t *player)
 		else
 			player->mo->rollangle = 0;
 	}
-#endif
 
 	if (G_IsSpecialStage(gamemap))
 	{ // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here!
@@ -7074,9 +7097,7 @@ static void P_NiGHTSMovement(player_t *player)
 	INT32 i;
 	statenum_t flystate;
 	UINT16 visangle;
-#ifdef ROTSPRITE
 	angle_t rollangle = 0;
-#endif
 
 	player->pflags &= ~PF_DRILLING;
 
@@ -7115,7 +7136,7 @@ static void P_NiGHTSMovement(player_t *player)
 		&& !player->exiting)
 			player->nightstime--;
 	}
-	else if (gametype != GT_RACE && gametype != GT_COMPETITION
+	else if (!(gametyperules & GTR_RACE)
 	&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
 			&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
 	&& !(player->capsule && player->capsule->reactiontime)
@@ -7261,9 +7282,7 @@ static void P_NiGHTSMovement(player_t *player)
 		&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6])
 	{
 		player->mo->momx = player->mo->momy = player->mo->momz = 0;
-#ifdef ROTSPRITE
 		player->mo->rollangle = 0;
-#endif
 		return;
 	}
 
@@ -7271,7 +7290,7 @@ static void P_NiGHTSMovement(player_t *player)
 	{
 		player->mo->momx = player->mo->momy = 0;
 
-		if (gametype != GT_RACE && gametype != GT_COMPETITION)
+		if (!(gametyperules & GTR_RACE))
 			P_SetObjectMomZ(player->mo, FRACUNIT/2, (P_MobjFlip(player->mo)*player->mo->momz >= 0));
 		else
 			player->mo->momz = 0;
@@ -7589,7 +7608,6 @@ static void P_NiGHTSMovement(player_t *player)
 			flystate += (visangle*2); // S_PLAY_NIGHTS_FLY0-C - the *2 is to skip over drill states
 #endif
 		}
-#ifdef ROTSPRITE
 		else
 		{
 			angle_t a = R_PointToAngle(player->mo->x, player->mo->y) - player->mo->angle;
@@ -7607,18 +7625,15 @@ static void P_NiGHTSMovement(player_t *player)
 
 			rollangle = FixedAngle(visangle<<FRACBITS);
 		}
-#endif
 	}
 
 	if (player->mo->state != &states[flystate])
 		P_SetPlayerMobjState(player->mo, flystate);
 
-#ifdef ROTSPRITE
 	if (player->charflags & SF_NONIGHTSROTATION)
 		player->mo->rollangle = 0;
 	else
 		player->mo->rollangle = rollangle;
-#endif
 
 	if (player == &players[consoleplayer])
 		localangle = player->mo->angle;
@@ -7776,6 +7791,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
 			flame->fuse = TICRATE*7; // takes about an extra second to hit the ground
 			flame->destscale = player->mo->scale;
 			P_SetScale(flame, player->mo->scale);
+			flame->flags2 = (flame->flags2 & ~MF2_OBJECTFLIP)|(player->mo->flags2 & MF2_OBJECTFLIP);
 			flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
 			P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale));
 			P_SetObjectMomZ(flame, 3*FRACUNIT, false);
@@ -8225,7 +8241,7 @@ static void P_MovePlayer(player_t *player)
 	if (player->pflags & PF_GLIDING)
 	{
 		mobj_t *mo = player->mo; // seriously why isn't this at the top of the function hngngngng
-		fixed_t leeway;
+		fixed_t leeway = !P_AnalogMove(player) ? FixedAngle(cmd->sidemove*(FRACUNIT)) : 0;
 		fixed_t glidespeed = player->actionspd;
 		fixed_t momx = mo->momx - player->cmomx, momy = mo->momy - player->cmomy;
 		angle_t angle, moveangle = R_PointToAngle2(0, 0, momx, momy);
@@ -8245,7 +8261,6 @@ static void P_MovePlayer(player_t *player)
 		}
 
 		// Strafing while gliding.
-		leeway = FixedAngle(cmd->sidemove*(FRACUNIT));
 		angle = mo->angle - leeway;
 
 		if (!player->skidtime) // TODO: make sure this works in 2D!
@@ -9295,7 +9310,7 @@ boolean P_GetLives(player_t *player)
 {
 	INT32 i, maxlivesplayer = -1, livescheck = 1;
 	if (!(netgame || multiplayer)
-	|| (gametype != GT_COOP)
+	|| !G_GametypeUsesCoopLives()
 	|| (player->lives == INFLIVES))
 		return true;
 
@@ -9444,7 +9459,7 @@ static void P_DeathThink(player_t *player)
 		player->playerstate = PST_REBORN;
 	else if ((player->lives > 0 || j != MAXPLAYERS) && !(!(netgame || multiplayer) && G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages!
 	{
-		if (gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2)
+		if (G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
 		{
 			P_ConsiderAllGone();
 			if ((player->deadtimer > TICRATE<<1) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE)))
@@ -9459,19 +9474,22 @@ static void P_DeathThink(player_t *player)
 			// Respawn with jump button, force respawn time (3 second default, cheat protected) in shooter modes.
 			if (cmd->buttons & BT_JUMP)
 			{
-				if (gametype != GT_COOP && player->spectator)
+				// You're a spectator, so respawn right away.
+				if ((gametyperules & GTR_SPECTATORS) && player->spectator)
 					player->playerstate = PST_REBORN;
-				else switch(gametype) {
-					case GT_COOP:
-					case GT_COMPETITION:
-					case GT_RACE:
-						if (player->deadtimer > TICRATE)
-							player->playerstate = PST_REBORN;
-						break;
-					default:
-						if (player->deadtimer > cv_respawntime.value*TICRATE)
-							player->playerstate = PST_REBORN;
-						break;
+				else
+				{
+					// Give me one second.
+					INT32 respawndelay = TICRATE;
+
+					// Non-platform gametypes
+					if (gametyperules & GTR_RESPAWNDELAY)
+						respawndelay = (cv_respawntime.value*TICRATE);
+
+					// You've been dead for enough time.
+					// You may now respawn.
+					if (player->deadtimer > respawndelay)
+						player->playerstate = PST_REBORN;
 				}
 			}
 
@@ -9482,11 +9500,10 @@ static void P_DeathThink(player_t *player)
 	}
 	else if ((netgame || multiplayer) && player->deadtimer >= 8*TICRATE)
 	{
-
 		INT32 i, deadtimercheck = INT32_MAX;
 
 		// In a net/multiplayer game, and out of lives
-		if (gametype == GT_COMPETITION)
+		if (G_CompetitionGametype())
 		{
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
@@ -9515,12 +9532,12 @@ static void P_DeathThink(player_t *player)
 		player->playerstate = PST_REBORN;
 	}
 
-	if (gametype == GT_RACE || gametype == GT_COMPETITION || (gametype == GT_COOP && (multiplayer || netgame)))
+	if ((gametyperules & GTR_RACE) || (gametype == GT_COOP && (multiplayer || netgame)))
 	{
 		// Keep time rolling in race mode
 		if (!(countdown2 && !countdown) && !player->exiting && !(player->pflags & PF_GAMETYPEOVER) && !stoppedclock)
 		{
-			if (gametype == GT_RACE || gametype == GT_COMPETITION)
+			if (gametyperules & GTR_RACE)
 			{
 				if (leveltime >= 4*TICRATE)
 					player->realtime = leveltime - 4*TICRATE;
@@ -9569,6 +9586,7 @@ static void CV_CamRotate2_OnChange(void)
 static CV_PossibleValue_t CV_CamSpeed[] = {{0, "MIN"}, {1*FRACUNIT, "MAX"}, {0, NULL}};
 static CV_PossibleValue_t rotation_cons_t[] = {{1, "MIN"}, {45, "MAX"}, {0, NULL}};
 static CV_PossibleValue_t CV_CamRotate[] = {{-720, "MIN"}, {720, "MAX"}, {0, NULL}};
+static CV_PossibleValue_t multiplier_cons_t[] = {{0, "MIN"}, {3*FRACUNIT, "MAX"}, {0, NULL}};
 
 consvar_t cv_cam_dist = {"cam_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam_height = {"cam_height", "25", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -9576,6 +9594,7 @@ consvar_t cv_cam_still = {"cam_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL,
 consvar_t cv_cam_speed = {"cam_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam_rotate = {"cam_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate_OnChange, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam_rotspeed = {"cam_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_cam_turnmultiplier = {"cam_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam_orbit = {"cam_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam_adjust = {"cam_adjust", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam2_dist = {"cam2_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -9584,6 +9603,7 @@ consvar_t cv_cam2_still = {"cam2_still", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL
 consvar_t cv_cam2_speed = {"cam2_speed", "0.3", CV_FLOAT|CV_SAVE, CV_CamSpeed, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam2_rotate = {"cam2_rotate", "0", CV_CALL|CV_NOINIT, CV_CamRotate, CV_CamRotate2_OnChange, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam2_rotspeed = {"cam2_rotspeed", "10", CV_SAVE, rotation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_cam2_turnmultiplier = {"cam2_turnmultiplier", "1.0", CV_FLOAT|CV_SAVE, multiplier_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam2_orbit = {"cam2_orbit", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_cam2_adjust = {"cam2_adjust", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
@@ -9652,17 +9672,28 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 
 	mo = player->mo;
 
+	if (player->playerstate == PST_REBORN)
+	{
+		P_CalcChasePostImg(player, thiscam);
+		return true;
+	}
+
 	if (player->exiting)
 	{
-		if (mo->target && mo->target->type == MT_SIGN && mo->target->spawnpoint)
+		if (mo->target && mo->target->type == MT_SIGN && mo->target->spawnpoint
+		&& !(gametype == GT_COOP && (netgame || multiplayer) && cv_exitmove.value)
+		&& !(twodlevel || (mo->flags2 & MF2_TWOD)))
 			sign = mo->target;
 		else if ((player->powers[pw_carry] == CR_NIGHTSMODE)
 		&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
 		&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6]))
+		{
+			P_CalcChasePostImg(player, thiscam);
 			return true;
+		}
 	}
 
-	cameranoclip = (player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
+	cameranoclip = (sign || player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
 
 	if (!(player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || tutorialmode))
 	{
@@ -9740,7 +9771,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		camorbit = (!stricmp(cv_cam_orbit.defaultvalue, "off")) ? false : true;
 		camrotate = atoi(cv_cam_rotate.defaultvalue);
 		camdist = FixedMul((INT32)(atof(cv_cam_dist.defaultvalue) * FRACUNIT), mo->scale);
-		camheight = FixedMul((INT32)(atof(cv_cam_height.defaultvalue) * FRACUNIT), FixedMul(player->camerascale, mo->scale));
+		camheight = FixedMul((INT32)(atof(cv_cam_height.defaultvalue) * FRACUNIT), mo->scale);
 	}
 	else if (thiscam == &camera)
 	{
@@ -9749,7 +9780,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		camorbit = cv_cam_orbit.value;
 		camrotate = cv_cam_rotate.value;
 		camdist = FixedMul(cv_cam_dist.value, mo->scale);
-		camheight = FixedMul(cv_cam_height.value, FixedMul(player->camerascale, mo->scale));
+		camheight = FixedMul(cv_cam_height.value, mo->scale);
 	}
 	else // Camera 2
 	{
@@ -9758,9 +9789,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		camorbit = cv_cam2_orbit.value;
 		camrotate = cv_cam2_rotate.value;
 		camdist = FixedMul(cv_cam2_dist.value, mo->scale);
-		camheight = FixedMul(cv_cam2_height.value, FixedMul(player->camerascale, mo->scale));
+		camheight = FixedMul(cv_cam2_height.value, mo->scale);
 	}
 
+	if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
+		camheight = FixedMul(camheight, player->camerascale);
+
 #ifdef REDSANALOG
 	if (P_AnalogMove(player) && (player->cmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)) {
 		camstill = true;
@@ -9792,7 +9826,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 				angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
 		}
 	}
-	else if (P_AnalogMove(player)) // Analog
+	else if (P_AnalogMove(player) && !sign) // Analog
 		angle = R_PointToAngle2(thiscam->x, thiscam->y, mo->x, mo->y);
 	else if (demoplayback)
 	{
@@ -9816,7 +9850,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		thiscam->angle = angle;
 	}
 
-	if ((((thiscam == &camera) && cv_analog.value) || ((thiscam != &camera) && cv_analog2.value) || demoplayback) && !objectplacing && !(twodlevel || (mo->flags2 & MF2_TWOD)) && (player->powers[pw_carry] != CR_NIGHTSMODE) && displayplayer == consoleplayer)
+	if ((((thiscam == &camera) && cv_analog.value) || ((thiscam != &camera) && cv_analog2.value) || demoplayback) && !sign && !objectplacing && !(twodlevel || (mo->flags2 & MF2_TWOD)) && (player->powers[pw_carry] != CR_NIGHTSMODE) && displayplayer == consoleplayer)
 	{
 #ifdef REDSANALOG
 		if ((player->cmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)); else
@@ -9854,26 +9888,25 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 			camheight = FixedMul(camheight, 3*FRACUNIT/2);
 		}
 
-		// x1.2 dist for analog
-		if (P_AnalogMove(player))
-		{
-			dist = FixedMul(dist, 6*FRACUNIT/5);
-			camheight = FixedMul(camheight, 6*FRACUNIT/5);
-		}
-
-		if (sign)
+		if (sign) // signpost camera has specific placement
 		{
 			camheight = mo->scale << 7;
 			camspeed = FRACUNIT/12;
 		}
+		else if (P_AnalogMove(player)) // x1.2 dist for analog
+		{
+			dist = FixedMul(dist, 6*FRACUNIT/5);
+			camheight = FixedMul(camheight, 6*FRACUNIT/5);
+		}
 
 		if (player->climbing || player->exiting || player->playerstate == PST_DEAD || (player->powers[pw_carry] == CR_ROPEHANG || player->powers[pw_carry] == CR_GENERIC || player->powers[pw_carry] == CR_MACESPIN))
 			dist <<= 1;
 	}
 
+	if (!sign && !(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
+		dist = FixedMul(dist, player->camerascale);
 
-
-	checkdist = (dist = FixedMul(dist, player->camerascale));
+	checkdist = dist;
 
 	if (checkdist < 128*FRACUNIT)
 		checkdist = 128*FRACUNIT;
@@ -9960,7 +9993,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 
 	if (sign)
 	{
-		if (mo->eflags & MFE_VERTICALFLIP)
+		if (sign->eflags & MFE_VERTICALFLIP)
 			z = sign->ceilingz - pviewheight - camheight;
 		else
 			z = sign->floorz + pviewheight + camheight;
@@ -10331,6 +10364,11 @@ boolean P_SpectatorJoinGame(player_t *player)
 		else
 			changeto = (P_RandomFixed() & 1) + 1;
 
+#ifdef HAVE_BLUA
+		if (!LUAh_TeamSwitch(player, changeto, true, false, false))
+			return false;
+#endif
+
 		if (player->mo)
 		{
 			P_RemoveMobj(player->mo);
@@ -10342,7 +10380,14 @@ boolean P_SpectatorJoinGame(player_t *player)
 
 		//Reset away view
 		if (P_IsLocalPlayer(player) && displayplayer != consoleplayer)
+		{
+#ifdef HAVE_BLUA
+			// Call ViewpointSwitch hooks here.
+			// The viewpoint was forcibly changed.
+			LUAh_ViewpointSwitch(player, &players[displayplayer], true);
+#endif
 			displayplayer = consoleplayer;
+		}
 
 		if (changeto == 1)
 			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x85', M_GetText("Red team"), '\x80');
@@ -10356,8 +10401,12 @@ boolean P_SpectatorJoinGame(player_t *player)
 	{
 		// Exception for hide and seek. Don't join a game when you simply
 		// respawn in place and sit there for the rest of the round.
-		if (!(gametype == GT_HIDEANDSEEK && leveltime > (hidetime * TICRATE)))
+		if (!((gametyperules & GTR_HIDEFROZEN) && leveltime > (hidetime * TICRATE)))
 		{
+#ifdef HAVE_BLUA
+			if (!LUAh_TeamSwitch(player, 3, true, false, false))
+				return false;
+#endif
 			if (player->mo)
 			{
 				P_RemoveMobj(player->mo);
@@ -10380,7 +10429,14 @@ boolean P_SpectatorJoinGame(player_t *player)
 
 			//Reset away view
 			if (P_IsLocalPlayer(player) && displayplayer != consoleplayer)
+			{
+#ifdef HAVE_BLUA
+				// Call ViewpointSwitch hooks here.
+				// The viewpoint was forcibly changed.
+				LUAh_ViewpointSwitch(player, &players[displayplayer], true);
+#endif
 				displayplayer = consoleplayer;
+			}
 
 			if (gametype != GT_COOP)
 				CONS_Printf(M_GetText("%s entered the game.\n"), player_names[player-players]);
@@ -10396,6 +10452,7 @@ boolean P_SpectatorJoinGame(player_t *player)
 	return false;
 }
 
+// the below is first person only, if you're curious. check out P_CalcChasePostImg in p_mobj.c for chasecam
 static void P_CalcPostImg(player_t *player)
 {
 	sector_t *sector = player->mo->subsector->sector;
@@ -10505,7 +10562,7 @@ void P_DoPityCheck(player_t *player)
 {
 	// No pity outside of match or CTF.
 	if (player->spectator
-		|| !(gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF))
+		|| !(gametyperules & GTR_PITYSHIELD))
 		return;
 
 	// Apply pity shield if available.
@@ -11202,7 +11259,10 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 		if (panim == PA_WALK)
 		{
 			if (stat != fume->info->spawnstate)
+			{
+				fume->threshold = 0;
 				P_SetMobjState(fume, fume->info->spawnstate);
+			}
 			return;
 		}
 	}
@@ -11233,6 +11293,12 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 		if (underwater)
 		{
 			fume->frame = (fume->frame & FF_FRAMEMASK) | FF_ANIMATE | (P_RandomRange(0, 9) * FF_TRANS10);
+			fume->threshold = 1;
+		}
+		else if (fume->threshold)
+		{
+			fume->frame = (fume->frame & FF_FRAMEMASK) | fume->state->frame;
+			fume->threshold = 0;
 		}
 	}
 
@@ -11338,7 +11404,7 @@ void P_PlayerThink(player_t *player)
 		I_Error("player %s is in PST_REBORN\n", sizeu1(playeri));
 #endif
 
-	if (gametype == GT_RACE || gametype == GT_COMPETITION)
+	if (gametyperules & GTR_RACE)
 	{
 		INT32 i;
 
@@ -11401,7 +11467,7 @@ void P_PlayerThink(player_t *player)
 		player->exiting > 0 && player->exiting <= 1*TICRATE &&
 		(!multiplayer || gametype == GT_COOP ? !mapheaderinfo[gamemap-1]->musinterfadeout : true) &&
 			// don't fade if we're fading during intermission. follows Y_StartIntermission intertype = int_coop
-		(gametype == GT_RACE || gametype == GT_COMPETITION ? countdown2 == 0 : true) && // don't fade on timeout
+		((gametyperules & GTR_RACE) ? countdown2 == 0 : true) && // don't fade on timeout
 		player->lives > 0 && // don't fade on game over (competition)
 		P_IsLocalPlayer(player))
 	{
@@ -11470,7 +11536,7 @@ void P_PlayerThink(player_t *player)
 
 	if (player->pflags & PF_FINISHED)
 	{
-		if (cv_exitmove.value && !G_EnoughPlayersFinished())
+		if ((gametype == GT_COOP && cv_exitmove.value) && !G_EnoughPlayersFinished())
 			player->exiting = 0;
 		else
 			P_DoPlayerExit(player);
@@ -11492,7 +11558,7 @@ void P_PlayerThink(player_t *player)
 #else
 	if (player->spectator &&
 #endif
-	gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2)
+	G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
 		P_ConsiderAllGone();
 
 	if (player->playerstate == PST_DEAD)
@@ -11516,7 +11582,7 @@ void P_PlayerThink(player_t *player)
 		player->lives = cv_startinglives.value;
 	}
 
-	if ((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE)
+	if ((gametyperules & GTR_RACE) && leveltime < 4*TICRATE)
 	{
 		cmd->buttons &= BT_USE; // Remove all buttons except BT_USE
 		cmd->forwardmove = 0;
@@ -11526,7 +11592,7 @@ void P_PlayerThink(player_t *player)
 	// Synchronizes the "real" amount of time spent in the level.
 	if (!player->exiting && !stoppedclock)
 	{
-		if (gametype == GT_RACE || gametype == GT_COMPETITION)
+		if (gametyperules & GTR_RACE)
 		{
 			if (leveltime >= 4*TICRATE)
 				player->realtime = leveltime - 4*TICRATE;
diff --git a/src/r_bsp.c b/src/r_bsp.c
index 23e751420e30a8a2393fbce60712ef799eca2887..6b4667aeef2afab999348a74f7a3ba4c8e505a3a 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -1169,9 +1169,11 @@ 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
 #ifdef POLYOBJECTS
-		if (!line->polyseg) // ignore segs that belong to polyobjects
+		&& !line->polyseg // ignore segs that belong to polyobjects
 #endif
+		)
 		R_AddLine(line);
 		line++;
 		curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so stuff doesn't try using it for other things */
@@ -1378,5 +1380,13 @@ void R_RenderBSPNode(INT32 bspnum)
 		bspnum = bsp->children[side^1];
 	}
 
+	// PORTAL CULLING
+	if (portalcullsector) {
+		sector_t *sect = subsectors[bspnum & ~NF_SUBSECTOR].sector;
+		if (sect != portalcullsector)
+			return;
+		portalcullsector = NULL;
+	}
+
 	R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR);
 }
diff --git a/src/r_bsp.h b/src/r_bsp.h
index 825be6064b078ba0f5c0966b1d726c72e2546c07..95aea9eff407b1abe77f78a7f7f4b56cf7d28189 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_data.c b/src/r_data.c
index 574c4e194307a4d6849e1cf19d61a5f33f0f0102..986b65deaca333a80d059d72f7875fd6c575116b 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -32,6 +32,10 @@
 #include <malloc.h> // alloca(sizeof)
 #endif
 
+#ifdef HWRENDER
+#include "hardware/hw_main.h" // HWR_LoadTextures
+#endif
+
 #if defined(_MSC_VER)
 #pragma pack(1)
 #endif
@@ -320,8 +324,8 @@ UINT8 ASTBlendPixel_8bpp(UINT8 background, UINT8 foreground, int style, UINT8 al
 	else if (style != AST_TRANSLUCENT)
 	{
 		RGBA_t texel;
-		RGBA_t bg = V_GetColor(background);
-		RGBA_t fg = V_GetColor(foreground);
+		RGBA_t bg = V_GetMasterColor(background);
+		RGBA_t fg = V_GetMasterColor(foreground);
 		texel.rgba = ASTBlendPixel(bg, fg, style, alpha);
 		return NearestColor(texel.s.red, texel.s.green, texel.s.blue);
 	}
@@ -437,7 +441,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 	texture_t *texture;
 	texpatch_t *patch;
 	patch_t *realpatch;
-	boolean dealloc = false;
+	UINT8 *pdata;
 	int x, x1, x2, i, width, height;
 	size_t blocksize;
 	column_t *patchcol;
@@ -465,12 +469,17 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 		wadnum = patch->wad;
 		lumpnum = patch->lump;
 		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-		realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); // can't use W_CachePatchNumPwad because OpenGL
+		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+		realpatch = (patch_t *)pdata;
 
 #ifndef NO_PNG_LUMPS
 		if (R_IsLumpPNG((UINT8 *)realpatch, lumplength))
 			goto multipatch;
 #endif
+#ifdef WALLFLATS
+		if (texture->type == TEXTURETYPE_FLAT)
+			goto multipatch;
+#endif
 
 		// Check the patch for holes.
 		if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height))
@@ -527,9 +536,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 	}
 
 	// multi-patch textures (or 'composite')
-#ifndef NO_PNG_LUMPS
 	multipatch:
-#endif
 	texture->holes = false;
 	texture->flip = 0;
 	blocksize = (texture->width * 4) + (texture->width * texture->height);
@@ -548,6 +555,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 	// Composite the columns together.
 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
 	{
+		boolean dealloc = true;
 		static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer.
 		if (patch->style != AST_COPY)
 			ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache;
@@ -556,17 +564,25 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 
 		wadnum = patch->wad;
 		lumpnum = patch->lump;
+		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
 		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-		realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-		dealloc = false;
+		realpatch = (patch_t *)pdata;
+		dealloc = true;
 
 #ifndef NO_PNG_LUMPS
 		if (R_IsLumpPNG((UINT8 *)realpatch, lumplength))
-		{
 			realpatch = R_PNGToPatch((UINT8 *)realpatch, lumplength, NULL, false);
-			dealloc = true;
-		}
+		else
+#endif
+#ifdef WALLFLATS
+		if (texture->type == TEXTURETYPE_FLAT)
+			realpatch = R_FlatToPatch(pdata, texture->width, texture->height, 0, 0, NULL, false);
+		else
 #endif
+		{
+			(void)lumplength;
+			dealloc = false;
+		}
 
 		x1 = patch->originx;
 		width = SHORT(realpatch->width);
@@ -721,7 +737,6 @@ void R_LoadTextures(void)
 	for (w = 0, numtextures = 0; w < numwadfiles; w++)
 	{
 		// Count the textures from TEXTURES lumps
-
 		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
 		while (texturesLumpPos != INT16_MAX)
 		{
@@ -730,7 +745,6 @@ void R_LoadTextures(void)
 		}
 
 		// Count single-patch textures
-
 		if (wadfiles[w]->type == RET_PK3)
 		{
 			texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
@@ -743,7 +757,11 @@ void R_LoadTextures(void)
 		}
 
 		if (texstart == INT16_MAX || texend == INT16_MAX)
+#ifdef WALLFLATS
+			goto countflats;
+#else
 			continue;
+#endif
 
 		texstart++; // Do not count the first marker
 
@@ -760,6 +778,40 @@ void R_LoadTextures(void)
 		{
 			numtextures += (UINT32)(texend - texstart);
 		}
+
+#ifdef WALLFLATS
+countflats:
+		// Count flats
+		if (wadfiles[w]->type == RET_PK3)
+		{
+			texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0);
+			texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart);
+		}
+		else
+		{
+			texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0);
+			texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart);
+		}
+
+		if (texstart == INT16_MAX || texend == INT16_MAX)
+			continue;
+
+		texstart++; // Do not count the first marker
+
+		// PK3s have subfolders, so we can't just make a simple sum
+		if (wadfiles[w]->type == RET_PK3)
+		{
+			for (j = texstart; j < texend; j++)
+			{
+				if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it
+					numtextures++;
+			}
+		}
+		else // Add all the textures between F_START and F_END
+		{
+			numtextures += (UINT32)(texend - texstart);
+		}
+#endif
 	}
 
 	// If no textures found by this point, bomb out
@@ -809,7 +861,11 @@ void R_LoadTextures(void)
 		}
 
 		if (texstart == INT16_MAX || texend == INT16_MAX)
+#ifdef WALLFLATS
+			goto checkflats;
+#else
 			continue;
+#endif
 
 		texstart++; // Do not count the first marker
 
@@ -853,6 +909,8 @@ void R_LoadTextures(void)
 				texture->width = SHORT(patchlump->width);
 				texture->height = SHORT(patchlump->height);
 			}
+
+			texture->type = TEXTURETYPE_SINGLEPATCH;
 			texture->patchcount = 1;
 			texture->holes = false;
 			texture->flip = 0;
@@ -871,7 +929,113 @@ void R_LoadTextures(void)
 			textureheight[i] = texture->height << FRACBITS;
 			i++;
 		}
+
+#ifdef WALLFLATS
+checkflats:
+		// Yes
+		if (wadfiles[w]->type == RET_PK3)
+		{
+			texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0);
+			texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart);
+		}
+		else
+		{
+			texstart = W_CheckNumForNamePwad("F_START", (UINT16)w, 0);
+			texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart);
+		}
+
+		if (texstart == INT16_MAX || texend == INT16_MAX)
+			continue;
+
+		texstart++; // Do not count the first marker
+
+		// Work through each lump between the markers in the WAD.
+		for (j = 0; j < (texend - texstart); j++)
+		{
+			UINT8 *flatlump;
+			UINT16 wadnum = (UINT16)w;
+			lumpnum_t lumpnum = texstart + j;
+			size_t lumplength;
+			size_t flatsize = 0;
+
+			if (wadfiles[w]->type == RET_PK3)
+			{
+				if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder
+					continue; // If it is then SKIP IT
+			}
+
+			flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+			lumplength = W_LumpLengthPwad(wadnum, lumpnum);
+
+			switch (lumplength)
+			{
+				case 4194304: // 2048x2048 lump
+					flatsize = 2048;
+					break;
+				case 1048576: // 1024x1024 lump
+					flatsize = 1024;
+					break;
+				case 262144:// 512x512 lump
+					flatsize = 512;
+					break;
+				case 65536: // 256x256 lump
+					flatsize = 256;
+					break;
+				case 16384: // 128x128 lump
+					flatsize = 128;
+					break;
+				case 1024: // 32x32 lump
+					flatsize = 32;
+					break;
+				default: // 64x64 lump
+					flatsize = 64;
+					break;
+			}
+
+			//CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize);
+			texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
+
+			// Set texture properties.
+			M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
+
+#ifndef NO_PNG_LUMPS
+			if (R_IsLumpPNG((UINT8 *)flatlump, lumplength))
+			{
+				INT16 width, height;
+				R_PNGDimensions((UINT8 *)flatlump, &width, &height, lumplength);
+				texture->width = width;
+				texture->height = height;
+			}
+			else
+#endif
+				texture->width = texture->height = flatsize;
+
+			texture->type = TEXTURETYPE_FLAT;
+			texture->patchcount = 1;
+			texture->holes = false;
+			texture->flip = 0;
+
+			// Allocate information for the texture's patches.
+			patch = &texture->patches[0];
+
+			patch->originx = patch->originy = 0;
+			patch->wad = (UINT16)w;
+			patch->lump = texstart + j;
+			patch->flip = 0;
+
+			Z_Unlock(flatlump);
+
+			texturewidth[i] = texture->width;
+			textureheight[i] = texture->height << FRACBITS;
+			i++;
+		}
+#endif
 	}
+
+#ifdef HWRENDER
+	if (rendermode == render_opengl)
+		HWR_LoadTextures(numtextures);
+#endif
 }
 
 static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
@@ -1182,6 +1346,7 @@ static texture_t *R_ParseTexture(boolean actuallyLoadTexture)
 			M_Memcpy(resultTexture->name, newTextureName, 8);
 			resultTexture->width = newTextureWidth;
 			resultTexture->height = newTextureHeight;
+			resultTexture->type = TEXTURETYPE_COMPOSITE;
 		}
 		Z_Free(texturesToken);
 		texturesToken = M_GetToken(NULL);
@@ -1499,7 +1664,7 @@ static void R_CreateFadeColormaps(void)
 #define GETCOLOR \
 	px = colormaps[i%256]; \
 	fade = (i/256) * (256 / FADECOLORMAPROWS); \
-	rgba = V_GetColor(px);
+	rgba = V_GetMasterColor(px);
 
 	// to black
 	makeblack:
@@ -1930,7 +2095,6 @@ lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
 	/////////////////////
 	// This code creates the colormap array used by software renderer
 	/////////////////////
-	if (rendermode == render_soft)
 	{
 		double r, g, b, cbrightness;
 		int p;
@@ -2552,7 +2716,7 @@ void R_PrecacheLevel(void)
 				lump = sf->lumppat[k];
 				if (devparm)
 					spritememory += W_LumpLength(lump);
-				W_CachePatchNum(lump, PU_CACHE);
+				W_CachePatchNum(lump, PU_PATCH);
 			}
 		}
 	}
diff --git a/src/r_data.h b/src/r_data.h
index e84b1026600089a5eb43c0e443a9458c9c5f396a..f028f2f5d16ef54da544d56780448adc84d8b858 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -45,6 +45,17 @@ typedef struct
 	enum patchalphastyle style;
 } texpatch_t;
 
+// texture type
+enum
+{
+	TEXTURETYPE_UNKNOWN,
+	TEXTURETYPE_SINGLEPATCH,
+	TEXTURETYPE_COMPOSITE,
+#ifdef WALLFLATS
+	TEXTURETYPE_FLAT,
+#endif
+};
+
 // A maptexturedef_t describes a rectangular texture,
 //  which is composed of one or more mappatch_t structures
 //  that arrange graphic patches.
@@ -52,6 +63,7 @@ typedef struct
 {
 	// Keep name for switch changing, etc.
 	char name[8];
+	UINT8 type; // TEXTURETYPE_
 	INT16 width, height;
 	boolean holes;
 	UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
@@ -159,6 +171,8 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap);
 #define R_PutRgbaRGB(r, g, b) (R_PutRgbaR(r) + R_PutRgbaG(g) + R_PutRgbaB(b))
 #define R_PutRgbaRGBA(r, g, b, a) (R_PutRgbaRGB(r, g, b) + R_PutRgbaA(a))
 
+UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b);
+
 extern INT32 numtextures;
 
 #endif
diff --git a/src/r_defs.h b/src/r_defs.h
index 6d0b10b706d27897c7445c92766ad7a4f074f9e2..c7c198d662e2713db2ce93716f40dfd68bc3a8d8 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -83,7 +83,7 @@ typedef struct extracolormap_s
   */
 typedef struct
 {
-	fixed_t x, y, z;
+	fixed_t x, y;
 } vertex_t;
 
 // Forward of linedefs, for sectors.
@@ -384,17 +384,6 @@ typedef struct sector_s
 	// for fade thinker
 	INT16 spawn_lightlevel;
 
-	// these are saved for netgames, so do not let Lua touch these!
-	INT32 spawn_nexttag, spawn_firsttag; // the actual nexttag/firsttag values may differ if the sector's tag was changed
-
-	// offsets sector spawned with (via linedef type 7)
-	fixed_t spawn_flr_xoffs, spawn_flr_yoffs;
-	fixed_t spawn_ceil_xoffs, spawn_ceil_yoffs;
-
-	// flag angles sector spawned with (via linedef type 7)
-	angle_t spawn_flrpic_angle;
-	angle_t spawn_ceilpic_angle;
-
 	// colormap structure
 	extracolormap_t *spawn_extra_colormap;
 } sector_t;
@@ -447,14 +436,10 @@ typedef struct line_s
 	polyobj_t *polyobj; // Belongs to a polyobject?
 #endif
 
-	char *text; // a concatination of all front and back texture names, for linedef specials that require a string.
+	char *text; // a concatenation of all front and back texture names, for linedef specials that require a string.
 	INT16 callcount; // no. of calls left before triggering, for the "X calls" linedef specials, defaults to 0
 } line_t;
 
-//
-// The SideDef.
-//
-
 typedef struct
 {
 	// add this to the calculated texture column
@@ -467,13 +452,16 @@ typedef struct
 	// We do not maintain names here.
 	INT32 toptexture, bottomtexture, midtexture;
 
-	// Sector the SideDef is facing.
+	// Linedef the sidedef belongs to
+	line_t *line;
+
+	// Sector the sidedef is facing.
 	sector_t *sector;
 
 	INT16 special; // the special of the linedef this side belongs to
 	INT16 repeatcnt; // # of times to repeat midtexture
 
-	char *text; // a concatination of all top, bottom, and mid texture names, for linedef specials that require a string.
+	char *text; // a concatenation of all top, bottom, and mid texture names, for linedef specials that require a string.
 
 	extracolormap_t *colormap_data; // storage for colormaps; not applied to sectors.
 } side_t;
@@ -598,6 +586,7 @@ typedef struct seg_s
 	polyobj_t *polyseg;
 	boolean dontrenderme;
 #endif
+	boolean glseg;
 } seg_t;
 
 //
@@ -742,9 +731,9 @@ typedef struct
 	boolean cached[8];
 #ifdef HWRENDER
 	aatree_t *hardware_patch[8];
-#endif
+#endif/*HWRENDER*/
 } rotsprite_t;
-#endif
+#endif/*ROTSPRITE*/
 
 typedef enum
 {
@@ -756,6 +745,24 @@ typedef enum
 	SRF_NONE        = 0xff // Initial value
 } spriterotateflags_t;     // SRF's up!
 
+// Same as a patch_t, except just the header
+// and the wadnum/lumpnum combination that points
+// to wherever the patch is in memory.
+struct patchinfo_s
+{
+	INT16 width;          // bounding box size
+	INT16 height;
+	INT16 leftoffset;     // pixels to the left of origin
+	INT16 topoffset;      // pixels below the origin
+
+	UINT16 wadnum;        // the software patch lump num for when the patch
+	UINT16 lumpnum;       // was flushed, and we need to re-create it
+
+	// next patchinfo_t in memory
+	struct patchinfo_s *next;
+};
+typedef struct patchinfo_s patchinfo_t;
+
 //
 // Sprites are patches with a special naming convention so they can be
 //  recognized by R_InitSprites.
diff --git a/src/r_draw.c b/src/r_draw.c
index 6fc8d65994e311780f76d29cd923c1fead9f44da..743965cfbf58052e38bd9cb741024ee7938255c1 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -107,7 +107,8 @@ UINT8 *ds_transmap; // one of the translucency tables
 
 #ifdef ESLOPE
 pslope_t *ds_slope; // Current slope being used
-floatv3_t ds_su, ds_sv, ds_sz; // Vectors for... stuff?
+floatv3_t ds_su[MAXVIDHEIGHT], ds_sv[MAXVIDHEIGHT], ds_sz[MAXVIDHEIGHT]; // Vectors for... stuff?
+floatv3_t *ds_sup, *ds_svp, *ds_szp;
 float focallengthf, zeroheight;
 #endif
 
@@ -960,6 +961,7 @@ void R_DrawViewBorder(void)
 // ==========================================================================
 
 #include "r_draw8.c"
+#include "r_draw8_npo2.c"
 
 // ==========================================================================
 //                   INCLUDE 16bpp DRAWING CODE HERE
diff --git a/src/r_draw.h b/src/r_draw.h
index a0ab748480930d6f06191b6de78adfefdb539ce5..da38c40e08ba22dc7e68144b13eb80012194655d 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -68,7 +68,8 @@ typedef struct {
 } floatv3_t;
 
 extern pslope_t *ds_slope; // Current slope being used
-extern floatv3_t ds_su, ds_sv, ds_sz; // Vectors for... stuff?
+extern floatv3_t ds_su[MAXVIDHEIGHT], ds_sv[MAXVIDHEIGHT], ds_sz[MAXVIDHEIGHT]; // Vectors for... stuff?
+extern floatv3_t *ds_sup, *ds_svp, *ds_szp;
 extern float focallengthf, zeroheight;
 #endif
 
@@ -138,48 +139,64 @@ void R_DrawViewBorder(void);
 // -----------------
 
 void R_DrawColumn_8(void);
-#define R_DrawWallColumn_8	R_DrawColumn_8
 void R_DrawShadeColumn_8(void);
 void R_DrawTranslucentColumn_8(void);
-
-#ifdef USEASM
-void ASMCALL R_DrawColumn_8_ASM(void);
-#define R_DrawWallColumn_8_ASM	R_DrawColumn_8_ASM
-void ASMCALL R_DrawShadeColumn_8_ASM(void);
-void ASMCALL R_DrawTranslucentColumn_8_ASM(void);
-void ASMCALL R_Draw2sMultiPatchColumn_8_ASM(void);
-
-void ASMCALL R_DrawColumn_8_MMX(void);
-#define R_DrawWallColumn_8_MMX	R_DrawColumn_8_MMX
-
-void ASMCALL R_Draw2sMultiPatchColumn_8_MMX(void);
-void ASMCALL R_DrawSpan_8_MMX(void);
-#endif
-
 void R_DrawTranslatedColumn_8(void);
 void R_DrawTranslatedTranslucentColumn_8(void);
+void R_Draw2sMultiPatchColumn_8(void);
+void R_Draw2sMultiPatchTranslucentColumn_8(void);
+void R_DrawFogColumn_8(void);
+void R_DrawColumnShadowed_8(void);
+
 void R_DrawSpan_8(void);
+void R_DrawSplat_8(void);
+void R_DrawTranslucentSpan_8(void);
+void R_DrawTranslucentSplat_8(void);
 #ifdef ESLOPE
-void R_CalcTiltedLighting(fixed_t start, fixed_t end);
 void R_DrawTiltedSpan_8(void);
 void R_DrawTiltedTranslucentSpan_8(void);
+#ifndef NOWATER
+void R_DrawTiltedTranslucentWaterSpan_8(void);
+#endif
 void R_DrawTiltedSplat_8(void);
+void R_CalcTiltedLighting(fixed_t start, fixed_t end);
+extern INT32 tiltlighting[MAXVIDWIDTH];
 #endif
-void R_DrawSplat_8(void);
-void R_DrawTranslucentSplat_8(void);
-void R_DrawTranslucentSpan_8(void);
-void R_Draw2sMultiPatchColumn_8(void);
-void R_Draw2sMultiPatchTranslucentColumn_8(void);
-void R_DrawFogSpan_8(void);
-void R_DrawFogColumn_8(void);
-void R_DrawColumnShadowed_8(void);
-
 #ifndef NOWATER
 void R_DrawTranslucentWaterSpan_8(void);
-
 extern INT32 ds_bgofs;
 extern INT32 ds_waterofs;
 #endif
+void R_DrawFogSpan_8(void);
+
+// Lactozilla: Non-powers-of-two
+void R_DrawSpan_NPO2_8(void);
+void R_DrawTranslucentSpan_NPO2_8(void);
+void R_DrawSplat_NPO2_8(void);
+void R_DrawTranslucentSplat_NPO2_8(void);
+#ifdef ESLOPE
+void R_DrawTiltedSpan_NPO2_8(void);
+void R_DrawTiltedTranslucentSpan_NPO2_8(void);
+#ifndef NOWATER
+void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void);
+#endif
+void R_DrawTiltedSplat_NPO2_8(void);
+#endif
+#ifndef NOWATER
+void R_DrawTranslucentWaterSpan_NPO2_8(void);
+#endif
+
+#ifdef USEASM
+void ASMCALL R_DrawColumn_8_ASM(void);
+void ASMCALL R_DrawShadeColumn_8_ASM(void);
+void ASMCALL R_DrawTranslucentColumn_8_ASM(void);
+void ASMCALL R_Draw2sMultiPatchColumn_8_ASM(void);
+
+void ASMCALL R_DrawColumn_8_MMX(void);
+
+void ASMCALL R_Draw2sMultiPatchColumn_8_MMX(void);
+void ASMCALL R_DrawSpan_8_MMX(void);
+#endif
 
 // ------------------
 // 16bpp DRAWING CODE
diff --git a/src/r_draw16.c b/src/r_draw16.c
index 918dd356c34bd67e0668031d7423164dbb98f301..48017f45e6c11af24e4b242d063b332db7a5d0f7 100644
--- a/src/r_draw16.c
+++ b/src/r_draw16.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_draw8.c b/src/r_draw8.c
index 77406f83c6d6420835a6cf11de32e7a7edb27441..015dac2a7f9f56c355424b8102caac88205e9732 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -562,11 +562,8 @@ void R_DrawSpan_8 (void)
 	// bit per power of two (obviously)
 	// Ok, because I was able to eliminate the variable spot below, this function is now FASTER
 	// than the original span renderer. Whodathunkit?
-	if (ds_powersoftwo)
-	{
-		xposition <<= nflatshiftup; yposition <<= nflatshiftup;
-		xstep <<= nflatshiftup; ystep <<= nflatshiftup;
-	}
+	xposition <<= nflatshiftup; yposition <<= nflatshiftup;
+	xstep <<= nflatshiftup; ystep <<= nflatshiftup;
 
 	source = ds_source;
 	colormap = ds_colormap;
@@ -575,82 +572,58 @@ void R_DrawSpan_8 (void)
 	if (dest+8 > deststop)
 		return;
 
-	if (!ds_powersoftwo)
+	while (count >= 8)
 	{
-		while (count-- && dest <= deststop)
-		{
-			fixed_t x = (xposition >> FRACBITS);
-			fixed_t y = (yposition >> FRACBITS);
-
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
-
-			*dest++ = colormap[source[((y * ds_flatwidth) + x)]];
-			xposition += xstep;
-			yposition += ystep;
-		}
+		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
+		// have the uber complicated math to calculate it now, so that was a memory write we didn't
+		// need!
+		dest[0] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[1] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[2] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[3] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[4] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[5] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[6] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[7] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest += 8;
+		count -= 8;
 	}
-	else
+	while (count-- && dest <= deststop)
 	{
-		while (count >= 8)
-		{
-			// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
-			// have the uber complicated math to calculate it now, so that was a memory write we didn't
-			// need!
-			dest[0] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[1] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[2] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[3] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[4] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[5] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[6] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[7] = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-
-			dest += 8;
-			count -= 8;
-		}
-		while (count-- && dest <= deststop)
-		{
-			*dest++ = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
-			xposition += xstep;
-			yposition += ystep;
-		}
+		*dest++ = colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]];
+		xposition += xstep;
+		yposition += ystep;
 	}
 }
 
 #ifdef ESLOPE
 // R_CalcTiltedLighting
 // Exactly what it says on the tin. I wish I wasn't too lazy to explain things properly.
-static INT32 tiltlighting[MAXVIDWIDTH];
+INT32 tiltlighting[MAXVIDWIDTH];
 void R_CalcTiltedLighting(fixed_t start, fixed_t end)
 {
 	// ZDoom uses a different lighting setup to us, and I couldn't figure out how to adapt their version
@@ -692,22 +665,22 @@ void R_DrawTiltedSpan_8(void)
 	double endz, endu, endv;
 	UINT32 stepu, stepv;
 
-	iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx);
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
 
 	// Lighting is simple. It's just linear interpolation from start to end
 	{
 		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
 		float lightstart, lightend;
 
-		lightend = (iz + ds_sz.x*width) * planelightfloat;
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
 		lightstart = iz * planelightfloat;
 
 		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
 		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
 	}
 
-	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);
+	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);
 
 	dest = ylookup[ds_y] + columnofs[ds_x1];
 	source = ds_source;
@@ -724,28 +697,11 @@ void R_DrawTiltedSpan_8(void)
 
 		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
 
-		if (!ds_powersoftwo)
-		{
-			fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-			fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
-
-			*dest = colormap[source[((y * ds_flatwidth) + x)]];
-		}
-		else
-			*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
+		*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
 		dest++;
-		iz += ds_sz.x;
-		uz += ds_su.x;
-		vz += ds_sv.x;
+		iz += ds_szp->x;
+		uz += ds_sup->x;
+		vz += ds_svp->x;
 	} while (--width >= 0);
 #else
 #define SPANSIZE 16
@@ -755,9 +711,9 @@ void R_DrawTiltedSpan_8(void)
 	startu = uz*startz;
 	startv = vz*startz;
 
-	izstep = ds_sz.x * SPANSIZE;
-	uzstep = ds_su.x * SPANSIZE;
-	vzstep = ds_sv.x * SPANSIZE;
+	izstep = ds_szp->x * SPANSIZE;
+	uzstep = ds_sup->x * SPANSIZE;
+	vzstep = ds_svp->x * SPANSIZE;
 	//x1 = 0;
 	width++;
 
@@ -778,24 +734,7 @@ void R_DrawTiltedSpan_8(void)
 		for (i = SPANSIZE-1; i >= 0; i--)
 		{
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			if (!ds_powersoftwo)
-			{
-				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-				// Carefully align all of my Friends.
-				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
-
-				*dest = colormap[source[((y * ds_flatwidth) + x)]];
-			}
-			else
-				*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
+			*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
 			dest++;
 			u += stepu;
 			v += stepv;
@@ -811,31 +750,14 @@ void R_DrawTiltedSpan_8(void)
 			u = (INT64)(startu);
 			v = (INT64)(startv);
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			if (!ds_powersoftwo)
-			{
-				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-				// Carefully align all of my Friends.
-				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
-
-				*dest = colormap[source[((y * ds_flatwidth) + x)]];
-			}
-			else
-				*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
+			*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
 		}
 		else
 		{
 			double left = width;
-			iz += ds_sz.x * left;
-			uz += ds_su.x * left;
-			vz += ds_sv.x * left;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
 
 			endz = 1.f/iz;
 			endu = uz*endz;
@@ -849,24 +771,7 @@ void R_DrawTiltedSpan_8(void)
 			for (; width != 0; width--)
 			{
 				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-				if (!ds_powersoftwo)
-				{
-					fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-					fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-					// Carefully align all of my Friends.
-					if (x < 0)
-						x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-					if (y < 0)
-						y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-					x %= ds_flatwidth;
-					y %= ds_flatheight;
-
-					*dest = colormap[source[((y * ds_flatwidth) + x)]];
-				}
-				else
-					*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
+				*dest = colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]];
 				dest++;
 				u += stepu;
 				v += stepv;
@@ -896,22 +801,22 @@ void R_DrawTiltedTranslucentSpan_8(void)
 	double endz, endu, endv;
 	UINT32 stepu, stepv;
 
-	iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx);
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
 
 	// Lighting is simple. It's just linear interpolation from start to end
 	{
 		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
 		float lightstart, lightend;
 
-		lightend = (iz + ds_sz.x*width) * planelightfloat;
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
 		lightstart = iz * planelightfloat;
 
 		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
 		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
 	}
 
-	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);
+	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);
 
 	dest = ylookup[ds_y] + columnofs[ds_x1];
 	source = ds_source;
@@ -927,28 +832,149 @@ void R_DrawTiltedTranslucentSpan_8(void)
 		v = (INT64)(vz*z) + viewy;
 
 		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-		if (!ds_powersoftwo)
-		{
-			fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-			fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+		*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;
+	} while (--width >= 0);
+#else
+#define SPANSIZE 16
+#define INVSPAN	0.0625f
+
+	startz = 1.f/iz;
+	startu = uz*startz;
+	startv = vz*startz;
 
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+	izstep = ds_szp->x * SPANSIZE;
+	uzstep = ds_sup->x * SPANSIZE;
+	vzstep = ds_svp->x * SPANSIZE;
+	//x1 = 0;
+	width++;
 
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
+	while (width >= SPANSIZE)
+	{
+		iz += izstep;
+		uz += uzstep;
+		vz += vzstep;
+
+		endz = 1.f/iz;
+		endu = uz*endz;
+		endv = vz*endz;
+		stepu = (INT64)((endu - startu) * INVSPAN);
+		stepv = (INT64)((endv - startv) * INVSPAN);
+		u = (INT64)(startu) + viewx;
+		v = (INT64)(startv) + viewy;
 
-			*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
+		for (i = SPANSIZE-1; i >= 0; i--)
+		{
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
+			dest++;
+			u += stepu;
+			v += stepv;
 		}
-		else
+		startu = endu;
+		startv = endv;
+		width -= SPANSIZE;
+	}
+	if (width > 0)
+	{
+		if (width == 1)
+		{
+			u = (INT64)(startu);
+			v = (INT64)(startv);
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
 			*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
+		}
+		else
+		{
+			double left = width;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
+
+			endz = 1.f/iz;
+			endu = uz*endz;
+			endv = vz*endz;
+			left = 1.f/left;
+			stepu = (INT64)((endu - startu) * left);
+			stepv = (INT64)((endv - startv) * left);
+			u = (INT64)(startu) + viewx;
+			v = (INT64)(startv) + viewy;
+
+			for (; width != 0; width--)
+			{
+				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+				*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
+				dest++;
+				u += stepu;
+				v += stepv;
+			}
+		}
+	}
+#endif
+}
+
+#ifndef NOWATER
+/**	\brief The R_DrawTiltedTranslucentWaterSpan_8 function
+	Like DrawTiltedTranslucentSpan, but for water
+*/
+void R_DrawTiltedTranslucentWaterSpan_8(void)
+{
+	// x1, x2 = ds_x1, ds_x2
+	int width = ds_x2 - ds_x1;
+	double iz, uz, vz;
+	UINT32 u, v;
+	int i;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+	UINT8 *dsrc;
+
+	double startz, startu, startv;
+	double izstep, uzstep, vzstep;
+	double endz, endu, endv;
+	UINT32 stepu, stepv;
+
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
+
+	// Lighting is simple. It's just linear interpolation from start to end
+	{
+		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
+		float lightstart, lightend;
+
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
+		lightstart = iz * planelightfloat;
+
+		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
+		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
+	}
+
+	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);
+
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1;
+	source = ds_source;
+	//colormap = ds_colormap;
+
+#if 0	// The "perfect" reference version of this routine. Pretty slow.
+		// Use it only to see how things are supposed to look.
+	i = 0;
+	do
+	{
+		double z = 1.f/iz;
+		u = (INT64)(uz*z) + viewx;
+		v = (INT64)(vz*z) + viewy;
+
+		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+		*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dsrc++);
 		dest++;
-		iz += ds_sz.x;
-		uz += ds_su.x;
-		vz += ds_sv.x;
+		iz += ds_szp->x;
+		uz += ds_sup->x;
+		vz += ds_svp->x;
 	} while (--width >= 0);
 #else
 #define SPANSIZE 16
@@ -958,9 +984,9 @@ void R_DrawTiltedTranslucentSpan_8(void)
 	startu = uz*startz;
 	startv = vz*startz;
 
-	izstep = ds_sz.x * SPANSIZE;
-	uzstep = ds_su.x * SPANSIZE;
-	vzstep = ds_sv.x * SPANSIZE;
+	izstep = ds_szp->x * SPANSIZE;
+	uzstep = ds_sup->x * SPANSIZE;
+	vzstep = ds_svp->x * SPANSIZE;
 	//x1 = 0;
 	width++;
 
@@ -981,24 +1007,7 @@ void R_DrawTiltedTranslucentSpan_8(void)
 		for (i = SPANSIZE-1; i >= 0; i--)
 		{
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			if (!ds_powersoftwo)
-			{
-				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-				// Carefully align all of my Friends.
-				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
-
-				*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
-			}
-			else
-				*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
+			*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dsrc++);
 			dest++;
 			u += stepu;
 			v += stepv;
@@ -1014,31 +1023,14 @@ void R_DrawTiltedTranslucentSpan_8(void)
 			u = (INT64)(startu);
 			v = (INT64)(startv);
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			if (!ds_powersoftwo)
-			{
-				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-				// Carefully align all of my Friends.
-				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
-
-				*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
-			}
-			else
-				*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
+			*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dsrc++);
 		}
 		else
 		{
 			double left = width;
-			iz += ds_sz.x * left;
-			uz += ds_su.x * left;
-			vz += ds_sv.x * left;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
 
 			endz = 1.f/iz;
 			endu = uz*endz;
@@ -1052,24 +1044,7 @@ void R_DrawTiltedTranslucentSpan_8(void)
 			for (; width != 0; width--)
 			{
 				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-				if (!ds_powersoftwo)
-				{
-					fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-					fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-					// Carefully align all of my Friends.
-					if (x < 0)
-						x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-					if (y < 0)
-						y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-					x %= ds_flatwidth;
-					y %= ds_flatheight;
-
-					*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
-				}
-				else
-					*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest);
+				*dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dsrc++);
 				dest++;
 				u += stepu;
 				v += stepv;
@@ -1078,6 +1053,7 @@ void R_DrawTiltedTranslucentSpan_8(void)
 	}
 #endif
 }
+#endif // NOWATER
 
 void R_DrawTiltedSplat_8(void)
 {
@@ -1098,22 +1074,22 @@ void R_DrawTiltedSplat_8(void)
 	double endz, endu, endv;
 	UINT32 stepu, stepv;
 
-	iz = ds_sz.z + ds_sz.y*(centery-ds_y) + ds_sz.x*(ds_x1-centerx);
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
 
 	// Lighting is simple. It's just linear interpolation from start to end
 	{
 		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
 		float lightstart, lightend;
 
-		lightend = (iz + ds_sz.x*width) * planelightfloat;
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
 		lightstart = iz * planelightfloat;
 
 		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
 		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
 	}
 
-	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);
+	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);
 
 	dest = ylookup[ds_y] + columnofs[ds_x1];
 	source = ds_source;
@@ -1130,32 +1106,14 @@ void R_DrawTiltedSplat_8(void)
 
 		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
 
-		if (!ds_powersoftwo)
-		{
-			fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-			fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
-
-			val = source[((y * ds_flatwidth) + x)];
-		}
-		else
-			val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
-
+		val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
 		if (val != TRANSPARENTPIXEL)
 			*dest = colormap[val];
 
 		dest++;
-		iz += ds_sz.x;
-		uz += ds_su.x;
-		vz += ds_sv.x;
+		iz += ds_szp->x;
+		uz += ds_sup->x;
+		vz += ds_svp->x;
 	} while (--width >= 0);
 #else
 #define SPANSIZE 16
@@ -1165,9 +1123,9 @@ void R_DrawTiltedSplat_8(void)
 	startu = uz*startz;
 	startv = vz*startz;
 
-	izstep = ds_sz.x * SPANSIZE;
-	uzstep = ds_su.x * SPANSIZE;
-	vzstep = ds_sv.x * SPANSIZE;
+	izstep = ds_szp->x * SPANSIZE;
+	uzstep = ds_sup->x * SPANSIZE;
+	vzstep = ds_svp->x * SPANSIZE;
 	//x1 = 0;
 	width++;
 
@@ -1188,24 +1146,7 @@ void R_DrawTiltedSplat_8(void)
 		for (i = SPANSIZE-1; i >= 0; i--)
 		{
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			if (!ds_powersoftwo)
-			{
-				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-				// Carefully align all of my Friends.
-				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
-
-				val = source[((y * ds_flatwidth) + x)];
-			}
-			else
-				val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
+			val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
 			if (val != TRANSPARENTPIXEL)
 				*dest = colormap[val];
 			dest++;
@@ -1223,33 +1164,16 @@ void R_DrawTiltedSplat_8(void)
 			u = (INT64)(startu);
 			v = (INT64)(startv);
 			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
-			if (!ds_powersoftwo)
-			{
-				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-				// Carefully align all of my Friends.
-				if (x < 0)
-					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-				if (y < 0)
-					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-				x %= ds_flatwidth;
-				y %= ds_flatheight;
-
-				val = source[((y * ds_flatwidth) + x)];
-			}
-			else
-				val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
+			val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
 			if (val != TRANSPARENTPIXEL)
 				*dest = colormap[val];
 		}
 		else
 		{
 			double left = width;
-			iz += ds_sz.x * left;
-			uz += ds_su.x * left;
-			vz += ds_sv.x * left;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
 
 			endz = 1.f/iz;
 			endu = uz*endz;
@@ -1264,24 +1188,6 @@ void R_DrawTiltedSplat_8(void)
 			{
 				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
 				val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
-				if (!ds_powersoftwo)
-				{
-					fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
-					fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
-
-					// Carefully align all of my Friends.
-					if (x < 0)
-						x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-					if (y < 0)
-						y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-					x %= ds_flatwidth;
-					y %= ds_flatheight;
-
-					val = source[((y * ds_flatwidth) + x)];
-				}
-				else
-					val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
 				if (val != TRANSPARENTPIXEL)
 					*dest = colormap[val];
 				dest++;
@@ -1321,125 +1227,95 @@ void R_DrawSplat_8 (void)
 	// bit per power of two (obviously)
 	// Ok, because I was able to eliminate the variable spot below, this function is now FASTER
 	// than the original span renderer. Whodathunkit?
-	if (ds_powersoftwo)
-	{
-		xposition <<= nflatshiftup; yposition <<= nflatshiftup;
-		xstep <<= nflatshiftup; ystep <<= nflatshiftup;
-	}
+	xposition <<= nflatshiftup; yposition <<= nflatshiftup;
+	xstep <<= nflatshiftup; ystep <<= nflatshiftup;
 
 	source = ds_source;
 	colormap = ds_colormap;
 	dest = ylookup[ds_y] + columnofs[ds_x1];
 
-	if (!ds_powersoftwo)
+	while (count >= 8)
 	{
-		while (count-- && dest <= deststop)
-		{
-			fixed_t x = (xposition >> FRACBITS);
-			fixed_t y = (yposition >> FRACBITS);
-
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
-
-			val = source[((y * ds_flatwidth) + x)];
-			if (val != TRANSPARENTPIXEL)
-				*dest = colormap[val];
-			dest++;
-			xposition += xstep;
-			yposition += ystep;
-		}
-	}
-	else
-	{
-		while (count >= 8)
-		{
-			// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
-			// have the uber complicated math to calculate it now, so that was a memory write we didn't
-			// need!
-			//
-			// <Callum> 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size)
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[0] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
+		// have the uber complicated math to calculate it now, so that was a memory write we didn't
+		// need!
+		//
+		// <Callum> 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size)
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[0] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[1] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[1] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[2] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[2] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[3] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[3] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[4] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[4] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[5] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[5] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[6] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[6] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			val &= 4194303;
-			val = source[val];
-			if (val != TRANSPARENTPIXEL)
-				dest[7] = colormap[val];
-			xposition += xstep;
-			yposition += ystep;
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		val &= 4194303;
+		val = source[val];
+		if (val != TRANSPARENTPIXEL)
+			dest[7] = colormap[val];
+		xposition += xstep;
+		yposition += ystep;
 
-			dest += 8;
-			count -= 8;
-		}
-		while (count-- && dest <= deststop)
-		{
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				*dest = colormap[val];
-			dest++;
-			xposition += xstep;
-			yposition += ystep;
-		}
+		dest += 8;
+		count -= 8;
+	}
+	while (count-- && dest <= deststop)
+	{
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			*dest = colormap[val];
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
 	}
 }
 
@@ -1470,112 +1346,82 @@ void R_DrawTranslucentSplat_8 (void)
 	// bit per power of two (obviously)
 	// Ok, because I was able to eliminate the variable spot below, this function is now FASTER
 	// than the original span renderer. Whodathunkit?
-	if (ds_powersoftwo)
-	{
-		xposition <<= nflatshiftup; yposition <<= nflatshiftup;
-		xstep <<= nflatshiftup; ystep <<= nflatshiftup;
-	}
+	xposition <<= nflatshiftup; yposition <<= nflatshiftup;
+	xstep <<= nflatshiftup; ystep <<= nflatshiftup;
 
 	source = ds_source;
 	colormap = ds_colormap;
 	dest = ylookup[ds_y] + columnofs[ds_x1];
 
-	if (!ds_powersoftwo)
+	while (count >= 8)
 	{
-		while (count-- && dest <= deststop)
-		{
-			fixed_t x = (xposition >> FRACBITS);
-			fixed_t y = (yposition >> FRACBITS);
-
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
-
-			val = source[((y * ds_flatwidth) + x)];
-			if (val != TRANSPARENTPIXEL)
-				*dest = *(ds_transmap + (colormap[val] << 8) + *dest);
-			dest++;
-			xposition += xstep;
-			yposition += ystep;
-		}
-	}
-	else
-	{
-		while (count >= 8)
-		{
-			// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
-			// have the uber complicated math to calculate it now, so that was a memory write we didn't
-			// need!
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]);
-			xposition += xstep;
-			yposition += ystep;
+		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
+		// have the uber complicated math to calculate it now, so that was a memory write we didn't
+		// need!
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[0] = *(ds_transmap + (colormap[val] << 8) + dest[0]);
+		xposition += xstep;
+		yposition += ystep;
 
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]);
-			xposition += xstep;
-			yposition += ystep;
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[1] = *(ds_transmap + (colormap[val] << 8) + dest[1]);
+		xposition += xstep;
+		yposition += ystep;
 
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]);
-			xposition += xstep;
-			yposition += ystep;
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[2] = *(ds_transmap + (colormap[val] << 8) + dest[2]);
+		xposition += xstep;
+		yposition += ystep;
 
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]);
-			xposition += xstep;
-			yposition += ystep;
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[3] = *(ds_transmap + (colormap[val] << 8) + dest[3]);
+		xposition += xstep;
+		yposition += ystep;
 
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]);
-			xposition += xstep;
-			yposition += ystep;
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[4] = *(ds_transmap + (colormap[val] << 8) + dest[4]);
+		xposition += xstep;
+		yposition += ystep;
 
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]);
-			xposition += xstep;
-			yposition += ystep;
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[5] = *(ds_transmap + (colormap[val] << 8) + dest[5]);
+		xposition += xstep;
+		yposition += ystep;
 
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]);
-			xposition += xstep;
-			yposition += ystep;
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[6] = *(ds_transmap + (colormap[val] << 8) + dest[6]);
+		xposition += xstep;
+		yposition += ystep;
 
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]);
-			xposition += xstep;
-			yposition += ystep;
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			dest[7] = *(ds_transmap + (colormap[val] << 8) + dest[7]);
+		xposition += xstep;
+		yposition += ystep;
 
-			dest += 8;
-			count -= 8;
-		}
-		while (count-- && dest <= deststop)
-		{
-			val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
-			if (val != TRANSPARENTPIXEL)
-				*dest = *(ds_transmap + (colormap[val] << 8) + *dest);
-			dest++;
-			xposition += xstep;
-			yposition += ystep;
-		}
+		dest += 8;
+		count -= 8;
+	}
+	while (count-- && dest <= deststop)
+	{
+		val = source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)];
+		if (val != TRANSPARENTPIXEL)
+			*dest = *(ds_transmap + (colormap[val] << 8) + *dest);
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
 	}
 }
 
 /**	\brief The R_DrawTranslucentSpan_8 function
-	Draws the actual span with translucent.
+	Draws the actual span with translucency.
 */
 void R_DrawTranslucentSpan_8 (void)
 {
@@ -1601,109 +1447,76 @@ void R_DrawTranslucentSpan_8 (void)
 	// bit per power of two (obviously)
 	// Ok, because I was able to eliminate the variable spot below, this function is now FASTER
 	// than the original span renderer. Whodathunkit?
-	if (ds_powersoftwo)
-	{
-		xposition <<= nflatshiftup; yposition <<= nflatshiftup;
-		xstep <<= nflatshiftup; ystep <<= nflatshiftup;
-	}
+	xposition <<= nflatshiftup; yposition <<= nflatshiftup;
+	xstep <<= nflatshiftup; ystep <<= nflatshiftup;
 
 	source = ds_source;
 	colormap = ds_colormap;
 	dest = ylookup[ds_y] + columnofs[ds_x1];
 
-	if (!ds_powersoftwo)
+	while (count >= 8)
 	{
-		while (count-- && dest <= deststop)
-		{
-			fixed_t x = (xposition >> FRACBITS);
-			fixed_t y = (yposition >> FRACBITS);
-
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
-
-			val = ((y * ds_flatwidth) + x);
-			*dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest);
-			dest++;
-			xposition += xstep;
-			yposition += ystep;
-		}
+		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
+		// have the uber complicated math to calculate it now, so that was a memory write we didn't
+		// need!
+		dest[0] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[0]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[1] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[1]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[2] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[2]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[3] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[3]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[4] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[4]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[5] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[5]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[6] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[6]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[7] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[7]);
+		xposition += xstep;
+		yposition += ystep;
+
+		dest += 8;
+		count -= 8;
 	}
-	else
+	while (count-- && dest <= deststop)
 	{
-		while (count >= 8)
-		{
-			// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
-			// have the uber complicated math to calculate it now, so that was a memory write we didn't
-			// need!
-			dest[0] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[0]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[1] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[1]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[2] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[2]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[3] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[3]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[4] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[4]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[5] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[5]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[6] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[6]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest[7] = *(ds_transmap + (colormap[source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)]] << 8) + dest[7]);
-			xposition += xstep;
-			yposition += ystep;
-
-			dest += 8;
-			count -= 8;
-		}
-		while (count-- && dest <= deststop)
-		{
-			val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
-			*dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest);
-			dest++;
-			xposition += xstep;
-			yposition += ystep;
-		}
+		val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift);
+		*dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest);
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
 	}
 }
 
 #ifndef NOWATER
 void R_DrawTranslucentWaterSpan_8(void)
 {
-	fixed_t xposition;
-	fixed_t yposition;
-	fixed_t xstep, ystep;
+	UINT32 xposition;
+	UINT32 yposition;
+	UINT32 xstep, ystep;
 
 	UINT8 *source;
 	UINT8 *colormap;
 	UINT8 *dest;
 	UINT8 *dsrc;
-	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
-
-	size_t count = (ds_x2 - ds_x1 + 1);
 
-	xposition = ds_xfrac; yposition = (ds_yfrac + ds_waterofs);
-	xstep = ds_xstep; ystep = ds_ystep;
+	size_t count;
 
 	// SoM: we only need 6 bits for the integer part (0 thru 63) so the rest
 	// can be used for the fraction part. This allows calculation of the memory address in the
@@ -1712,46 +1525,60 @@ void R_DrawTranslucentWaterSpan_8(void)
 	// bit per power of two (obviously)
 	// Ok, because I was able to eliminate the variable spot below, this function is now FASTER
 	// than the original span renderer. Whodathunkit?
-	if (ds_powersoftwo)
-	{
-		xposition <<= nflatshiftup; yposition <<= nflatshiftup;
-		xstep <<= nflatshiftup; ystep <<= nflatshiftup;
-	}
+	xposition = ds_xfrac << nflatshiftup; yposition = (ds_yfrac + ds_waterofs) << nflatshiftup;
+	xstep = ds_xstep << nflatshiftup; ystep = ds_ystep << nflatshiftup;
 
 	source = ds_source;
 	colormap = ds_colormap;
 	dest = ylookup[ds_y] + columnofs[ds_x1];
 	dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1;
+	count = ds_x2 - ds_x1 + 1;
 
-	if (!ds_powersoftwo)
+	while (count >= 8)
 	{
-		while (count-- && dest <= deststop)
-		{
-			fixed_t x = (xposition >> FRACBITS);
-			fixed_t y = (yposition >> FRACBITS);
-
-			// Carefully align all of my Friends.
-			if (x < 0)
-				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
-			if (y < 0)
-				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
-
-			x %= ds_flatwidth;
-			y %= ds_flatheight;
-
-			*dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x)] << 8) + *dsrc++)];
-			xposition += xstep;
-			yposition += ystep;
-		}
+		// SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't
+		// have the uber complicated math to calculate it now, so that was a memory write we didn't
+		// need!
+		dest[0] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[1] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[2] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[3] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[4] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[5] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[6] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest[7] = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+
+		dest += 8;
+		count -= 8;
 	}
-	else
+	while (count--)
 	{
-		while (count-- && dest <= deststop)
-		{
-			*dest++ = colormap[*(ds_transmap + (source[(((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift)] << 8) + *dsrc++)];
-			xposition += xstep;
-			yposition += ystep;
-		}
+		*dest++ = colormap[*(ds_transmap + (source[((yposition >> nflatyshift) & nflatmask) | (xposition >> nflatxshift)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
 	}
 }
 #endif
@@ -1880,7 +1707,7 @@ void R_DrawColumnShadowed_8(void)
 
 		if (dc_yh > realyh)
 			dc_yh = realyh;
-		basecolfunc();		// R_DrawColumn_8 for the appropriate architecture
+		(colfuncs[BASEDRAWFUNC])();		// R_DrawColumn_8 for the appropriate architecture
 		if (solid)
 			dc_yl = bheight;
 		else
@@ -1890,5 +1717,5 @@ void R_DrawColumnShadowed_8(void)
 	}
 	dc_yh = realyh;
 	if (dc_yl <= realyh)
-		walldrawerfunc();		// R_DrawWallColumn_8 for the appropriate architecture
+		(colfuncs[BASEDRAWFUNC])();		// R_DrawWallColumn_8 for the appropriate architecture
 }
diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c
new file mode 100644
index 0000000000000000000000000000000000000000..748ca195c585e163fba3f67cc72241c6f81949dd
--- /dev/null
+++ b/src/r_draw8_npo2.c
@@ -0,0 +1,1044 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file  r_draw8_npo2.c
+/// \brief 8bpp span drawer functions (for non-powers-of-two flat dimensions)
+/// \note  no includes because this is included as part of r_draw.c
+
+// ==========================================================================
+// SPANS
+// ==========================================================================
+
+/**	\brief The R_DrawSpan_NPO2_8 function
+	Draws the actual span.
+*/
+void R_DrawSpan_NPO2_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	source = ds_source;
+	colormap = ds_colormap;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	if (dest+8 > deststop)
+		return;
+
+	while (count-- && dest <= deststop)
+	{
+		fixed_t x = (xposition >> FRACBITS);
+		fixed_t y = (yposition >> FRACBITS);
+
+		// Carefully align all of my Friends.
+		if (x < 0)
+			x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+		if (y < 0)
+			y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+		x %= ds_flatwidth;
+		y %= ds_flatheight;
+
+		*dest++ = colormap[source[((y * ds_flatwidth) + x)]];
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
+#ifdef ESLOPE
+/**	\brief The R_DrawTiltedSpan_NPO2_8 function
+	Draw slopes! Holy sheit!
+*/
+void R_DrawTiltedSpan_NPO2_8(void)
+{
+	// x1, x2 = ds_x1, ds_x2
+	int width = ds_x2 - ds_x1;
+	double iz, uz, vz;
+	UINT32 u, v;
+	int i;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+
+	double startz, startu, startv;
+	double izstep, uzstep, vzstep;
+	double endz, endu, endv;
+	UINT32 stepu, stepv;
+
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
+
+	// Lighting is simple. It's just linear interpolation from start to end
+	{
+		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
+		float lightstart, lightend;
+
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
+		lightstart = iz * planelightfloat;
+
+		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
+		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
+	}
+
+	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);
+
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	source = ds_source;
+	//colormap = ds_colormap;
+
+#if 0	// The "perfect" reference version of this routine. Pretty slow.
+		// Use it only to see how things are supposed to look.
+	i = 0;
+	do
+	{
+		double z = 1.f/iz;
+		u = (INT64)(uz*z) + viewx;
+		v = (INT64)(vz*z) + viewy;
+
+		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+
+		// Lactozilla: Non-powers-of-two
+		{
+			fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+			fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+			// Carefully align all of my Friends.
+			if (x < 0)
+				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+			if (y < 0)
+				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+			x %= ds_flatwidth;
+			y %= ds_flatheight;
+
+			*dest = colormap[source[((y * ds_flatwidth) + x)]];
+		}
+		dest++;
+		iz += ds_szp->x;
+		uz += ds_sup->x;
+		vz += ds_svp->x;
+	} while (--width >= 0);
+#else
+#define SPANSIZE 16
+#define INVSPAN	0.0625f
+
+	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;
+	//x1 = 0;
+	width++;
+
+	while (width >= SPANSIZE)
+	{
+		iz += izstep;
+		uz += uzstep;
+		vz += vzstep;
+
+		endz = 1.f/iz;
+		endu = uz*endz;
+		endv = vz*endz;
+		stepu = (INT64)((endu - startu) * INVSPAN);
+		stepv = (INT64)((endv - startv) * INVSPAN);
+		u = (INT64)(startu) + viewx;
+		v = (INT64)(startv) + viewy;
+
+		for (i = SPANSIZE-1; i >= 0; i--)
+		{
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				*dest = colormap[source[((y * ds_flatwidth) + x)]];
+			}
+			dest++;
+			u += stepu;
+			v += stepv;
+		}
+		startu = endu;
+		startv = endv;
+		width -= SPANSIZE;
+	}
+	if (width > 0)
+	{
+		if (width == 1)
+		{
+			u = (INT64)(startu);
+			v = (INT64)(startv);
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				*dest = colormap[source[((y * ds_flatwidth) + x)]];
+			}
+		}
+		else
+		{
+			double left = width;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
+
+			endz = 1.f/iz;
+			endu = uz*endz;
+			endv = vz*endz;
+			left = 1.f/left;
+			stepu = (INT64)((endu - startu) * left);
+			stepv = (INT64)((endv - startv) * left);
+			u = (INT64)(startu) + viewx;
+			v = (INT64)(startv) + viewy;
+
+			for (; width != 0; width--)
+			{
+				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+				// Lactozilla: Non-powers-of-two
+				{
+					fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+					fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+					// Carefully align all of my Friends.
+					if (x < 0)
+						x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					if (y < 0)
+						y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+					x %= ds_flatwidth;
+					y %= ds_flatheight;
+
+					*dest = colormap[source[((y * ds_flatwidth) + x)]];
+				}
+				dest++;
+				u += stepu;
+				v += stepv;
+			}
+		}
+	}
+#endif
+}
+
+/**	\brief The R_DrawTiltedTranslucentSpan_NPO2_8 function
+	Like DrawTiltedSpan_NPO2, but translucent
+*/
+void R_DrawTiltedTranslucentSpan_NPO2_8(void)
+{
+	// x1, x2 = ds_x1, ds_x2
+	int width = ds_x2 - ds_x1;
+	double iz, uz, vz;
+	UINT32 u, v;
+	int i;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+
+	double startz, startu, startv;
+	double izstep, uzstep, vzstep;
+	double endz, endu, endv;
+	UINT32 stepu, stepv;
+
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
+
+	// Lighting is simple. It's just linear interpolation from start to end
+	{
+		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
+		float lightstart, lightend;
+
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
+		lightstart = iz * planelightfloat;
+
+		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
+		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
+	}
+
+	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);
+
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	source = ds_source;
+	//colormap = ds_colormap;
+
+#if 0	// The "perfect" reference version of this routine. Pretty slow.
+		// Use it only to see how things are supposed to look.
+	i = 0;
+	do
+	{
+		double z = 1.f/iz;
+		u = (INT64)(uz*z) + viewx;
+		v = (INT64)(vz*z) + viewy;
+
+		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+		// Lactozilla: Non-powers-of-two
+		{
+			fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+			fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+			// Carefully align all of my Friends.
+			if (x < 0)
+				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+			if (y < 0)
+				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+			x %= ds_flatwidth;
+			y %= ds_flatheight;
+
+			*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
+		}
+		dest++;
+		iz += ds_szp->x;
+		uz += ds_sup->x;
+		vz += ds_svp->x;
+	} while (--width >= 0);
+#else
+#define SPANSIZE 16
+#define INVSPAN	0.0625f
+
+	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;
+	//x1 = 0;
+	width++;
+
+	while (width >= SPANSIZE)
+	{
+		iz += izstep;
+		uz += uzstep;
+		vz += vzstep;
+
+		endz = 1.f/iz;
+		endu = uz*endz;
+		endv = vz*endz;
+		stepu = (INT64)((endu - startu) * INVSPAN);
+		stepv = (INT64)((endv - startv) * INVSPAN);
+		u = (INT64)(startu) + viewx;
+		v = (INT64)(startv) + viewy;
+
+		for (i = SPANSIZE-1; i >= 0; i--)
+		{
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
+			}
+			dest++;
+			u += stepu;
+			v += stepv;
+		}
+		startu = endu;
+		startv = endv;
+		width -= SPANSIZE;
+	}
+	if (width > 0)
+	{
+		if (width == 1)
+		{
+			u = (INT64)(startu);
+			v = (INT64)(startv);
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
+			}
+		}
+		else
+		{
+			double left = width;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
+
+			endz = 1.f/iz;
+			endu = uz*endz;
+			endv = vz*endz;
+			left = 1.f/left;
+			stepu = (INT64)((endu - startu) * left);
+			stepv = (INT64)((endv - startv) * left);
+			u = (INT64)(startu) + viewx;
+			v = (INT64)(startv) + viewy;
+
+			for (; width != 0; width--)
+			{
+				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+				// Lactozilla: Non-powers-of-two
+				{
+					fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+					fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+					// Carefully align all of my Friends.
+					if (x < 0)
+						x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					if (y < 0)
+						y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+					x %= ds_flatwidth;
+					y %= ds_flatheight;
+
+					*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest);
+				}
+				dest++;
+				u += stepu;
+				v += stepv;
+			}
+		}
+	}
+#endif
+}
+
+void R_DrawTiltedSplat_NPO2_8(void)
+{
+	// x1, x2 = ds_x1, ds_x2
+	int width = ds_x2 - ds_x1;
+	double iz, uz, vz;
+	UINT32 u, v;
+	int i;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+
+	UINT8 val;
+
+	double startz, startu, startv;
+	double izstep, uzstep, vzstep;
+	double endz, endu, endv;
+	UINT32 stepu, stepv;
+
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
+
+	// Lighting is simple. It's just linear interpolation from start to end
+	{
+		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
+		float lightstart, lightend;
+
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
+		lightstart = iz * planelightfloat;
+
+		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
+		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
+	}
+
+	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);
+
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	source = ds_source;
+	//colormap = ds_colormap;
+
+#if 0	// The "perfect" reference version of this routine. Pretty slow.
+		// Use it only to see how things are supposed to look.
+	i = 0;
+	do
+	{
+		double z = 1.f/iz;
+		u = (INT64)(uz*z) + viewx;
+		v = (INT64)(vz*z) + viewy;
+
+		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+
+		// Lactozilla: Non-powers-of-two
+		{
+			fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+			fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+			// Carefully align all of my Friends.
+			if (x < 0)
+				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+			if (y < 0)
+				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+			x %= ds_flatwidth;
+			y %= ds_flatheight;
+
+			val = source[((y * ds_flatwidth) + x)];
+		}
+
+		if (val != TRANSPARENTPIXEL)
+			*dest = colormap[val];
+
+		dest++;
+		iz += ds_szp->x;
+		uz += ds_sup->x;
+		vz += ds_svp->x;
+	} while (--width >= 0);
+#else
+#define SPANSIZE 16
+#define INVSPAN	0.0625f
+
+	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;
+	//x1 = 0;
+	width++;
+
+	while (width >= SPANSIZE)
+	{
+		iz += izstep;
+		uz += uzstep;
+		vz += vzstep;
+
+		endz = 1.f/iz;
+		endu = uz*endz;
+		endv = vz*endz;
+		stepu = (INT64)((endu - startu) * INVSPAN);
+		stepv = (INT64)((endv - startv) * INVSPAN);
+		u = (INT64)(startu) + viewx;
+		v = (INT64)(startv) + viewy;
+
+		for (i = SPANSIZE-1; i >= 0; i--)
+		{
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				val = source[((y * ds_flatwidth) + x)];
+			}
+			if (val != TRANSPARENTPIXEL)
+				*dest = colormap[val];
+			dest++;
+			u += stepu;
+			v += stepv;
+		}
+		startu = endu;
+		startv = endv;
+		width -= SPANSIZE;
+	}
+	if (width > 0)
+	{
+		if (width == 1)
+		{
+			u = (INT64)(startu);
+			v = (INT64)(startv);
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				val = source[((y * ds_flatwidth) + x)];
+			}
+			if (val != TRANSPARENTPIXEL)
+				*dest = colormap[val];
+		}
+		else
+		{
+			double left = width;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
+
+			endz = 1.f/iz;
+			endu = uz*endz;
+			endv = vz*endz;
+			left = 1.f/left;
+			stepu = (INT64)((endu - startu) * left);
+			stepv = (INT64)((endv - startv) * left);
+			u = (INT64)(startu) + viewx;
+			v = (INT64)(startv) + viewy;
+
+			for (; width != 0; width--)
+			{
+				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+				val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)];
+				// Lactozilla: Non-powers-of-two
+				{
+					fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+					fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+					// Carefully align all of my Friends.
+					if (x < 0)
+						x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					if (y < 0)
+						y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+					x %= ds_flatwidth;
+					y %= ds_flatheight;
+
+					val = source[((y * ds_flatwidth) + x)];
+				}
+				if (val != TRANSPARENTPIXEL)
+					*dest = colormap[val];
+				dest++;
+				u += stepu;
+				v += stepv;
+			}
+		}
+	}
+#endif
+}
+#endif // ESLOPE
+
+/**	\brief The R_DrawSplat_NPO2_8 function
+	Just like R_DrawSpan_NPO2_8, but skips transparent pixels.
+*/
+void R_DrawSplat_NPO2_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+	UINT32 val;
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	source = ds_source;
+	colormap = ds_colormap;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	while (count-- && dest <= deststop)
+	{
+		fixed_t x = (xposition >> FRACBITS);
+		fixed_t y = (yposition >> FRACBITS);
+
+		// Carefully align all of my Friends.
+		if (x < 0)
+			x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+		if (y < 0)
+			y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+		x %= ds_flatwidth;
+		y %= ds_flatheight;
+
+		val = source[((y * ds_flatwidth) + x)];
+		if (val != TRANSPARENTPIXEL)
+			*dest = colormap[val];
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
+/**	\brief The R_DrawTranslucentSplat_NPO2_8 function
+	Just like R_DrawSplat_NPO2_8, but is translucent!
+*/
+void R_DrawTranslucentSplat_NPO2_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+	UINT32 val;
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	source = ds_source;
+	colormap = ds_colormap;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	while (count-- && dest <= deststop)
+	{
+		fixed_t x = (xposition >> FRACBITS);
+		fixed_t y = (yposition >> FRACBITS);
+
+		// Carefully align all of my Friends.
+		if (x < 0)
+			x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+		if (y < 0)
+			y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+		x %= ds_flatwidth;
+		y %= ds_flatheight;
+
+		val = source[((y * ds_flatwidth) + x)];
+		if (val != TRANSPARENTPIXEL)
+			*dest = *(ds_transmap + (colormap[val] << 8) + *dest);
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
+/**	\brief The R_DrawTranslucentSpan_NPO2_8 function
+	Draws the actual span with translucency.
+*/
+void R_DrawTranslucentSpan_NPO2_8 (void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+	UINT32 val;
+
+	xposition = ds_xfrac; yposition = ds_yfrac;
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	source = ds_source;
+	colormap = ds_colormap;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+
+	while (count-- && dest <= deststop)
+	{
+		fixed_t x = (xposition >> FRACBITS);
+		fixed_t y = (yposition >> FRACBITS);
+
+		// Carefully align all of my Friends.
+		if (x < 0)
+			x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+		if (y < 0)
+			y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+		x %= ds_flatwidth;
+		y %= ds_flatheight;
+
+		val = ((y * ds_flatwidth) + x);
+		*dest = *(ds_transmap + (colormap[source[val]] << 8) + *dest);
+		dest++;
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
+#ifndef NOWATER
+void R_DrawTranslucentWaterSpan_NPO2_8(void)
+{
+	fixed_t xposition;
+	fixed_t yposition;
+	fixed_t xstep, ystep;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+	UINT8 *dsrc;
+	const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
+
+	size_t count = (ds_x2 - ds_x1 + 1);
+
+	xposition = ds_xfrac; yposition = (ds_yfrac + ds_waterofs);
+	xstep = ds_xstep; ystep = ds_ystep;
+
+	source = ds_source;
+	colormap = ds_colormap;
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1;
+
+	while (count-- && dest <= deststop)
+	{
+		fixed_t x = (xposition >> FRACBITS);
+		fixed_t y = (yposition >> FRACBITS);
+
+		// Carefully align all of my Friends.
+		if (x < 0)
+			x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+		if (y < 0)
+			y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+		x %= ds_flatwidth;
+		y %= ds_flatheight;
+
+		*dest++ = colormap[*(ds_transmap + (source[((y * ds_flatwidth) + x)] << 8) + *dsrc++)];
+		xposition += xstep;
+		yposition += ystep;
+	}
+}
+
+#ifdef ESLOPE
+/**	\brief The R_DrawTiltedTranslucentWaterSpan_NPO2_8 function
+	Like DrawTiltedTranslucentSpan_NPO2, but for water
+*/
+void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void)
+{
+	// x1, x2 = ds_x1, ds_x2
+	int width = ds_x2 - ds_x1;
+	double iz, uz, vz;
+	UINT32 u, v;
+	int i;
+
+	UINT8 *source;
+	UINT8 *colormap;
+	UINT8 *dest;
+	UINT8 *dsrc;
+
+	double startz, startu, startv;
+	double izstep, uzstep, vzstep;
+	double endz, endu, endv;
+	UINT32 stepu, stepv;
+
+	iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx);
+
+	// Lighting is simple. It's just linear interpolation from start to end
+	{
+		float planelightfloat = BASEVIDWIDTH*BASEVIDWIDTH/vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f;
+		float lightstart, lightend;
+
+		lightend = (iz + ds_szp->x*width) * planelightfloat;
+		lightstart = iz * planelightfloat;
+
+		R_CalcTiltedLighting(FLOAT_TO_FIXED(lightstart), FLOAT_TO_FIXED(lightend));
+		//CONS_Printf("tilted lighting %f to %f (foc %f)\n", lightstart, lightend, focallengthf);
+	}
+
+	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);
+
+	dest = ylookup[ds_y] + columnofs[ds_x1];
+	dsrc = screens[1] + (ds_y+ds_bgofs)*vid.width + ds_x1;
+	source = ds_source;
+	//colormap = ds_colormap;
+
+#if 0	// The "perfect" reference version of this routine. Pretty slow.
+		// Use it only to see how things are supposed to look.
+	i = 0;
+	do
+	{
+		double z = 1.f/iz;
+		u = (INT64)(uz*z) + viewx;
+		v = (INT64)(vz*z) + viewy;
+
+		colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+		// Lactozilla: Non-powers-of-two
+		{
+			fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+			fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+			// Carefully align all of my Friends.
+			if (x < 0)
+				x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+			if (y < 0)
+				y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+			x %= ds_flatwidth;
+			y %= ds_flatheight;
+
+			*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++);
+		}
+		dest++;
+		iz += ds_szp->x;
+		uz += ds_sup->x;
+		vz += ds_svp->x;
+	} while (--width >= 0);
+#else
+#define SPANSIZE 16
+#define INVSPAN	0.0625f
+
+	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;
+	//x1 = 0;
+	width++;
+
+	while (width >= SPANSIZE)
+	{
+		iz += izstep;
+		uz += uzstep;
+		vz += vzstep;
+
+		endz = 1.f/iz;
+		endu = uz*endz;
+		endv = vz*endz;
+		stepu = (INT64)((endu - startu) * INVSPAN);
+		stepv = (INT64)((endv - startv) * INVSPAN);
+		u = (INT64)(startu) + viewx;
+		v = (INT64)(startv) + viewy;
+
+		for (i = SPANSIZE-1; i >= 0; i--)
+		{
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++);
+			}
+			dest++;
+			u += stepu;
+			v += stepv;
+		}
+		startu = endu;
+		startv = endv;
+		width -= SPANSIZE;
+	}
+	if (width > 0)
+	{
+		if (width == 1)
+		{
+			u = (INT64)(startu);
+			v = (INT64)(startv);
+			colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+			// Lactozilla: Non-powers-of-two
+			{
+				fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+				fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+				// Carefully align all of my Friends.
+				if (x < 0)
+					x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+				if (y < 0)
+					y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+				x %= ds_flatwidth;
+				y %= ds_flatheight;
+
+				*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++);
+			}
+		}
+		else
+		{
+			double left = width;
+			iz += ds_szp->x * left;
+			uz += ds_sup->x * left;
+			vz += ds_svp->x * left;
+
+			endz = 1.f/iz;
+			endu = uz*endz;
+			endv = vz*endz;
+			left = 1.f/left;
+			stepu = (INT64)((endu - startu) * left);
+			stepv = (INT64)((endv - startv) * left);
+			u = (INT64)(startu) + viewx;
+			v = (INT64)(startv) + viewy;
+
+			for (; width != 0; width--)
+			{
+				colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps);
+				// Lactozilla: Non-powers-of-two
+				{
+					fixed_t x = (((fixed_t)u-viewx) >> FRACBITS);
+					fixed_t y = (((fixed_t)v-viewy) >> FRACBITS);
+
+					// Carefully align all of my Friends.
+					if (x < 0)
+						x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth);
+					if (y < 0)
+						y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight);
+
+					x %= ds_flatwidth;
+					y %= ds_flatheight;
+
+					*dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++);
+				}
+				dest++;
+				u += stepu;
+				v += stepv;
+			}
+		}
+	}
+#endif
+}
+#endif // ESLOPE
+#endif // NOWATER
diff --git a/src/r_local.h b/src/r_local.h
index 1d3187750ca607111eaacb0a0d41b07f6d1bd2b2..379d5520588a479dc82873365b83c331b0ce1c05 100644
--- a/src/r_local.h
+++ b/src/r_local.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_main.c b/src/r_main.c
index 5e758e52a02d967fd21ccb48d8868753ed07eb46..22061b407bc7a7fd4e52acbb48061b09cc6eb566 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -19,6 +19,7 @@
 #include "r_local.h"
 #include "r_splats.h" // faB(21jan): testing
 #include "r_sky.h"
+#include "hu_stuff.h"
 #include "st_stuff.h"
 #include "p_local.h"
 #include "keys.h"
@@ -28,6 +29,7 @@
 #include "d_main.h"
 #include "v_video.h"
 #include "p_spec.h" // skyboxmo
+#include "p_setup.h"
 #include "z_zone.h"
 #include "m_random.h" // quake camera shake
 #include "r_portal.h"
@@ -696,6 +698,7 @@ subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y)
 	INT32 side, i;
 	size_t nodenum;
 	subsector_t *ret;
+	seg_t *seg;
 
 	// single subsector is a special case
 	if (numnodes == 0)
@@ -711,10 +714,15 @@ subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y)
 	}
 
 	ret = &subsectors[nodenum & ~NF_SUBSECTOR];
-	for (i = 0; i < ret->numlines; i++)
-		//if (R_PointOnSegSide(x, y, &segs[ret->firstline + i])) -- breaks in ogl because polyvertex_t cast over vertex pointers
-		if (P_PointOnLineSide(x, y, segs[ret->firstline + i].linedef) != segs[ret->firstline + i].side)
+	for (i = 0, seg = &segs[ret->firstline]; i < ret->numlines; i++, seg++)
+	{
+		if (seg->glseg)
+			continue;
+
+		//if (R_PointOnSegSide(x, y, seg)) -- breaks in ogl because polyvertex_t cast over vertex pointers
+		if (P_PointOnLineSide(x, y, seg->linedef) != seg->side)
 			return 0;
+	}
 
 	return ret;
 }
@@ -1008,11 +1016,13 @@ static void R_PortalFrame(portal_t *portal)
 	if (portal->clipline != -1)
 	{
 		portalclipline = &lines[portal->clipline];
+		portalcullsector = portalclipline->frontsector;
 		viewsector = portalclipline->frontsector;
 	}
 	else
 	{
 		portalclipline = NULL;
+		portalcullsector = NULL;
 		viewsector = R_PointInSubsector(viewx, viewy)->sector;
 	}
 }
@@ -1152,6 +1162,26 @@ void R_RenderPlayerView(player_t *player)
 	free(masks);
 }
 
+// Lactozilla: Renderer switching
+#ifdef HWRENDER
+void R_InitHardwareMode(void)
+{
+	HWR_AddSessionCommands();
+	HWR_Switch();
+	HWR_LoadTextures(numtextures);
+	if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))
+		HWR_SetupLevel();
+}
+#endif
+
+void R_ReloadHUDGraphics(void)
+{
+	CONS_Debug(DBG_RENDER, "R_ReloadHUDGraphics()...\n");
+	ST_LoadGraphics();
+	HU_LoadGraphics();
+	ST_ReloadSkinFaceGraphics();
+}
+
 // =========================================================================
 //                    ENGINE COMMANDS & VARS
 // =========================================================================
@@ -1190,6 +1220,7 @@ void R_RegisterEngineStuff(void)
 	CV_RegisterVar(&cv_cam_speed);
 	CV_RegisterVar(&cv_cam_rotate);
 	CV_RegisterVar(&cv_cam_rotspeed);
+	CV_RegisterVar(&cv_cam_turnmultiplier);
 	CV_RegisterVar(&cv_cam_orbit);
 	CV_RegisterVar(&cv_cam_adjust);
 
@@ -1199,6 +1230,7 @@ void R_RegisterEngineStuff(void)
 	CV_RegisterVar(&cv_cam2_speed);
 	CV_RegisterVar(&cv_cam2_rotate);
 	CV_RegisterVar(&cv_cam2_rotspeed);
+	CV_RegisterVar(&cv_cam2_turnmultiplier);
 	CV_RegisterVar(&cv_cam2_orbit);
 	CV_RegisterVar(&cv_cam2_adjust);
 
@@ -1208,30 +1240,4 @@ void R_RegisterEngineStuff(void)
 	CV_RegisterVar(&cv_maxportals);
 
 	CV_RegisterVar(&cv_movebob);
-
-#ifdef HWRENDER
-	// GL-specific Commands
-	CV_RegisterVar(&cv_grgammablue);
-	CV_RegisterVar(&cv_grgammagreen);
-	CV_RegisterVar(&cv_grgammared);
-	CV_RegisterVar(&cv_grfovchange);
-	CV_RegisterVar(&cv_grfog);
-	CV_RegisterVar(&cv_grfogcolor);
-	CV_RegisterVar(&cv_grsoftwarefog);
-#ifdef ALAM_LIGHTING
-	CV_RegisterVar(&cv_grstaticlighting);
-	CV_RegisterVar(&cv_grdynamiclighting);
-	CV_RegisterVar(&cv_grcoronas);
-	CV_RegisterVar(&cv_grcoronasize);
-#endif
-	CV_RegisterVar(&cv_grmodelinterpolation);
-	CV_RegisterVar(&cv_grmodels);
-	CV_RegisterVar(&cv_grspritebillboarding);
-	CV_RegisterVar(&cv_grskydome);
-#endif
-
-#ifdef HWRENDER
-	if (rendermode != render_soft && rendermode != render_none)
-		HWR_AddCommands();
-#endif
 }
diff --git a/src/r_main.h b/src/r_main.h
index ae74ee067d145abc8ed5eb8bacb1e723442050eb..143cc1de0225e978cc2d549f64878a08603f58b5 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -89,6 +89,10 @@ extern consvar_t cv_tailspickup;
 
 // Called by startup code.
 void R_Init(void);
+#ifdef HWRENDER
+void R_InitHardwareMode(void);
+#endif
+void R_ReloadHUDGraphics(void);
 
 // just sets setsizeneeded true
 extern boolean setsizeneeded;
diff --git a/src/r_patch.c b/src/r_patch.c
index 639f682fd07b1c839ebd585bc7398072480d17b3..fbb64e3621a266f3febab774f4c66b52a4a60615 100644
--- a/src/r_patch.c
+++ b/src/r_patch.c
@@ -1,8 +1,9 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 2005-2009 by Andrey "entryway" Budko.
+// Copyright (C) 2018-2019 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2019      by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -48,8 +49,6 @@
 #endif
 
 static unsigned char imgbuf[1<<26];
-fixed_t cosang2rad[ROTANGLES];
-fixed_t sinang2rad[ROTANGLES];
 
 //
 // R_CheckIfPatch
@@ -786,13 +785,11 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
 {
 	char *sprinfoToken;
 	size_t sprinfoTokenLength;
-	char *frameChar;
-	UINT8 frameFrame;
-#ifdef ROTSPRITE
+	char *frameChar = NULL;
+	UINT8 frameFrame = 0xFF;
 	INT16 frameXPivot = 0;
 	INT16 frameYPivot = 0;
 	rotaxis_t frameRotAxis = 0;
-#endif
 
 	// Sprite identifier
 	sprinfoToken = M_GetToken(NULL);
@@ -827,7 +824,6 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
 			}
 			while (strcmp(sprinfoToken,"}")!=0)
 			{
-#ifdef ROTSPRITE
 				if (stricmp(sprinfoToken, "XPIVOT")==0)
 				{
 					Z_Free(sprinfoToken);
@@ -851,7 +847,6 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
 					else if ((stricmp(sprinfoToken, "Z")==0) || (stricmp(sprinfoToken, "ZAXIS")==0) || (stricmp(sprinfoToken, "YAW")==0))
 						frameRotAxis = ROTAXIS_Z;
 				}
-#endif
 				Z_Free(sprinfoToken);
 
 				sprinfoToken = M_GetToken(NULL);
@@ -865,11 +860,9 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
 	}
 
 	// set fields
-#ifdef ROTSPRITE
 	info->pivot[frameFrame].x = frameXPivot;
 	info->pivot[frameFrame].y = frameYPivot;
 	info->pivot[frameFrame].rotaxis = frameRotAxis;
-#endif
 }
 
 //
@@ -1092,16 +1085,60 @@ void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps)
 	for (i = 0; i < numlumps; i++, lumpinfo++)
 	{
 		name = lumpinfo->name;
-		// load SPRTINFO lumps
-		if (!stricmp(name, "SPRTINFO"))
+		// Load SPRTINFO and SPR_ lumps as SpriteInfo
+		if (!memcmp(name, "SPRTINFO", 8) || !memcmp(name, "SPR_", 4))
 			R_ParseSPRTINFOLump(wadnum, i);
-		// load SPR_ lumps (as DEHACKED lump)
-		else if (!memcmp(name, "SPR_", 4))
-			DEH_LoadDehackedLumpPwad(wadnum, i, false);
 	}
 }
 
+static UINT16 GetPatchPixel(patch_t *patch, INT32 x, INT32 y, boolean flip)
+{
+	fixed_t ofs;
+	column_t *column;
+	UINT8 *source;
+
+	if (x >= 0 && x < SHORT(patch->width))
+	{
+		INT32 topdelta, prevdelta = -1;
+		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[flip ? (patch->width-1-x) : x]));
+		while (column->topdelta != 0xff)
+		{
+			topdelta = column->topdelta;
+			if (topdelta <= prevdelta)
+				topdelta += prevdelta;
+			prevdelta = topdelta;
+			source = (UINT8 *)(column) + 3;
+			for (ofs = 0; ofs < column->length; ofs++)
+			{
+				if ((topdelta + ofs) == y)
+					return source[ofs];
+			}
+			column = (column_t *)((UINT8 *)column + column->length + 4);
+		}
+	}
+
+	return 0xFF00;
+}
+
 #ifdef ROTSPRITE
+//
+// R_GetRollAngle
+//
+// Angles precalculated in R_InitSprites.
+//
+fixed_t rollcosang[ROTANGLES];
+fixed_t rollsinang[ROTANGLES];
+INT32 R_GetRollAngle(angle_t rollangle)
+{
+	INT32 ra = AngleFixed(rollangle)>>FRACBITS;
+#if (ROTANGDIFF > 1)
+	ra += (ROTANGDIFF/2);
+#endif
+	ra /= ROTANGDIFF;
+	ra %= ROTANGLES;
+	return ra;
+}
+
 //
 // R_CacheRotSprite
 //
@@ -1113,8 +1150,8 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 	INT32 angle;
 	patch_t *patch;
 	patch_t *newpatch;
-	UINT16 *rawsrc, *rawdst;
-	size_t size, size2;
+	UINT16 *rawdst;
+	size_t size;
 	INT32 bflip = (flip != 0x00);
 
 #define SPRITE_XCENTER (leftoffset)
@@ -1159,23 +1196,12 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 			leftoffset = width - leftoffset;
 		}
 
-		// Draw the sprite to a temporary buffer.
-		size = (width*height);
-		rawsrc = Z_Malloc(size * sizeof(UINT16), PU_STATIC, NULL);
-
-		// can't memset here
-		for (i = 0; i < size; i++)
-			rawsrc[i] = 0xFF00;
-
-		R_PatchToFlat_16bpp(patch, rawsrc, bflip);
-
-		// Don't cache angle = 0
 		for (angle = 1; angle < ROTANGLES; angle++)
 		{
 			INT32 newwidth, newheight;
 
-			ca = cosang2rad[angle];
-			sa = sinang2rad[angle];
+			ca = rollcosang[angle];
+			sa = rollsinang[angle];
 
 			// Find the dimensions of the rotated patch.
 			{
@@ -1236,17 +1262,15 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 #undef BOUNDARYADJUST
 			}
 
-			size2 = (newwidth * newheight);
-			if (!size2)
-				size2 = size;
-
-			rawdst = Z_Malloc(size2 * sizeof(UINT16), PU_STATIC, NULL);
+			// Draw the rotated sprite to a temporary buffer.
+			size = (newwidth * newheight);
+			if (!size)
+				size = (width * height);
 
-			// can't memset here
-			for (i = 0; i < size2; i++)
+			rawdst = Z_Malloc(size * sizeof(UINT16), PU_STATIC, NULL);
+			for (i = 0; i < size; i++)
 				rawdst[i] = 0xFF00;
 
-			// Draw the rotated sprite to a temporary buffer.
 			for (dy = 0; dy < newheight; dy++)
 			{
 				for (dx = 0; dx < newwidth; dx++)
@@ -1258,7 +1282,7 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 					sx >>= FRACBITS;
 					sy >>= FRACBITS;
 					if (sx >= 0 && sy >= 0 && sx < width && sy < height)
-						rawdst[(dy*newwidth)+dx] = rawsrc[(sy*width)+sx];
+						rawdst[(dy*newwidth)+dx] = GetPatchPixel(patch, sx, sy, bflip);
 				}
 			}
 
@@ -1295,7 +1319,6 @@ void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, sp
 		sprframe->rotsprite.cached[rot] = true;
 
 		// free image data
-		Z_Free(rawsrc);
 		Z_Free(patch);
 	}
 #undef SPRITE_XCENTER
@@ -1373,4 +1396,19 @@ void R_FreeSkinRotSprite(size_t skinnum)
 		skinsprites++;
 	}
 }
+
+//
+// R_FreeAllRotSprite
+//
+// Free ALL sprite rotation data from memory.
+//
+void R_FreeAllRotSprite(void)
+{
+	INT32 i;
+	size_t s;
+	for (s = 0; s < numsprites; s++)
+		R_FreeSingleRotSprite(&sprites[s]);
+	for (i = 0; i < numskins; ++i)
+		R_FreeSkinRotSprite(i);
+}
 #endif
diff --git a/src/r_patch.h b/src/r_patch.h
index 23ac025f6909b6739b079927f5b3882e8506aa23..2961448a0bc10d1401ae5bf39e8dc06f6664a30f 100644
--- a/src/r_patch.h
+++ b/src/r_patch.h
@@ -1,8 +1,8 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 2018-2019 by Jaime "Lactozilla" Passos.
+// Copyright (C) 2019      by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -17,13 +17,12 @@
 #include "r_defs.h"
 #include "doomdef.h"
 
-// structs
-#ifdef ROTSPRITE
+// Structs
 typedef enum
 {
-	ROTAXIS_X, // roll (the default)
-	ROTAXIS_Y, // pitch
-	ROTAXIS_Z  // yaw
+	ROTAXIS_X, // Roll (the default)
+	ROTAXIS_Y, // Pitch
+	ROTAXIS_Z  // Yaw
 } rotaxis_t;
 
 typedef struct
@@ -31,46 +30,45 @@ typedef struct
 	INT32 x, y;
 	rotaxis_t rotaxis;
 } spriteframepivot_t;
-#endif
 
 typedef struct
 {
-#ifdef ROTSPRITE
 	spriteframepivot_t pivot[64];
-#endif
 	boolean available;
 } spriteinfo_t;
 
-extern spriteinfo_t spriteinfo[NUMSPRITES];
-
-// patches, flats, textures...
+// Conversions between patches / flats / textures...
 boolean R_CheckIfPatch(lumpnum_t lump);
-boolean R_IsLumpPNG(const UINT8 *d, size_t s);
-
 void R_TextureToFlat(size_t tex, UINT8 *flat);
 void R_PatchToFlat(patch_t *patch, UINT8 *flat);
 void R_PatchToFlat_16bpp(patch_t *patch, UINT16 *raw, boolean flip);
 patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency);
 patch_t *R_FlatToPatch_16bpp(UINT16 *raw, UINT16 width, UINT16 height, size_t *size);
 
-// png
+// Portable Network Graphics
+boolean R_IsLumpPNG(const UINT8 *d, size_t s);
+#define W_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic
+
 #ifndef NO_PNG_LUMPS
 UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size);
 patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean transparency);
 boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size);
 #endif
 
-// spriteinfo
+// SpriteInfo
+extern spriteinfo_t spriteinfo[NUMSPRITES];
 void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps);
 void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum);
 
-// rotsprite
+// Sprite rotation
 #ifdef ROTSPRITE
+INT32 R_GetRollAngle(angle_t rollangle);
 void R_CacheRotSprite(spritenum_t sprnum, UINT8 frame, spriteinfo_t *sprinfo, spriteframe_t *sprframe, INT32 rot, UINT8 flip);
 void R_FreeSingleRotSprite(spritedef_t *spritedef);
 void R_FreeSkinRotSprite(size_t skinnum);
-extern fixed_t cosang2rad[ROTANGLES];
-extern fixed_t sinang2rad[ROTANGLES];
+extern fixed_t rollcosang[ROTANGLES];
+extern fixed_t rollsinang[ROTANGLES];
+void R_FreeAllRotSprite(void);
 #endif
 
 #endif // __R_PATCH__
diff --git a/src/r_plane.c b/src/r_plane.c
index f21f49101266d1bd044d2eceedbfb31f19bcdf61..5d5e1f20df6899bddabe214991567c8681cb41e4 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -116,6 +116,35 @@ void R_InitPlanes(void)
 	// FIXME: unused
 }
 
+//
+// Water ripple effect!!
+// Needs the height of the plane, and the vertical position of the span.
+// Sets ripple_xfrac and ripple_yfrac, added to ds_xfrac and ds_yfrac, if the span is not tilted.
+//
+
+#ifndef NOWATER
+INT32 ds_bgofs;
+INT32 ds_waterofs;
+
+static INT32 wtofs=0;
+static boolean itswater;
+static fixed_t ripple_xfrac;
+static fixed_t ripple_yfrac;
+
+static void R_PlaneRipple(visplane_t *plane, INT32 y, fixed_t plheight)
+{
+	fixed_t distance = FixedMul(plheight, yslope[y]);
+	const INT32 yay = (wtofs + (distance>>9) ) & 8191;
+	// ripples da water texture
+	angle_t angle = (plane->viewangle + plane->plangle)>>ANGLETOFINESHIFT;
+	ds_bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS;
+
+	angle = (angle + 2048) & 8191;  // 90 degrees
+	ripple_xfrac = FixedMul(FINECOSINE(angle), (ds_bgofs<<FRACBITS));
+	ripple_yfrac = FixedMul(FINESINE(angle), (ds_bgofs<<FRACBITS));
+}
+#endif
+
 //
 // R_MapPlane
 //
@@ -129,14 +158,6 @@ void R_InitPlanes(void)
 //  viewcos
 //  viewheight
 
-#ifndef NOWATER
-INT32 ds_bgofs;
-INT32 ds_waterofs;
-
-static INT32 wtofs=0;
-static boolean itswater;
-#endif
-
 void R_MapPlane(INT32 y, INT32 x1, INT32 x2)
 {
 	angle_t angle, planecos, planesin;
@@ -181,14 +202,22 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2)
 #ifndef NOWATER
 	if (itswater)
 	{
-		const INT32 yay = (wtofs + (distance>>9) ) & 8191;
-		// ripples da water texture
-		ds_bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS;
-		angle = (currentplane->viewangle + currentplane->plangle + xtoviewangle[x1])>>ANGLETOFINESHIFT;
+		// Needed for ds_bgofs
+		R_PlaneRipple(currentplane, y, planeheight);
 
-		angle = (angle + 2048) & 8191;  // 90 degrees
-		ds_xfrac += FixedMul(FINECOSINE(angle), (ds_bgofs<<FRACBITS));
-		ds_yfrac += FixedMul(FINESINE(angle), (ds_bgofs<<FRACBITS));
+#ifdef ESLOPE
+		if (currentplane->slope)
+		{
+			ds_sup = &ds_su[y];
+			ds_svp = &ds_sv[y];
+			ds_szp = &ds_sz[y];
+		}
+		else
+#endif
+		{
+			ds_xfrac += ripple_xfrac;
+			ds_yfrac += ripple_yfrac;
+		}
 
 		if (y+ds_bgofs>=viewheight)
 			ds_bgofs = viewheight-y-1;
@@ -587,8 +616,7 @@ void R_DrawPlanes(void)
 
 	// Note: are these two lines really needed?
 	// R_DrawSinglePlane and R_DrawSkyPlane do span/column drawer resets themselves anyway
-	spanfunc = basespanfunc;
-	wallcolfunc = walldrawerfunc;
+	spanfunc = spanfuncs[BASEDRAWFUNC];
 
 	for (i = 0; i < MAXVISPLANES; i++, pl++)
 	{
@@ -622,7 +650,7 @@ static void R_DrawSkyPlane(visplane_t *pl)
 
 	// Reset column drawer function (note: couldn't we just call walldrawerfunc directly?)
 	// (that is, unless we'll need to switch drawers in future for some reason)
-	wallcolfunc = walldrawerfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 
 	// use correct aspect ratio scale
 	dc_iscale = skyscale;
@@ -648,7 +676,7 @@ static void R_DrawSkyPlane(visplane_t *pl)
 			dc_source =
 				R_GetColumn(texturetranslation[skytexture],
 					-angle); // get negative of angle for each column to display sky correct way round! --Monster Iestyn 27/01/18
-			wallcolfunc();
+			colfunc();
 		}
 	}
 }
@@ -760,6 +788,8 @@ static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boo
 	patch_t *patch = NULL;
 	boolean texturechanged = (leveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false);
 
+	(void)ispng;
+
 	// Check if the texture changed.
 	if (leveltexture && (!texturechanged))
 	{
@@ -833,6 +863,99 @@ static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boo
 	return flat;
 }
 
+#ifdef ESLOPE
+static void R_SlopeVectors(visplane_t *pl, INT32 i, float fudge)
+{
+	// 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 p, m, n;
+	float ang;
+	float vx, vy, vz;
+	// compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly
+	// use this as a temp var to store P_GetZAt's return value each time
+	fixed_t temp;
+
+	vx = FIXED_TO_FLOAT(pl->viewx+xoffs);
+	vy = FIXED_TO_FLOAT(pl->viewy-yoffs);
+	vz = FIXED_TO_FLOAT(pl->viewz);
+
+	temp = P_GetZAt(pl->slope, pl->viewx, pl->viewy);
+	zeroheight = FIXED_TO_FLOAT(temp);
+
+	// p 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.
+	ang = ANG2RAD(ANGLE_270 - pl->viewangle);
+	p.x = vx * cos(ang) - vy * sin(ang);
+	p.z = vx * sin(ang) + vy * cos(ang);
+	temp = P_GetZAt(pl->slope, -xoffs, yoffs);
+	p.y = FIXED_TO_FLOAT(temp) - vz;
+
+	// m is the v direction vector in view space
+	ang = ANG2RAD(ANGLE_180 - (pl->viewangle + pl->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);
+
+	ang = ANG2RAD(pl->plangle);
+	temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(sin(ang)), pl->viewy + FLOAT_TO_FIXED(cos(ang)));
+	m.y = FIXED_TO_FLOAT(temp) - zeroheight;
+	temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(cos(ang)), pl->viewy - FLOAT_TO_FIXED(sin(ang)));
+	n.y = FIXED_TO_FLOAT(temp) - zeroheight;
+
+	if (ds_powersoftwo)
+	{
+		m.x /= fudge;
+		m.y /= fudge;
+		m.z /= fudge;
+
+		n.x *= fudge;
+		n.y *= fudge;
+		n.z *= fudge;
+	}
+
+	// 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_su[i], p, m);
+	CROSS(ds_sv[i], p, n);
+	CROSS(ds_sz[i], m, n);
+#undef CROSS
+
+	ds_su[i].z *= focallengthf;
+	ds_sv[i].z *= focallengthf;
+	ds_sz[i].z *= focallengthf;
+
+	// Premultiply the texture vectors with the scale factors
+#define SFMULT 65536.f
+	if (ds_powersoftwo)
+	{
+		ds_su[i].x *= (SFMULT * (1<<nflatshiftup));
+		ds_su[i].y *= (SFMULT * (1<<nflatshiftup));
+		ds_su[i].z *= (SFMULT * (1<<nflatshiftup));
+		ds_sv[i].x *= (SFMULT * (1<<nflatshiftup));
+		ds_sv[i].y *= (SFMULT * (1<<nflatshiftup));
+		ds_sv[i].z *= (SFMULT * (1<<nflatshiftup));
+	}
+	else
+	{
+		// Lactozilla: I'm essentially multiplying the vectors by FRACUNIT...
+		ds_su[i].x *= SFMULT;
+		ds_su[i].y *= SFMULT;
+		ds_su[i].z *= SFMULT;
+		ds_sv[i].x *= SFMULT;
+		ds_sv[i].y *= SFMULT;
+		ds_sv[i].z *= SFMULT;
+	}
+#undef SFMULT
+}
+#endif // ESLOPE
+
 void R_DrawSinglePlane(visplane_t *pl)
 {
 	UINT8 *flat;
@@ -842,6 +965,7 @@ void R_DrawSinglePlane(visplane_t *pl)
 	ffloor_t *rover;
 	levelflat_t *levelflat;
 	int type;
+	int spanfunctype = BASEDRAWFUNC;
 
 	if (!(pl->minx <= pl->maxx))
 		return;
@@ -856,11 +980,12 @@ void R_DrawSinglePlane(visplane_t *pl)
 #ifndef NOWATER
 	itswater = false;
 #endif
-	spanfunc = basespanfunc;
+	spanfunc = spanfuncs[BASEDRAWFUNC];
 
 #ifdef POLYOBJECTS_PLANES
-	if (pl->polyobj && pl->polyobj->translucency != 0) {
-		spanfunc = R_DrawTranslucentSpan_8;
+	if (pl->polyobj && pl->polyobj->translucency != 0)
+	{
+		spanfunctype = SPANDRAWFUNC_TRANS;
 
 		// Hacked up support for alpha value in software mode Tails 09-24-2002 (sidenote: ported to polys 10-15-2014, there was no time travel involved -Red)
 		if (pl->polyobj->translucency >= 10)
@@ -868,10 +993,10 @@ void R_DrawSinglePlane(visplane_t *pl)
 		else if (pl->polyobj->translucency > 0)
 			ds_transmap = transtables + ((pl->polyobj->translucency-1)<<FF_TRANSSHIFT);
 		else // Opaque, but allow transparent flat pixels
-			spanfunc = splatfunc;
+			spanfunctype = SPANDRAWFUNC_SPLAT;
 
 #ifdef SHITPLANESPARENCY
-		if ((spanfunc == splatfunc) != (pl->extra_colormap && (pl->extra_colormap->fog & 4)))
+		if ((spanfunctype == SPANDRAWFUNC_SPLAT) != (pl->extra_colormap && (pl->extra_colormap->fog & 4)))
 #else
 		if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
 #endif
@@ -902,7 +1027,7 @@ void R_DrawSinglePlane(visplane_t *pl)
 
 		if (pl->ffloor->flags & FF_TRANSLUCENT)
 		{
-			spanfunc = R_DrawTranslucentSpan_8;
+			spanfunctype = SPANDRAWFUNC_TRANS;
 
 			// Hacked up support for alpha value in software mode Tails 09-24-2002
 			if (pl->ffloor->alpha < 12)
@@ -926,10 +1051,10 @@ void R_DrawSinglePlane(visplane_t *pl)
 			else if (pl->ffloor->alpha < 243)
 				ds_transmap = transtables + ((tr_trans10-1)<<FF_TRANSSHIFT);
 			else // Opaque, but allow transparent flat pixels
-				spanfunc = splatfunc;
+				spanfunctype = SPANDRAWFUNC_SPLAT;
 
 #ifdef SHITPLANESPARENCY
-			if ((spanfunc == splatfunc) != (pl->extra_colormap && (pl->extra_colormap->fog & 4)))
+			if ((spanfunctype == SPANDRAWFUNC_SPLAT) != (pl->extra_colormap && (pl->extra_colormap->fog & 4)))
 #else
 			if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
 #endif
@@ -939,24 +1064,20 @@ void R_DrawSinglePlane(visplane_t *pl)
 		}
 		else if (pl->ffloor->flags & FF_FOG)
 		{
-			spanfunc = R_DrawFogSpan_8;
+			spanfunctype = SPANDRAWFUNC_FOG;
 			light = (pl->lightlevel >> LIGHTSEGSHIFT);
 		}
 		else light = (pl->lightlevel >> LIGHTSEGSHIFT);
 
 #ifndef NOWATER
-		if (pl->ffloor->flags & FF_RIPPLE
-#ifdef ESLOPE
-				&& !pl->slope
-#endif
-			)
+		if (pl->ffloor->flags & FF_RIPPLE)
 		{
 			INT32 top, bottom;
 
 			itswater = true;
-			if (spanfunc == R_DrawTranslucentSpan_8)
+			if (spanfunctype == SPANDRAWFUNC_TRANS)
 			{
-				spanfunc = R_DrawTranslucentWaterSpan_8;
+				spanfunctype = SPANDRAWFUNC_WATER;
 
 				// Copy the current scene, ugh
 				top = pl->high-8;
@@ -1007,8 +1128,6 @@ void R_DrawSinglePlane(visplane_t *pl)
 			R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum));
 			// Raw flats always have dimensions that are powers-of-two numbers.
 			ds_powersoftwo = true;
-			if (spanfunc == basespanfunc)
-				spanfunc = mmxspanfunc;
 			break;
 		default:
 			switch (type)
@@ -1031,11 +1150,7 @@ void R_DrawSinglePlane(visplane_t *pl)
 			}
 			// Check if this texture or patch has power-of-two dimensions.
 			if (R_CheckPowersOfTwo())
-			{
 				R_CheckFlatLength(ds_flatwidth * ds_flatheight);
-				if (spanfunc == basespanfunc)
-					spanfunc = mmxspanfunc;
-			}
 	}
 
 	if (light >= LIGHTLEVELS)
@@ -1045,25 +1160,18 @@ void R_DrawSinglePlane(visplane_t *pl)
 		light = 0;
 
 #ifdef ESLOPE
-	if (pl->slope) {
-		// 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 p, m, n;
-		float ang;
-		float vx, vy, vz;
-		float fudge = 0;
-		// compiler complains when P_GetZAt is used in FLOAT_TO_FIXED directly
-		// use this as a temp var to store P_GetZAt's return value each time
-		fixed_t temp;
-
+	if (pl->slope)
+	{
+		float fudgecanyon = 0;
 		angle_t hack = (pl->plangle & (ANGLE_90-1));
 
 		yoffs *= 1;
 
 		if (ds_powersoftwo)
 		{
+			fixed_t temp;
 			// Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red
-			fudge = ((1<<nflatshiftup)+1.0f)/(1<<nflatshiftup);
+			fudgecanyon = ((1<<nflatshiftup)+1.0f)/(1<<nflatshiftup);
 			if (hack)
 			{
 				/*
@@ -1105,95 +1213,50 @@ void R_DrawSinglePlane(visplane_t *pl)
 				xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1);
 				yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1);
 			}
-			xoffs = (fixed_t)(xoffs*fudge);
-			yoffs = (fixed_t)(yoffs/fudge);
+			xoffs = (fixed_t)(xoffs*fudgecanyon);
+			yoffs = (fixed_t)(yoffs/fudgecanyon);
 		}
 
-		vx = FIXED_TO_FLOAT(pl->viewx+xoffs);
-		vy = FIXED_TO_FLOAT(pl->viewy-yoffs);
-		vz = FIXED_TO_FLOAT(pl->viewz);
-
-		temp = P_GetZAt(pl->slope, pl->viewx, pl->viewy);
-		zeroheight = FIXED_TO_FLOAT(temp);
-
-		// p 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.
-		ang = ANG2RAD(ANGLE_270 - pl->viewangle);
-		p.x = vx * cos(ang) - vy * sin(ang);
-		p.z = vx * sin(ang) + vy * cos(ang);
-		temp = P_GetZAt(pl->slope, -xoffs, yoffs);
-		p.y = FIXED_TO_FLOAT(temp) - vz;
-
-		// m is the v direction vector in view space
-		ang = ANG2RAD(ANGLE_180 - (pl->viewangle + pl->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);
-
-		ang = ANG2RAD(pl->plangle);
-		temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(sin(ang)), pl->viewy + FLOAT_TO_FIXED(cos(ang)));
-		m.y = FIXED_TO_FLOAT(temp) - zeroheight;
-		temp = P_GetZAt(pl->slope, pl->viewx + FLOAT_TO_FIXED(cos(ang)), pl->viewy - FLOAT_TO_FIXED(sin(ang)));
-		n.y = FIXED_TO_FLOAT(temp) - zeroheight;
+		ds_sup = &ds_su[0];
+		ds_svp = &ds_sv[0];
+		ds_szp = &ds_sz[0];
 
-		if (ds_powersoftwo)
+#ifndef NOWATER
+		if (itswater)
 		{
-			m.x /= fudge;
-			m.y /= fudge;
-			m.z /= fudge;
-
-			n.x *= fudge;
-			n.y *= fudge;
-			n.z *= fudge;
-		}
+			INT32 i;
+			fixed_t plheight = abs(P_GetZAt(pl->slope, pl->viewx, pl->viewy) - pl->viewz);
+			fixed_t rxoffs = xoffs;
+			fixed_t ryoffs = yoffs;
 
-		// 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_su, p, m);
-		CROSS(ds_sv, p, n);
-		CROSS(ds_sz, m, n);
-#undef CROSS
+			R_PlaneBounds(pl);
 
-		ds_su.z *= focallengthf;
-		ds_sv.z *= focallengthf;
-		ds_sz.z *= focallengthf;
+			for (i = pl->high; i < pl->low; i++)
+			{
+				R_PlaneRipple(pl, i, plheight);
+				xoffs = rxoffs + ripple_xfrac;
+				yoffs = ryoffs + ripple_yfrac;
+				R_SlopeVectors(pl, i, fudgecanyon);
+			}
 
-		// Premultiply the texture vectors with the scale factors
-#define SFMULT 65536.f
-		if (ds_powersoftwo)
-		{
-			ds_su.x *= (SFMULT * (1<<nflatshiftup));
-			ds_su.y *= (SFMULT * (1<<nflatshiftup));
-			ds_su.z *= (SFMULT * (1<<nflatshiftup));
-			ds_sv.x *= (SFMULT * (1<<nflatshiftup));
-			ds_sv.y *= (SFMULT * (1<<nflatshiftup));
-			ds_sv.z *= (SFMULT * (1<<nflatshiftup));
+			xoffs = rxoffs;
+			yoffs = ryoffs;
 		}
 		else
-		{
-			// I'm essentially multiplying the vectors by FRACUNIT...
-			ds_su.x *= SFMULT;
-			ds_su.y *= SFMULT;
-			ds_su.z *= SFMULT;
-			ds_sv.x *= SFMULT;
-			ds_sv.y *= SFMULT;
-			ds_sv.z *= SFMULT;
-		}
-#undef SFMULT
+#endif
+			R_SlopeVectors(pl, 0, fudgecanyon);
 
-		if (spanfunc == R_DrawTranslucentSpan_8)
-			spanfunc = R_DrawTiltedTranslucentSpan_8;
-		else if (spanfunc == splatfunc)
-			spanfunc = R_DrawTiltedSplat_8;
+#ifndef NOWATER
+		if (itswater && (spanfunctype == SPANDRAWFUNC_WATER))
+			spanfunctype = SPANDRAWFUNC_TILTEDWATER;
+		else
+#endif
+		if (spanfunctype == SPANDRAWFUNC_TRANS)
+			spanfunctype = SPANDRAWFUNC_TILTEDTRANS;
+		else if (spanfunctype == SPANDRAWFUNC_SPLAT)
+			spanfunctype = SPANDRAWFUNC_TILTEDSPLAT;
 		else
-			spanfunc = R_DrawTiltedSpan_8;
+			spanfunctype = SPANDRAWFUNC_TILTED;
 
 		planezlight = scalelight[light];
 	} else
@@ -1201,6 +1264,17 @@ void R_DrawSinglePlane(visplane_t *pl)
 
 	planezlight = zlight[light];
 
+	// Use the correct span drawer depending on the powers-of-twoness
+	if (!ds_powersoftwo)
+	{
+		if (spanfuncs_npo2[spanfunctype])
+			spanfunc = spanfuncs_npo2[spanfunctype];
+		else
+			spanfunc = spanfuncs[spanfunctype];
+	}
+	else
+		spanfunc = spanfuncs[spanfunctype];
+
 	// set the maximum value for unsigned
 	pl->top[pl->maxx+1] = 0xffff;
 	pl->top[pl->minx-1] = 0xffff;
@@ -1240,11 +1314,11 @@ a 'smoothing' of the texture while
 using the palette colors.
 */
 #ifdef QUINCUNX
-	if (spanfunc == R_DrawSpan_8)
+	if (spanfunc == spanfuncs[BASEDRAWFUNC])
 	{
 		INT32 i;
 		ds_transmap = transtables + ((tr_trans50-1)<<FF_TRANSSHIFT);
-		spanfunc = R_DrawTranslucentSpan_8;
+		spanfunc = spanfuncs[SPANDRAWFUNC_TRANS];
 		for (i=0; i<4; i++)
 		{
 			xoffs = pl->xoffs;
diff --git a/src/r_plane.h b/src/r_plane.h
index 11cbb56f1bcbffb94019630eaff2811f90c30e3a..d9ba5c56bfd9883a080c8bdbb9898bd656d2ffd6 100644
--- a/src/r_plane.h
+++ b/src/r_plane.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_portal.c b/src/r_portal.c
index ea24cd91c49b420df663e847cdbebb651a3f36db..576b606ec06d7e44a2c5506c111b495880e35ca5 100644
--- a/src/r_portal.c
+++ b/src/r_portal.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -26,6 +26,7 @@ UINT8 portalrender;			/**< When rendering a portal, it establishes the depth of
 portal_t *portal_base, *portal_cap;
 
 line_t *portalclipline;
+sector_t *portalcullsector;
 INT32 portalclipstart, portalclipend;
 
 boolean portalline; // is curline a portal seg?
diff --git a/src/r_portal.h b/src/r_portal.h
index e8f9119e84a6eddc41e2bf651cf1ac408772bc2d..c46ddfdab77b5a7e89d36fe8a5b46f8784e1866a 100644
--- a/src/r_portal.h
+++ b/src/r_portal.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -45,6 +45,7 @@ extern portal_t* portal_cap;
 extern UINT8 portalrender;
 
 extern line_t *portalclipline;
+extern sector_t *portalcullsector;
 extern INT32 portalclipstart, portalclipend;
 
 void Portal_InitList	(void);
diff --git a/src/r_segs.c b/src/r_segs.c
index ee62bfc739faf207ade72cda308433522621592a..dcb5fc160a89d534c9013f1816f8bdcc1c4f4856 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -165,7 +165,7 @@ static void R_DrawWallSplats(void)
 		mfloorclip = floorclip;
 		mceilingclip = ceilingclip;
 
-		patch = W_CachePatchNum(splat->patch, PU_CACHE);
+		patch = W_CachePatchNum(splat->patch, PU_PATCH);
 
 		dc_texturemid = splat->top + (SHORT(patch->height)<<(FRACBITS-1)) - viewz;
 		if (splat->yoffset)
@@ -177,20 +177,20 @@ static void R_DrawWallSplats(void)
 		switch (splat->flags & SPLATDRAWMODE_MASK)
 		{
 			case SPLATDRAWMODE_OPAQUE:
-				colfunc = basecolfunc;
+				colfunc = colfuncs[BASEDRAWFUNC];
 				break;
 			case SPLATDRAWMODE_TRANS:
 				if (!cv_translucency.value)
-					colfunc = basecolfunc;
+					colfunc = colfuncs[BASEDRAWFUNC];
 				else
 				{
 					dc_transmap = transtables + ((tr_trans50 - 1)<<FF_TRANSSHIFT);
-					colfunc = fuzzcolfunc;
+					colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 				}
 
 				break;
 			case SPLATDRAWMODE_SHADE:
-				colfunc = shadecolfunc;
+				colfunc = colfuncs[COLDRAWFUNC_SHADE];
 				break;
 		}
 
@@ -226,7 +226,7 @@ static void R_DrawWallSplats(void)
 		}
 	} // next splat
 
-	colfunc = basecolfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 }
 
 #endif //WALLSPLATS
@@ -270,10 +270,10 @@ static void R_Render2sidedMultiPatchColumn(column_t *column)
 	{
 		dc_source = (UINT8 *)column + 3;
 
-		if (colfunc == wallcolfunc)
-			twosmultipatchfunc();
-		else if (colfunc == fuzzcolfunc)
-			twosmultipatchtransfunc();
+		if (colfunc == colfuncs[BASEDRAWFUNC])
+			(colfuncs[COLDRAWFUNC_TWOSMULTIPATCH])();
+		else if (colfunc == colfuncs[COLDRAWFUNC_FUZZY])
+			(colfuncs[COLDRAWFUNC_TWOSMULTIPATCHTRANS])();
 		else
 			colfunc();
 	}
@@ -308,6 +308,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	//   for horizontal / vertical / diagonal. Diagonal?
 	// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
 	curline = ds->curline;
+
 	frontsector = curline->frontsector;
 	backsector = curline->backsector;
 	texnum = R_GetTextureNum(curline->sidedef->midtexture);
@@ -327,15 +328,15 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 		case 907:
 		case 908:
 			dc_transmap = transtables + ((ldef->special-900)<<FF_TRANSSHIFT);
-			colfunc = fuzzcolfunc;
+			colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 			break;
 		case 909:
-			colfunc = R_DrawFogColumn_8;
+			colfunc = colfuncs[COLDRAWFUNC_FOG];
 			windowtop = frontsector->ceilingheight;
 			windowbottom = frontsector->floorheight;
 			break;
 		default:
-			colfunc = wallcolfunc;
+			colfunc = colfuncs[BASEDRAWFUNC];
 			break;
 	}
 
@@ -345,7 +346,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 			return;
 
 		dc_transmap = transtables + ((curline->polyseg->translucency-1)<<FF_TRANSSHIFT);
-		colfunc = fuzzcolfunc;
+		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 	}
 
 #ifdef ESLOPE
@@ -419,7 +420,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 
 			if (rlight->flags & FF_FOG || (rlight->extra_colormap && rlight->extra_colormap->fog))
 				lightnum = (rlight->lightlevel >> LIGHTSEGSHIFT);
-			else if (colfunc == fuzzcolfunc)
+			else if (colfunc == colfuncs[COLDRAWFUNC_FUZZY])
 				lightnum = LIGHTLEVELS - 1;
 			else
 				lightnum = (rlight->lightlevel >> LIGHTSEGSHIFT);
@@ -436,7 +437,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	}
 	else
 	{
-		if (colfunc == fuzzcolfunc)
+		if (colfunc == colfuncs[COLDRAWFUNC_FUZZY])
 		{
 			if (frontsector->extra_colormap && frontsector->extra_colormap->fog)
 				lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT);
@@ -446,7 +447,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 		else
 			lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT);
 
-		if (colfunc == R_DrawFogColumn_8
+		if (colfunc == colfuncs[COLDRAWFUNC_FOG]
 			|| (frontsector->extra_colormap && frontsector->extra_colormap->fog))
 			;
 		else if (curline->v1->y == curline->v2->y)
@@ -718,7 +719,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 			spryscale += rw_scalestep;
 		}
 	}
-	colfunc = wallcolfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 }
 
 // Loop through R_DrawMaskedColumn calls
@@ -789,7 +790,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	frontsector = curline->frontsector == pfloor->target ? curline->backsector : curline->frontsector;
 	texnum = R_GetTextureNum(sides[pfloor->master->sidenum[0]].midtexture);
 
-	colfunc = wallcolfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 
 	if (pfloor->master->flags & ML_TFERLINE)
 	{
@@ -827,10 +828,10 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 			fuzzy = false; // Opaque
 
 		if (fuzzy)
-			colfunc = fuzzcolfunc;
+			colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 	}
 	else if (pfloor->flags & FF_FOG)
-		colfunc = R_DrawFogColumn_8;
+		colfunc = colfuncs[COLDRAWFUNC_FOG];
 
 #ifdef ESLOPE
 	range = max(ds->x2-ds->x1, 1);
@@ -965,7 +966,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 			lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT);
 		else if (pfloor->flags & FF_FOG)
 			lightnum = (pfloor->master->frontsector->lightlevel >> LIGHTSEGSHIFT);
-		else if (colfunc == fuzzcolfunc)
+		else if (colfunc == colfuncs[COLDRAWFUNC_FUZZY])
 			lightnum = LIGHTLEVELS-1;
 		else
 			lightnum = R_FakeFlat(frontsector, &tempsec, &templight, &templight, false)
@@ -1297,7 +1298,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 			spryscale += rw_scalestep;
 		}
 	}
-	colfunc = wallcolfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 
 #undef CLAMPMAX
 #undef CLAMPMIN
@@ -1530,7 +1531,7 @@ static void R_RenderSegLoop (void)
 				else
 					dc_lightlist[i].rcolormap = xwalllights[pindex];
 
-				colfunc = R_DrawColumnShadowed_8;
+				colfunc = colfuncs[COLDRAWFUNC_SHADOWED];
 			}
 		}
 
@@ -1736,6 +1737,8 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	memset(&segright, 0x00, sizeof(segright));
 #endif
 
+	colfunc = colfuncs[BASEDRAWFUNC];
+
 	if (ds_p == drawsegs+maxdrawsegs)
 	{
 		size_t curpos = curdrawsegs - drawsegs;
@@ -3186,7 +3189,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	else
 #endif
 		R_RenderSegLoop();
-	colfunc = wallcolfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 
 	if (portalline) // if curline is a portal, set portalrender for drawseg
 		ds_p->portalpass = portalrender+1;
diff --git a/src/r_segs.h b/src/r_segs.h
index 92d0100e2ee49765bfe4e733257d72b5122d6bc3..1c852ddc7c4b931b0105f646f247587bd93a1dad 100644
--- a/src/r_segs.h
+++ b/src/r_segs.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_sky.c b/src/r_sky.c
index 817e8368a722b2cd81a2328a4b11f3e1ff3b77dd..c9d28cebc00fff2b51f7a19c1cd984113ad37de6 100644
--- a/src/r_sky.c
+++ b/src/r_sky.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_sky.h b/src/r_sky.h
index 86b615595d22506b99a582e683c4a3f87290f902..8eca2077ab8a12b82ff82f7211bb320c031b0279 100644
--- a/src/r_sky.h
+++ b/src/r_sky.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_splats.c b/src/r_splats.c
index 9ab671274403f1f7dfea967271f286d8aafef1cd..f690dfa9417a4dff9a45d5605c719d6a9860208c 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -147,7 +147,7 @@ void R_AddWallSplat(line_t *wallline, INT16 sectorside, const char *patchname, f
 	splat->flags = flags;
 
 	// bad.. but will be needed for drawing anyway..
-	patch = W_CachePatchNum(splat->patch, PU_CACHE);
+	patch = W_CachePatchNum(splat->patch, PU_PATCH);
 
 	// offset needed by draw code for texture mapping
 	linelength = P_SegLength((seg_t *)wallline);
@@ -512,7 +512,7 @@ static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTe
 			ds_x1 = x1;
 			ds_x2 = x2;
 			ds_transmap = transtables + ((tr_trans50-1)<<FF_TRANSSHIFT);
-			splatfunc();
+			(spanfuncs[SPANDRAWFUNC_SPLAT])();
 		}
 
 		// reset for next calls to edge rasterizer
diff --git a/src/r_splats.h b/src/r_splats.h
index 387b2958295eaa8f09945c0a2d05b79377627192..7565b4bcc22955449ebc6d095c577783d649d5f9 100644
--- a/src/r_splats.h
+++ b/src/r_splats.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/r_state.h b/src/r_state.h
index 9773849265ef77a17f4fd6712078388c2180aa46..4e1eb388e7d0a05d46a9c5ee80970c7b2545d2c9 100644
--- a/src/r_state.h
+++ b/src/r_state.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -63,6 +63,7 @@ extern seg_t *segs;
 
 extern size_t numsectors;
 extern sector_t *sectors;
+extern sector_t *spawnsectors;
 
 extern size_t numsubsectors;
 extern subsector_t *subsectors;
@@ -72,9 +73,11 @@ extern node_t *nodes;
 
 extern size_t numlines;
 extern line_t *lines;
+extern line_t *spawnlines;
 
 extern size_t numsides;
 extern side_t *sides;
+extern side_t *spawnsides;
 
 //
 // POV data.
diff --git a/src/r_things.c b/src/r_things.c
index c89d87bf4529a6c41381ff4d5d8b4834702e9144..927d199a54b361bc836ec4e9bc15e886010bf1d0 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -126,11 +126,10 @@ static void R_InstallSpriteLump(UINT16 wad,            // graphics patch
 		for (ang = 0; ang < ROTANGLES; ang++)
 			sprtemp[frame].rotsprite.patch[r][ang] = NULL;
 #ifdef HWRENDER
-		if (rendermode == render_opengl)
-			sprtemp[frame].rotsprite.hardware_patch[r] = M_AATreeAlloc(AATREE_ZUSER);
-#endif // HWRENDER
+		sprtemp[frame].rotsprite.hardware_patch[r] = M_AATreeAlloc(AATREE_ZUSER);
+#endif/*HWRENDER*/
 	}
-#endif
+#endif/*ROTSPRITE*/
 
 	if (rotation == 0)
 	{
@@ -451,7 +450,7 @@ void R_AddSpriteDefs(UINT16 wadnum)
 	else
 		start++;   // just after S_START
 
-	if (end == INT16_MAX)
+	if (end == INT16_MAX || start >= end)
 	{
 		CONS_Debug(DBG_SETUP, "no sprites in pwad %d\n", wadnum);
 		return;
@@ -500,7 +499,7 @@ void R_InitSprites(void)
 {
 	size_t i;
 #ifdef ROTSPRITE
-	INT32 angle, realangle = 0;
+	INT32 angle;
 	float fa;
 #endif
 
@@ -508,12 +507,11 @@ void R_InitSprites(void)
 		negonearray[i] = -1;
 
 #ifdef ROTSPRITE
-	for (angle = 0; angle < ROTANGLES; angle++)
+	for (angle = 1; angle < ROTANGLES; angle++)
 	{
-		fa = ANG2RAD(FixedAngle(realangle<<FRACBITS));
-		cosang2rad[angle] = FLOAT_TO_FIXED(cos(-fa));
-		sinang2rad[angle] = FLOAT_TO_FIXED(sin(-fa));
-		realangle += ROTANGDIFF;
+		fa = ANG2RAD(FixedAngle((ROTANGDIFF * angle)<<FRACBITS));
+		rollcosang[angle] = FLOAT_TO_FIXED(cos(-fa));
+		rollsinang[angle] = FLOAT_TO_FIXED(sin(-fa));
 	}
 #endif
 
@@ -653,11 +651,7 @@ void R_DrawMaskedColumn(column_t *column)
 			// quick fix... something more proper should be done!!!
 			if (ylookup[dc_yl])
 				colfunc();
-			else if (colfunc == R_DrawColumn_8
-#ifdef USEASM
-			|| colfunc == R_DrawColumn_8_ASM || colfunc == R_DrawColumn_8_MMX
-#endif
-			)
+			else if (colfunc == colfuncs[COLDRAWFUNC_BASE])
 			{
 				static INT32 first = 1;
 				if (first)
@@ -724,11 +718,7 @@ void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
 			// Still drawn by R_DrawColumn.
 			if (ylookup[dc_yl])
 				colfunc();
-			else if (colfunc == R_DrawColumn_8
-#ifdef USEASM
-			|| colfunc == R_DrawColumn_8_ASM || colfunc == R_DrawColumn_8_MMX
-#endif
-			)
+			else if (colfunc == colfuncs[COLDRAWFUNC_BASE])
 			{
 				static INT32 first = 1;
 				if (first)
@@ -776,12 +766,12 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // ditto
 	}
 
-	colfunc = basecolfunc; // hack: this isn't resetting properly somewhere.
+	colfunc = colfuncs[BASEDRAWFUNC]; // hack: this isn't resetting properly somewhere.
 	dc_colormap = vis->colormap;
 	if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & (MF_ENEMY|MF_BOSS)) && (vis->mobj->flags2 & MF2_FRET) && !(vis->mobj->flags & MF_GRENADEBOUNCE) && (leveltime & 1)) // Bosses "flash"
 	{
 		// translate certain pixels to white
-		colfunc = transcolfunc;
+		colfunc = colfuncs[COLDRAWFUNC_TRANS];
 		if (vis->mobj->type == MT_CYBRAKDEMON || vis->mobj->colorized)
 			dc_translation = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
 		else if (vis->mobj->type == MT_METALSONIC_BATTLE)
@@ -791,7 +781,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	}
 	else if (vis->mobj->color && vis->transmap) // Color mapping
 	{
-		colfunc = transtransfunc;
+		colfunc = colfuncs[COLDRAWFUNC_TRANSTRANS];
 		dc_transmap = vis->transmap;
 		if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
 			dc_translation = R_GetTranslationColormap(TC_RAINBOW, vis->mobj->color, GTC_CACHE);
@@ -815,13 +805,13 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	}
 	else if (vis->transmap)
 	{
-		colfunc = fuzzcolfunc;
+		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 		dc_transmap = vis->transmap;    //Fab : 29-04-98: translucency table
 	}
 	else if (vis->mobj->color)
 	{
 		// translate green skin to another color
-		colfunc = transcolfunc;
+		colfunc = colfuncs[COLDRAWFUNC_TRANS];
 
 		// New colormap stuff for skins Tails 06-07-2002
 		if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
@@ -846,7 +836,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	}
 	else if (vis->mobj->sprite == SPR_PLAY) // Looks like a player, but doesn't have a color? Get rid of green sonic syndrome.
 	{
-		colfunc = transcolfunc;
+		colfunc = colfuncs[COLDRAWFUNC_TRANS];
 		dc_translation = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLUE, GTC_CACHE);
 	}
 
@@ -926,7 +916,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		spryscale += vis->scalestep;
 	}
 
-	colfunc = basecolfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 	dc_hires = 0;
 
 	vis->x1 = x1;
@@ -956,7 +946,7 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
 
 	if (vis->transmap)
 	{
-		colfunc = fuzzcolfunc;
+		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 		dc_transmap = vis->transmap;    //Fab : 29-04-98: translucency table
 	}
 
@@ -992,12 +982,12 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis)
 		R_DrawMaskedColumn(column);
 	}
 
-	colfunc = basecolfunc;
+	colfunc = colfuncs[BASEDRAWFUNC];
 }
 
 //
 // R_SplitSprite
-// runs through a sector's lightlist and
+// runs through a sector's lightlist and Knuckles
 static void R_SplitSprite(vissprite_t *sprite)
 {
 	INT32 i, lightnum, lindex;
@@ -1082,6 +1072,8 @@ static void R_SplitSprite(vissprite_t *sprite)
 	}
 }
 
+//#define PROPERPAPER // This was reverted less than 7 hours before 2.2's release because of very strange, frequent crashes.
+
 //
 // R_ProjectSprite
 // Generates a vissprite for a thing
@@ -1133,11 +1125,12 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t spr_offset, spr_topoffset;
 #ifdef ROTSPRITE
 	patch_t *rotsprite = NULL;
-	angle_t arollangle = thing->rollangle;
-	UINT32 rollangle = AngleFixed(arollangle)>>FRACBITS;
+	INT32 rollangle = 0;
 #endif
 
+#ifndef PROPERPAPER
 	fixed_t ang_scale = FRACUNIT;
+#endif
 
 	// transform the origin point
 	tr_x = thing->x - viewx;
@@ -1223,8 +1216,10 @@ static void R_ProjectSprite(mobj_t *thing)
 	if (sprframe->rotate != SRF_SINGLE || papersprite)
 	{
 		ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle);
+#ifndef PROPERPAPER
 		if (papersprite)
 			ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
+#endif
 	}
 
 	if (sprframe->rotate == SRF_SINGLE)
@@ -1262,11 +1257,11 @@ static void R_ProjectSprite(mobj_t *thing)
 	spr_topoffset = spritecachedinfo[lump].topoffset;
 
 #ifdef ROTSPRITE
-	if (rollangle > 0)
+	if (thing->rollangle)
 	{
+		rollangle = R_GetRollAngle(thing->rollangle);
 		if (!sprframe->rotsprite.cached[rot])
 			R_CacheRotSprite(thing->sprite, (thing->frame & FF_FRAMEMASK), sprinfo, sprframe, rot, flip);
-		rollangle /= ROTANGDIFF;
 		rotsprite = sprframe->rotsprite.patch[rot][rollangle];
 		if (rotsprite != NULL)
 		{
@@ -1286,24 +1281,31 @@ static void R_ProjectSprite(mobj_t *thing)
 	else
 		offset = -spr_offset;
 	offset = FixedMul(offset, this_scale);
+#ifndef PROPERPAPER
 	tx += FixedMul(offset, ang_scale);
 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
 
 	// off the right side?
 	if (x1 > viewwidth)
 		return;
-
+#endif
 	offset2 = FixedMul(spr_width, this_scale);
+#ifndef PROPERPAPER
 	tx += FixedMul(offset2, ang_scale);
 	x2 = ((centerxfrac + FixedMul (tx,xscale)) >> FRACBITS) - (papersprite ? 2 : 1);
 
 	// off the left side
 	if (x2 < 0)
 		return;
+#endif
 
 	if (papersprite)
 	{
-		fixed_t yscale2, cosmul, sinmul, tz2;
+		fixed_t
+#ifdef PROPERPAPER
+			xscale2,
+#endif
+					yscale2, cosmul, sinmul, tz2;
 		INT32 range;
 
 		if (ang >= ANGLE_180)
@@ -1323,6 +1325,18 @@ static void R_ProjectSprite(mobj_t *thing)
 		yscale = FixedDiv(projectiony, tz);
 		if (yscale < 64) return; // Fix some funky visuals
 
+#ifdef PROPERPAPER
+		gxt = -FixedMul(tr_x, viewsin);
+		gyt = FixedMul(tr_y, viewcos);
+		tx = -(gyt + gxt);
+		xscale = FixedDiv(projection, tz);
+		x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
+
+		// off the right side?
+		if (x1 > viewwidth)
+			return;
+#endif
+
 		tr_x += FixedMul(offset2, cosmul);
 		tr_y += FixedMul(offset2, sinmul);
 		gxt = FixedMul(tr_x, viewcos);
@@ -1331,15 +1345,36 @@ static void R_ProjectSprite(mobj_t *thing)
 		yscale2 = FixedDiv(projectiony, tz2);
 		if (yscale2 < 64) return; // ditto
 
+#ifdef PROPERPAPER
+		gxt = -FixedMul(tr_x, viewsin);
+		gyt = FixedMul(tr_y, viewcos);
+		tx = -(gyt + gxt);
+		xscale2 = FixedDiv(projection, tz2);
+		x2 = (centerxfrac + FixedMul(tx,xscale2))>>FRACBITS; x2--;
+
+		// off the left side
+		if (x2 < 0)
+			return;
+#endif
+
 		if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier
 			return;
 
-		if (x2 > x1)
-			range = (x2 - x1);
-		else
-			range = 1;
+		if ((range = x2 - x1) <= 0)
+			return;
+
+#ifdef PROPERPAPER
+		range++; // fencepost problem
+#endif
 
-		scalestep = (yscale2 - yscale)/range ?: 1;
+		scalestep = (yscale2 - yscale)/range;
+		xscale =
+#ifdef PROPERPAPER
+		FixedDiv(range<<FRACBITS, abs(offset2))+1
+#else
+		FixedMul(xscale, ang_scale)
+#endif
+		;
 
 		// The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2?
 		// sortscale = max(yscale, yscale2);
@@ -1349,9 +1384,22 @@ static void R_ProjectSprite(mobj_t *thing)
 	{
 		scalestep = 0;
 		yscale = sortscale;
-	}
+#ifdef PROPERPAPER
+		tx += offset;
+		x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
+
+		// off the right side?
+		if (x1 > viewwidth)
+			return;
 
-	xscale = FixedMul(xscale, ang_scale);
+		tx += offset2;
+		x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
+
+		// off the left side
+		if (x2 < 0)
+			return;
+#endif
+	}
 
 	if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
 	{
@@ -2788,7 +2836,7 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
 {
 	return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0...
 		|| (!skins[skinnum].availability)
-		|| ((playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked))
+		|| (((netgame || multiplayer) && playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked))
 		|| (modeattacking) // If you have someone else's run you might as well take a look
 		|| (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1.
 		|| (netgame && (cv_forceskin.value == skinnum)) // Force 2.
diff --git a/src/r_things.h b/src/r_things.h
index b186a56b598ea29f42f6976d839138e6f402cea8..8e4a543c36067d1101068a86a5d33e779c4cd8cf 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/s_sound.c b/src/s_sound.c
index 8e4e7715d0d37dfcca1b1c774df74c446384cb36..0235d8376ce791b7859ab8997153709a2b336848 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -61,7 +61,9 @@ static void GameMIDIMusic_OnChange(void);
 static void GameSounds_OnChange(void);
 static void GameDigiMusic_OnChange(void);
 
+#ifdef HAVE_OPENMPT
 static void ModFilter_OnChange(void);
+#endif
 
 static lumpnum_t S_GetMusicLumpNum(const char *mname);
 
@@ -860,7 +862,6 @@ static INT32 actualmidimusicvolume;
 void S_UpdateSounds(void)
 {
 	INT32 audible, cnum, volume, sep, pitch;
-	UINT8 i;
 	channel_t *c;
 
 	listener_t listener;
@@ -1017,28 +1018,30 @@ void S_UpdateSounds(void)
 
 notinlevel:
 	I_UpdateSound();
+}
 
+void S_UpdateClosedCaptions(void)
+{
+	UINT8 i;
+	boolean gamestopped = (paused || P_AutoPause());
+	for (i = 0; i < NUMCAPTIONS; i++) // update captions
 	{
-		boolean gamestopped = (paused || P_AutoPause());
-		for (i = 0; i < NUMCAPTIONS; i++) // update captions
-		{
-			if (!closedcaptions[i].s)
-				continue;
+		if (!closedcaptions[i].s)
+			continue;
 
-			if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped)
-				continue;
+		if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped)
+			continue;
 
-			if (!(--closedcaptions[i].t))
-			{
-				closedcaptions[i].c = NULL;
-				closedcaptions[i].s = NULL;
-			}
-			else if (closedcaptions[i].c && !I_SoundIsPlaying(closedcaptions[i].c->handle))
-			{
-				closedcaptions[i].c = NULL;
-				if (closedcaptions[i].t > CAPTIONFADETICS)
-					closedcaptions[i].t = CAPTIONFADETICS;
-			}
+		if (!(--closedcaptions[i].t))
+		{
+			closedcaptions[i].c = NULL;
+			closedcaptions[i].s = NULL;
+		}
+		else if (closedcaptions[i].c && !I_SoundIsPlaying(closedcaptions[i].c->handle))
+		{
+			closedcaptions[i].c = NULL;
+			if (closedcaptions[i].t > CAPTIONFADETICS)
+				closedcaptions[i].t = CAPTIONFADETICS;
 		}
 	}
 }
@@ -2094,21 +2097,8 @@ static lumpnum_t S_GetMusicLumpNum(const char *mname)
 		return W_GetNumForName(va("o_%s", mname));
 	else if (!S_MIDIMusicDisabled() && S_MIDIExists(mname))
 		return W_GetNumForName(va("d_%s", mname));
-	else if (S_DigMusicDisabled() && S_DigExists(mname))
-	{
-		//CONS_Alert(CONS_NOTICE, "Digital music is disabled!\n");
-		return LUMPERROR;
-	}
-	else if (S_MIDIMusicDisabled() && S_MIDIExists(mname))
-	{
-		//CONS_Alert(CONS_NOTICE, "MIDI music is disabled!\n");
-		return LUMPERROR;
-	}
 	else
-	{
-		CONS_Alert(CONS_ERROR, M_GetText("Music lump %.6s not found!\n"), mname);
 		return LUMPERROR;
-	}
 }
 
 static boolean S_LoadMusic(const char *mname)
@@ -2122,7 +2112,10 @@ static boolean S_LoadMusic(const char *mname)
 	mlumpnum = S_GetMusicLumpNum(mname);
 
 	if (mlumpnum == LUMPERROR)
+	{
+		CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded: lump not found!\n", mname);
 		return false;
+	}
 
 	// load & register it
 	mdata = W_CacheLumpNum(mlumpnum, PU_MUSIC);
@@ -2147,7 +2140,10 @@ static boolean S_LoadMusic(const char *mname)
 		return true;
 	}
 	else
+	{
+		CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded: engine failure!\n", mname);
 		return false;
+	}
 }
 
 static void S_UnloadMusic(void)
@@ -2172,6 +2168,7 @@ static boolean S_PlayMusic(boolean looping, UINT32 fadeinms)
 	if ((!fadeinms && !I_PlaySong(looping)) ||
 		(fadeinms && !I_FadeInPlaySong(fadeinms, looping)))
 	{
+		CONS_Alert(CONS_ERROR, "Music %.6s could not be played: engine failure!\n", music_name);
 		S_UnloadMusic();
 		return false;
 	}
@@ -2248,19 +2245,13 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32
 		S_StopMusic();
 
 		if (!S_LoadMusic(newmusic))
-		{
-			CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded!\n", newmusic);
 			return;
-		}
 
 		music_flags = mflags;
 		music_looping = looping;
 
 		if (!S_PlayMusic(looping, fadeinms))
- 		{
-			CONS_Alert(CONS_ERROR, "Music %.6s could not be played!\n", newmusic);
 			return;
-		}
 
 		if (position)
 			I_SetSongPosition(position);
diff --git a/src/s_sound.h b/src/s_sound.h
index d1551df0bdcad2210517dd3ae894fa7848169da2..18f2d37433b85789f76101ab992635210709394f 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -303,6 +303,7 @@ boolean S_FadeOutStopMusic(UINT32 ms);
 // Updates music & sounds
 //
 void S_UpdateSounds(void);
+void S_UpdateClosedCaptions(void);
 
 FUNCMATH fixed_t S_CalculateSoundDistance(fixed_t px1, fixed_t py1, fixed_t pz1, fixed_t px2, fixed_t py2, fixed_t pz2);
 
diff --git a/src/screen.c b/src/screen.c
index 5005118b6c2a1de28d37d2f447488e8e3e362559..5bb304c0844708545d18a18b4d8e0335e61faf74 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -41,26 +41,19 @@
 // --------------------------------------------
 // assembly or c drawer routines for 8bpp/16bpp
 // --------------------------------------------
-void (*wallcolfunc)(void); // new wall column drawer to draw posts >128 high
-void (*colfunc)(void); // standard column, up to 128 high posts
-
-void (*basecolfunc)(void);
-void (*fuzzcolfunc)(void); // standard fuzzy effect column drawer
-void (*transcolfunc)(void); // translation column drawer
-void (*shadecolfunc)(void); // smokie test..
-void (*spanfunc)(void); // span drawer, use a 64x64 tile
-void (*mmxspanfunc)(void); // span drawer in MMX assembly
-void (*splatfunc)(void); // span drawer w/ transparency
-void (*basespanfunc)(void); // default span func for color mode
-void (*transtransfunc)(void); // translucent translated column drawer
-void (*twosmultipatchfunc)(void); // for cols with transparent pixels
-void (*twosmultipatchtransfunc)(void); // for cols with transparent pixels AND translucency
+void (*colfunc)(void);
+void (*colfuncs[COLDRAWFUNC_MAX])(void);
+
+void (*spanfunc)(void);
+void (*spanfuncs[SPANDRAWFUNC_MAX])(void);
+void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void);
 
 // ------------------
 // global video state
 // ------------------
 viddef_t vid;
 INT32 setmodeneeded; //video mode change needed if > 0 (the mode number to set + 1)
+UINT8 setrenderneeded = 0;
 
 static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"}, {24, "24 bits"}, {32, "32 bits"}, {0, NULL}};
 
@@ -70,7 +63,17 @@ consvar_t cv_scr_height = {"scr_height", "800", CV_SAVE, CV_Unsigned, NULL, 0, N
 consvar_t cv_scr_depth = {"scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_renderview = {"renderview", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
-static void SCR_ChangeFullscreen (void);
+static void SCR_ActuallyChangeRenderer(void);
+CV_PossibleValue_t cv_renderer_t[] = {
+	{1, "Software"},
+#ifdef HWRENDER
+	{2, "OpenGL"},
+#endif
+	{0, NULL}
+};
+consvar_t cv_renderer = {"renderer", "Software", CV_SAVE|CV_NOLUA|CV_CALL, cv_renderer_t, SCR_ChangeRenderer, 0, NULL, NULL, 0, 0, NULL};
+
+static void SCR_ChangeFullscreen(void);
 
 consvar_t cv_fullscreen = {"fullscreen", "Yes", CV_SAVE|CV_CALL, CV_YesNo, SCR_ChangeFullscreen, 0, NULL, NULL, 0, 0, NULL};
 
@@ -95,54 +98,79 @@ boolean R_3DNow = false;
 boolean R_MMXExt = false;
 boolean R_SSE2 = false;
 
-
-void SCR_SetMode(void)
+void SCR_SetDrawFuncs(void)
 {
-	if (dedicated)
-		return;
-
-	if (!setmodeneeded || WipeInAction)
-		return; // should never happen and don't change it during a wipe, BAD!
-
-	VID_SetMode(--setmodeneeded);
-
-	V_SetPalette(0);
-
 	//
 	//  setup the right draw routines for either 8bpp or 16bpp
 	//
 	if (true)//vid.bpp == 1) //Always run in 8bpp. todo: remove all 16bpp code?
 	{
-		spanfunc = basespanfunc = mmxspanfunc = R_DrawSpan_8;
-		splatfunc = R_DrawSplat_8;
-		transcolfunc = R_DrawTranslatedColumn_8;
-		transtransfunc = R_DrawTranslatedTranslucentColumn_8;
-
-		colfunc = basecolfunc = R_DrawColumn_8;
-		shadecolfunc = R_DrawShadeColumn_8;
-		fuzzcolfunc = R_DrawTranslucentColumn_8;
-		walldrawerfunc = R_DrawWallColumn_8;
-		twosmultipatchfunc = R_Draw2sMultiPatchColumn_8;
-		twosmultipatchtransfunc = R_Draw2sMultiPatchTranslucentColumn_8;
+		colfuncs[BASEDRAWFUNC] = R_DrawColumn_8;
+		spanfuncs[BASEDRAWFUNC] = R_DrawSpan_8;
+
+		colfunc = colfuncs[BASEDRAWFUNC];
+		spanfunc = spanfuncs[BASEDRAWFUNC];
+
+		colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8;
+		colfuncs[COLDRAWFUNC_TRANS] = R_DrawTranslatedColumn_8;
+		colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8;
+		colfuncs[COLDRAWFUNC_SHADOWED] = R_DrawColumnShadowed_8;
+		colfuncs[COLDRAWFUNC_TRANSTRANS] = R_DrawTranslatedTranslucentColumn_8;
+		colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8;
+		colfuncs[COLDRAWFUNC_TWOSMULTIPATCHTRANS] = R_Draw2sMultiPatchTranslucentColumn_8;
+		colfuncs[COLDRAWFUNC_FOG] = R_DrawFogColumn_8;
+
+		spanfuncs[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_8;
+		spanfuncs[SPANDRAWFUNC_SPLAT] = R_DrawSplat_8;
+		spanfuncs[SPANDRAWFUNC_TRANSSPLAT] = R_DrawTranslucentSplat_8;
+		spanfuncs[SPANDRAWFUNC_FOG] = R_DrawFogSpan_8;
+#ifndef NOWATER
+		spanfuncs[SPANDRAWFUNC_WATER] = R_DrawTranslucentWaterSpan_8;
+#endif
+#ifdef ESLOPE
+		spanfuncs[SPANDRAWFUNC_TILTED] = R_DrawTiltedSpan_8;
+		spanfuncs[SPANDRAWFUNC_TILTEDTRANS] = R_DrawTiltedTranslucentSpan_8;
+#ifndef NOWATER
+		spanfuncs[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedTranslucentWaterSpan_8;
+#endif
+		spanfuncs[SPANDRAWFUNC_TILTEDSPLAT] = R_DrawTiltedSplat_8;
+#endif
+
+		// Lactozilla: Non-powers-of-two
+		spanfuncs_npo2[BASEDRAWFUNC] = R_DrawSpan_NPO2_8;
+		spanfuncs_npo2[SPANDRAWFUNC_TRANS] = R_DrawTranslucentSpan_NPO2_8;
+		spanfuncs_npo2[SPANDRAWFUNC_SPLAT] = R_DrawSplat_NPO2_8;
+		spanfuncs_npo2[SPANDRAWFUNC_TRANSSPLAT] = R_DrawTranslucentSplat_NPO2_8;
+		spanfuncs_npo2[SPANDRAWFUNC_FOG] = NULL; // Not needed
+#ifndef NOWATER
+		spanfuncs_npo2[SPANDRAWFUNC_WATER] = R_DrawTranslucentWaterSpan_NPO2_8;
+#endif
+#ifdef ESLOPE
+		spanfuncs_npo2[SPANDRAWFUNC_TILTED] = R_DrawTiltedSpan_NPO2_8;
+		spanfuncs_npo2[SPANDRAWFUNC_TILTEDTRANS] = R_DrawTiltedTranslucentSpan_NPO2_8;
+#ifndef NOWATER
+		spanfuncs_npo2[SPANDRAWFUNC_TILTEDWATER] = R_DrawTiltedTranslucentWaterSpan_NPO2_8;
+#endif
+		spanfuncs_npo2[SPANDRAWFUNC_TILTEDSPLAT] = R_DrawTiltedSplat_NPO2_8;
+#endif
+
 #ifdef RUSEASM
 		if (R_ASM)
 		{
 			if (R_MMX)
 			{
-				colfunc = basecolfunc = R_DrawColumn_8_MMX;
-				//shadecolfunc = R_DrawShadeColumn_8_ASM;
-				//fuzzcolfunc = R_DrawTranslucentColumn_8_ASM;
-				walldrawerfunc = R_DrawWallColumn_8_MMX;
-				twosmultipatchfunc = R_Draw2sMultiPatchColumn_8_MMX;
-				mmxspanfunc = R_DrawSpan_8_MMX;
+				colfuncs[BASEDRAWFUNC] = R_DrawColumn_8_MMX;
+				//colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8_ASM;
+				//colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8_ASM;
+				colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8_MMX;
+				spanfuncs[BASEDRAWFUNC] = R_DrawSpan_8_MMX;
 			}
 			else
 			{
-				colfunc = basecolfunc = R_DrawColumn_8_ASM;
-				//shadecolfunc = R_DrawShadeColumn_8_ASM;
-				//fuzzcolfunc = R_DrawTranslucentColumn_8_ASM;
-				walldrawerfunc = R_DrawWallColumn_8_ASM;
-				twosmultipatchfunc = R_Draw2sMultiPatchColumn_8_ASM;
+				colfuncs[BASEDRAWFUNC] = R_DrawColumn_8_ASM;
+				//colfuncs[COLDRAWFUNC_SHADE] = R_DrawShadeColumn_8_ASM;
+				//colfuncs[COLDRAWFUNC_FUZZY] = R_DrawTranslucentColumn_8_ASM;
+				colfuncs[COLDRAWFUNC_TWOSMULTIPATCH] = R_Draw2sMultiPatchColumn_8_ASM;
 			}
 		}
 #endif
@@ -165,11 +193,37 @@ void SCR_SetMode(void)
 	if (SCR_IsAspectCorrect(vid.width, vid.height))
 		CONS_Alert(CONS_WARNING, M_GetText("Resolution is not aspect-correct!\nUse a multiple of %dx%d\n"), BASEVIDWIDTH, BASEVIDHEIGHT);
 */
+}
 
-	wallcolfunc = walldrawerfunc;
+void SCR_SetMode(void)
+{
+	if (dedicated)
+		return;
+
+	if (!(setmodeneeded || setrenderneeded) || WipeInAction)
+		return; // should never happen and don't change it during a wipe, BAD!
+
+	// Lactozilla: Renderer switching
+	if (setrenderneeded)
+	{
+		Z_PreparePatchFlush();
+		needpatchflush = true;
+		needpatchrecache = true;
+		VID_CheckRenderer();
+		if (!setmodeneeded)
+			VID_SetMode(vid.modenum);
+	}
+
+	if (setmodeneeded)
+		VID_SetMode(--setmodeneeded);
+
+	V_SetPalette(0);
+
+	SCR_SetDrawFuncs();
 
 	// set the apprpriate drawer for the sky (tall or INT16)
 	setmodeneeded = 0;
+	setrenderneeded = 0;
 }
 
 // do some initial settings for the game loading screen
@@ -357,6 +411,8 @@ void SCR_CheckDefaultMode(void)
 		// see note above
 		setmodeneeded = VID_GetModeForSize(cv_scr_width.value, cv_scr_height.value) + 1;
 	}
+
+	SCR_ActuallyChangeRenderer();
 }
 
 // sets the modenum as the new default video mode to be saved in the config file
@@ -386,6 +442,55 @@ void SCR_ChangeFullscreen(void)
 #endif
 }
 
+static int target_renderer = 0;
+
+void SCR_ActuallyChangeRenderer(void)
+{
+	setrenderneeded = target_renderer;
+	// setting the same renderer twice WILL crash your game, so let's not, please
+	if (rendermode == setrenderneeded)
+		setrenderneeded = 0;
+}
+
+// Lactozilla: Renderer switching
+void SCR_ChangeRenderer(void)
+{
+	setrenderneeded = 0;
+
+	if (con_startup)
+	{
+		target_renderer = cv_renderer.value;
+#ifdef HWRENDER
+		if (M_CheckParm("-opengl"))
+			target_renderer = rendermode = render_opengl;
+		else
+#endif
+		if (M_CheckParm("-software"))
+			target_renderer = rendermode = render_soft;
+		// set cv_renderer back
+		SCR_ChangeRendererCVars(rendermode);
+		return;
+	}
+
+	if (cv_renderer.value == 1)
+		target_renderer = render_soft;
+	else if (cv_renderer.value == 2)
+		target_renderer = render_opengl;
+	SCR_ActuallyChangeRenderer();
+}
+
+void SCR_ChangeRendererCVars(INT32 mode)
+{
+	// set cv_renderer back
+	if (mode == render_soft)
+		CV_StealthSetValue(&cv_renderer, 1);
+	else if (mode == render_opengl)
+		CV_StealthSetValue(&cv_renderer, 2);
+#ifdef HWRENDER
+	CV_StealthSetValue(&cv_newrenderer, cv_renderer.value);
+#endif
+}
+
 boolean SCR_IsAspectCorrect(INT32 width, INT32 height)
 {
 	return
diff --git a/src/screen.h b/src/screen.h
index 6f03612c46b86b08bec2b6c68b131a9e16371b9b..02b336f7530f1186102c956922706d50f33b57f5 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -110,19 +110,51 @@ extern vmode_t specialmodes[NUMSPECIALMODES];
 // color mode dependent drawer function pointers
 // ---------------------------------------------
 
-extern void (*wallcolfunc)(void);
+#define BASEDRAWFUNC 0
+
+enum
+{
+	COLDRAWFUNC_BASE = BASEDRAWFUNC,
+	COLDRAWFUNC_FUZZY,
+	COLDRAWFUNC_TRANS,
+	COLDRAWFUNC_SHADE,
+	COLDRAWFUNC_SHADOWED,
+	COLDRAWFUNC_TRANSTRANS,
+	COLDRAWFUNC_TWOSMULTIPATCH,
+	COLDRAWFUNC_TWOSMULTIPATCHTRANS,
+	COLDRAWFUNC_FOG,
+
+	COLDRAWFUNC_MAX
+};
+
 extern void (*colfunc)(void);
-extern void (*basecolfunc)(void);
-extern void (*fuzzcolfunc)(void);
-extern void (*transcolfunc)(void);
-extern void (*shadecolfunc)(void);
+extern void (*colfuncs[COLDRAWFUNC_MAX])(void);
+
+enum
+{
+	SPANDRAWFUNC_BASE = BASEDRAWFUNC,
+	SPANDRAWFUNC_TRANS,
+	SPANDRAWFUNC_SPLAT,
+	SPANDRAWFUNC_TRANSSPLAT,
+	SPANDRAWFUNC_FOG,
+#ifndef NOWATER
+	SPANDRAWFUNC_WATER,
+#endif
+#ifdef ESLOPE
+	SPANDRAWFUNC_TILTED,
+	SPANDRAWFUNC_TILTEDTRANS,
+	SPANDRAWFUNC_TILTEDSPLAT,
+#ifndef NOWATER
+	SPANDRAWFUNC_TILTEDWATER,
+#endif
+#endif
+
+	SPANDRAWFUNC_MAX
+};
+
 extern void (*spanfunc)(void);
-extern void (*basespanfunc)(void);
-extern void (*mmxspanfunc)(void);
-extern void (*splatfunc)(void);
-extern void (*transtransfunc)(void);
-extern void (*twosmultipatchfunc)(void);
-extern void (*twosmultipatchtransfunc)(void);
+extern void (*spanfuncs[SPANDRAWFUNC_MAX])(void);
+extern void (*spanfuncs_npo2[SPANDRAWFUNC_MAX])(void);
 
 // -----
 // CPUID
@@ -138,21 +170,29 @@ extern boolean R_SSE2;
 // ----------------
 // screen variables
 // ----------------
+
 extern viddef_t vid;
 extern INT32 setmodeneeded; // mode number to set if needed, or 0
 
+void SCR_ChangeRenderer(void);
+void SCR_ChangeRendererCVars(INT32 mode);
+extern UINT8 setrenderneeded;
+
 extern INT32 scr_bpp;
 extern UINT8 *scr_borderpatch; // patch used to fill the view borders
 
-extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_fullscreen;
+extern CV_PossibleValue_t cv_renderer_t[];
+
+extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_renderer, cv_fullscreen;
+#ifdef HWRENDER
+extern consvar_t cv_newrenderer;
+#endif
 // wait for page flipping to end or not
 extern consvar_t cv_vidwait;
 
-// quick fix for tall/short skies, depending on bytesperpixel
-extern void (*walldrawerfunc)(void);
-
 // Change video mode, only at the start of a refresh.
 void SCR_SetMode(void);
+void SCR_SetDrawFuncs(void);
 // Recalc screen size dependent stuff
 void SCR_Recalc(void);
 // Check parms once at startup
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index f1aa135b9eafd520e0700835b9c509898f22bef3..38d557a3f59517b33b256db39aecff4ed3d6ddb9 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -140,7 +140,13 @@ if(${SDL2_FOUND})
 	endif()
 
 	add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 ${SRB2_SDL2_TOTAL_SOURCES})
-	set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME ${SRB2_SDL2_EXE_NAME})
+	if(${CMAKE_SYSTEM} MATCHES Windows)
+		set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME srb2win)
+	elseif(${CMAKE_SYSTEM} MATCHES Linux)
+		set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME lsdlsrb2)
+	else()
+		set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME srb2)
+	endif()
 
 	if(${CMAKE_SYSTEM} MATCHES Darwin)
 		find_library(CORE_LIB CoreFoundation)
diff --git a/src/sdl/IMG_xpm.c b/src/sdl/IMG_xpm.c
index 43fb4ded235ee33ac961870dcc18a7aa104a9db0..458131620e16cc1ff543739323d24bd611340619 100644
--- a/src/sdl/IMG_xpm.c
+++ b/src/sdl/IMG_xpm.c
@@ -1012,7 +1012,7 @@ static SDL_Surface *load_xpm(const char **xpm, SDL_RWops *src)
     size_t index;
     int x, y;
     int w, h, cpp;
-    size_t ncolors;
+    long unsigned int ncolors;
     size_t indexed;
     Uint8 *dst;
     struct color_hash *colors = NULL;
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 0d1fa1a4b5df7947be1559b1663175c592a087d3..b334f6313614b5890c9b971b07068e7c404b6cec 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -438,6 +438,9 @@
     <ClCompile Include="..\r_draw8.c">
       <ExcludedFromBuild>true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="..\r_draw8_npo2.c">
+      <ExcludedFromBuild>true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="..\r_main.c" />
     <ClCompile Include="..\r_plane.c" />
 	<ClCompile Include="..\r_patch.c" />
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index cface637773be2167340011d7eec451d1f251042..3f61e87098fd08e732f7c521435d93fb63ca77f9 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -837,6 +837,9 @@
     <ClCompile Include="..\r_draw8.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
+    <ClCompile Include="..\r_draw8_npo2.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
     <ClCompile Include="..\r_main.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c
index 029febc059a10e8458de222eccce1180927e1a52..5d0009927f1f64579c538d2cc604ee30fa3aa007 100644
--- a/src/sdl/i_main.c
+++ b/src/sdl/i_main.c
@@ -20,12 +20,17 @@
 #include "../doomdef.h"
 #include "../m_argv.h"
 #include "../d_main.h"
+#include "../m_misc.h"/* path shit */
 #include "../i_system.h"
 
-#ifdef __GNUC__
+#if defined (__GNUC__) || defined (__unix__)
 #include <unistd.h>
 #endif
 
+#ifdef __unix__
+#include <errno.h>
+#endif
+
 #include "time.h" // For log timestamps
 
 #ifdef HAVE_SDL
@@ -47,6 +52,7 @@ extern int SDL_main(int argc, char *argv[]);
 
 #ifdef LOGMESSAGES
 FILE *logstream = NULL;
+char logfilename[1024];
 #endif
 
 #ifndef DOXYGEN
@@ -116,7 +122,6 @@ int main(int argc, char **argv)
 #endif
 {
 	const char *logdir = NULL;
-	char logfile[MAX_WADPATH];
 	myargc = argc;
 	myargv = argv; /// \todo pull out path to exe from this string
 
@@ -133,32 +138,86 @@ int main(int argc, char **argv)
 	{
 		time_t my_time;
 		struct tm * timeinfo;
-		char buf[26];
+		const char *format;
+		const char *reldir;
+		int left;
+		boolean fileabs;
+#ifdef __unix__
+		const char *link;
+#endif
 
 		logdir = D_Home();
 
 		my_time = time(NULL);
 		timeinfo = localtime(&my_time);
 
-		strftime(buf, 26, "%Y-%m-%d %H-%M-%S", timeinfo);
-		strcpy(logfile, va("log-%s.txt", buf));
+		if (M_CheckParm("-logfile") && M_IsNextParm())
+		{
+			format = M_GetNextParm();
+			fileabs = M_IsPathAbsolute(format);
+		}
+		else
+		{
+			format = "log-%Y-%m-%d_%H-%M-%S.txt";
+			fileabs = false;
+		}
 
-#ifdef DEFAULTDIR
-		if (logdir)
+		if (fileabs)
 		{
-			// Create dirs here because D_SRB2Main() is too late.
-			I_mkdir(va("%s%s"DEFAULTDIR, logdir, PATHSEP), 0755);
-			I_mkdir(va("%s%s"DEFAULTDIR"%slogs",logdir, PATHSEP, PATHSEP), 0755);
-			logstream = fopen(va("%s%s"DEFAULTDIR"%slogs%s%s",logdir, PATHSEP, PATHSEP, PATHSEP, logfile), "wt");
+			strftime(logfilename, sizeof logfilename, format, timeinfo);
 		}
 		else
-#endif
 		{
-			I_mkdir("."PATHSEP"logs"PATHSEP, 0755);
-			logstream = fopen(va("."PATHSEP"logs"PATHSEP"%s", logfile), "wt");
+			if (M_CheckParm("-logdir") && M_IsNextParm())
+				reldir = M_GetNextParm();
+			else
+				reldir = "logs";
+
+			if (M_IsPathAbsolute(reldir))
+			{
+				left = snprintf(logfilename, sizeof logfilename,
+						"%s"PATHSEP, reldir);
+			}
+			else
+#ifdef DEFAULTDIR
+			if (logdir)
+			{
+				left = snprintf(logfilename, sizeof logfilename,
+						"%s"PATHSEP DEFAULTDIR PATHSEP"%s"PATHSEP, logdir, reldir);
+			}
+			else
+#endif/*DEFAULTDIR*/
+			{
+				left = snprintf(logfilename, sizeof logfilename,
+						"."PATHSEP"%s"PATHSEP, reldir);
+			}
+#endif/*LOGMESSAGES*/
+
+			strftime(&logfilename[left], sizeof logfilename - left,
+					format, timeinfo);
 		}
+
+		M_MkdirEachUntil(logfilename,
+				M_PathParts(logdir) - 1,
+				M_PathParts(logfilename) - 1, 0755);
+
+#ifdef __unix__
+		logstream = fopen(logfilename, "w");
+#ifdef DEFAULTDIR
+		if (logdir)
+			link = va("%s/"DEFAULTDIR"/latest-log.txt", logdir);
+		else
+#endif/*DEFAULTDIR*/
+			link = "latest-log.txt";
+		unlink(link);
+		if (symlink(logfilename, link) == -1)
+		{
+			I_OutputMsg("Error symlinking latest-log.txt: %s\n", strerror(errno));
+		}
+#else/*__unix__*/
+		logstream = fopen("latest-log.txt", "wt+");
+#endif/*__unix__*/
 	}
-#endif
 
 	//I_OutputMsg("I_StartupSystem() ...\n");
 	I_StartupSystem();
@@ -181,12 +240,13 @@ int main(int argc, char **argv)
 #endif
 	MakeCodeWritable();
 #endif
+
 	// startup SRB2
 	CONS_Printf("Setting up SRB2...\n");
 	D_SRB2Main();
 #ifdef LOGMESSAGES
 	if (!M_CheckParm("-nolog"))
-		CONS_Printf("Logfile: %s\n", logfile);
+		CONS_Printf("Logfile: %s\n", logfilename);
 #endif
 	CONS_Printf("Entering main game loop...\n");
 	// never return
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index e7f8f2e4f9529e69a243f30defd30e835e7fc514..81420e75772d621e1e20163c558ab0c9bd81d3ae 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -5,7 +5,7 @@
 //
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 2014-2018 by Sonic Team Junior.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -102,6 +102,12 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
 #endif
 #endif
 
+#if (defined (__unix__) && !defined (_MSDOS)) || defined (UNIXCOMMON)
+#include <errno.h>
+#include <sys/wait.h>
+#define NEWSIGNALHANDLER
+#endif
+
 #ifndef NOMUMBLE
 #ifdef __linux__ // need -lrt
 #include <sys/mman.h>
@@ -229,13 +235,11 @@ SDL_bool framebuffer = SDL_FALSE;
 
 UINT8 keyboard_started = false;
 
-FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
+static void I_ReportSignal(int num, int coredumped)
 {
 	//static char msg[] = "oh no! back to reality!\r\n";
 	const char *      sigmsg;
-	char        sigdef[32];
-
-	D_QuitNetGame(); // Fix server freezes
+	char msg[128];
 
 	switch (num)
 	{
@@ -261,20 +265,41 @@ FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
 		sigmsg = "SIGABRT - abnormal termination triggered by abort call";
 		break;
 	default:
-		sprintf(sigdef,"signal number %d", num);
-		sigmsg = sigdef;
+		sprintf(msg,"signal number %d", num);
+		if (coredumped)
+			sigmsg = 0;
+		else
+			sigmsg = msg;
 	}
 
-	I_OutputMsg("\nsignal_handler() error: %s\n", sigmsg);
+	if (coredumped)
+	{
+		if (sigmsg)
+			sprintf(msg, "%s (core dumped)", sigmsg);
+		else
+			strcat(msg, " (core dumped)");
+
+		sigmsg = msg;
+	}
+
+	I_OutputMsg("\nProcess killed by signal: %s\n\n", sigmsg);
 
 	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
-		"Signal caught",
+		"Process killed by signal",
 		sigmsg, NULL);
+}
+
+#ifndef NEWSIGNALHANDLER
+FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
+{
+	D_QuitNetGame(); // Fix server freezes
+	I_ReportSignal(num, 0);
 	I_ShutdownSystem();
 	signal(num, SIG_DFL);               //default signal action
 	raise(num);
 	I_Quit();
 }
+#endif
 
 FUNCNORETURN static ATTRNORETURN void quit_handler(int num)
 {
@@ -650,7 +675,7 @@ static inline void I_ShutdownConsole(void){}
 //
 // StartupKeyboard
 //
-void I_StartupKeyboard (void)
+static void I_RegisterSignals (void)
 {
 #ifdef SIGINT
 	signal(SIGINT , quit_handler);
@@ -664,10 +689,12 @@ void I_StartupKeyboard (void)
 
 	// If these defines don't exist,
 	// then compilation would have failed above us...
+#ifndef NEWSIGNALHANDLER
 	signal(SIGILL , signal_handler);
 	signal(SIGSEGV , signal_handler);
 	signal(SIGABRT , signal_handler);
 	signal(SIGFPE , signal_handler);
+#endif
 }
 
 //
@@ -2139,6 +2166,85 @@ void I_Sleep(void)
 		SDL_Delay(cv_sleep.value);
 }
 
+#ifdef NEWSIGNALHANDLER
+static void newsignalhandler_Warn(const char *pr)
+{
+	char text[128];
+
+	snprintf(text, sizeof text,
+			"Error while setting up signal reporting: %s: %s",
+			pr,
+			strerror(errno)
+	);
+
+	I_OutputMsg("%s\n", text);
+
+	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
+		"Startup error",
+		text, NULL);
+
+	I_ShutdownConsole();
+	exit(-1);
+}
+
+static void I_Fork(void)
+{
+	int child;
+	int status;
+	int signum;
+	int c;
+
+	child = fork();
+
+	switch (child)
+	{
+		case -1:
+			newsignalhandler_Warn("fork()");
+			break;
+		case 0:
+			break;
+		default:
+			if (logstream)
+				fclose(logstream);/* the child has this */
+
+			c = wait(&status);
+
+#ifdef LOGMESSAGES
+			/* By the way, exit closes files. */
+			logstream = fopen(logfilename, "at");
+#else
+			logstream = 0;
+#endif
+
+			if (c == -1)
+			{
+				kill(child, SIGKILL);
+				newsignalhandler_Warn("wait()");
+			}
+			else
+			{
+				if (WIFSIGNALED (status))
+				{
+					signum = WTERMSIG (status);
+#ifdef WCOREDUMP
+					I_ReportSignal(signum, WCOREDUMP (status));
+#else
+					I_ReportSignal(signum, 0);
+#endif
+					status = 128 + signum;
+				}
+				else if (WIFEXITED (status))
+				{
+					status = WEXITSTATUS (status);
+				}
+
+				I_ShutdownConsole();
+				exit(status);
+			}
+	}
+}
+#endif/*NEWSIGNALHANDLER*/
+
 INT32 I_StartupSystem(void)
 {
 	SDL_version SDLcompiled;
@@ -2146,6 +2252,10 @@ INT32 I_StartupSystem(void)
 	SDL_VERSION(&SDLcompiled)
 	SDL_GetVersion(&SDLlinked);
 	I_StartupConsole();
+#ifdef NEWSIGNALHANDLER
+	I_Fork();
+#endif
+	I_RegisterSignals();
 	I_OutputMsg("Compiled for SDL version: %d.%d.%d\n",
 	 SDLcompiled.major, SDLcompiled.minor, SDLcompiled.patch);
 	I_OutputMsg("Linked with SDL version: %d.%d.%d\n",
@@ -2169,7 +2279,6 @@ void I_Quit(void)
 	if (quiting) goto death;
 	SDLforceUngrabMouse();
 	quiting = SDL_FALSE;
-	I_ShutdownConsole();
 	M_SaveConfig(NULL); //save game config, cvars..
 #ifndef NONET
 	D_SaveBan(); // save the ban list
@@ -2287,8 +2396,6 @@ void I_Error(const char *error, ...)
 	I_OutputMsg("\nI_Error(): %s\n", buffer);
 	// ---
 
-	I_ShutdownConsole();
-
 	M_SaveConfig(NULL); // save game config, cvars..
 #ifndef NONET
 	D_SaveBan(); // save the ban list
@@ -2377,6 +2484,48 @@ void I_RemoveExitFunc(void (*func)())
 	}
 }
 
+#ifndef __unix__
+static void Shittycopyerror(const char *name)
+{
+	I_OutputMsg(
+			"Error copying log file: %s: %s\n",
+			name,
+			strerror(errno)
+	);
+}
+
+static void Shittylogcopy(void)
+{
+	char buf[8192];
+	FILE *fp;
+	size_t r;
+	if (fseek(logstream, 0, SEEK_SET) == -1)
+	{
+		Shittycopyerror("fseek");
+	}
+	else if (( fp = fopen(logfilename, "wt") ))
+	{
+		while (( r = fread(buf, 1, sizeof buf, logstream) ))
+		{
+			if (fwrite(buf, 1, r, fp) < r)
+			{
+				Shittycopyerror("fwrite");
+				break;
+			}
+		}
+		if (ferror(logstream))
+		{
+			Shittycopyerror("fread");
+		}
+		fclose(fp);
+	}
+	else
+	{
+		Shittycopyerror(logfilename);
+	}
+}
+#endif/*__unix__*/
+
 //
 //  Closes down everything. This includes restoring the initial
 //  palette and video mode, and removing whatever mouse, keyboard, and
@@ -2388,6 +2537,10 @@ void I_ShutdownSystem(void)
 {
 	INT32 c;
 
+#ifndef NEWSIGNALHANDLER
+	I_ShutdownConsole();
+#endif
+
 	for (c = MAX_QUIT_FUNCS-1; c >= 0; c--)
 		if (quit_funcs[c])
 			(*quit_funcs[c])();
@@ -2395,6 +2548,9 @@ void I_ShutdownSystem(void)
 	if (logstream)
 	{
 		I_OutputMsg("I_ShutdownSystem(): end of logstream.\n");
+#ifndef __unix__
+		Shittylogcopy();
+#endif
 		fclose(logstream);
 		logstream = NULL;
 	}
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 95ddab3cc51615079a1a3f553ab9ac66e0baedc3..2b8633e5b47dd70c59d74962cea83b6a76a8468d 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -4,7 +4,7 @@
 //
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 2014-2018 by Sonic Team Junior.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -32,6 +32,7 @@
 #include "SDL.h"
 
 #ifdef _MSC_VER
+#include <windows.h>
 #pragma warning(default : 4214 4244)
 #endif
 
@@ -66,10 +67,12 @@
 #include "../s_sound.h"
 #include "../i_joy.h"
 #include "../st_stuff.h"
+#include "../hu_stuff.h"
 #include "../g_game.h"
 #include "../i_video.h"
 #include "../console.h"
 #include "../command.h"
+#include "../r_main.h"
 #include "sdlmain.h"
 #ifdef HWRENDER
 #include "../hardware/hw_main.h"
@@ -97,6 +100,7 @@ boolean highcolor = false;
 // synchronize page flipping with screen refresh
 consvar_t cv_vidwait = {"vid_wait", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 static consvar_t cv_stretch = {"stretch", "Off", CV_SAVE|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+static consvar_t cv_alwaysgrabmouse = {"alwaysgrabmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 UINT8 graphics_started = 0; // Is used in console.c and screen.c
 
@@ -106,6 +110,7 @@ static SDL_bool disable_fullscreen = SDL_FALSE;
 #define USE_FULLSCREEN (disable_fullscreen||!allow_fullscreen)?0:cv_fullscreen.value
 static SDL_bool disable_mouse = SDL_FALSE;
 #define USE_MOUSEINPUT (!disable_mouse && cv_usemouse.value && havefocus)
+#define IGNORE_MOUSE (!cv_alwaysgrabmouse.value && (menuactive || paused || con_destlines || chat_on || gamestate != GS_LEVEL))
 #define MOUSE_MENU false //(!disable_mouse && cv_usemouse.value && menuactive && !USE_FULLSCREEN)
 #define MOUSEBUTTONS_MAX MOUSEBUTTONS
 
@@ -376,12 +381,15 @@ static void SDLdoUngrabMouse(void)
 void SDLforceUngrabMouse(void)
 {
 	if (SDL_WasInit(SDL_INIT_VIDEO)==SDL_INIT_VIDEO && window != NULL)
-	{
-		SDL_ShowCursor(SDL_ENABLE);
-		SDL_SetWindowGrab(window, SDL_FALSE);
-		wrapmouseok = SDL_FALSE;
-		SDL_SetRelativeMouseMode(SDL_FALSE);
-	}
+		SDLdoUngrabMouse();
+}
+
+void I_UpdateMouseGrab(void)
+{
+	if (SDL_WasInit(SDL_INIT_VIDEO) == SDL_INIT_VIDEO && window != NULL
+	&& SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window
+	&& USE_MOUSEINPUT && !IGNORE_MOUSE)
+		SDLdoGrabMouse();
 }
 
 static void VID_Command_NumModes_f (void)
@@ -588,7 +596,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
 		}
 		//else firsttimeonmouse = SDL_FALSE;
 
-		if (USE_MOUSEINPUT)
+		if (USE_MOUSEINPUT && !IGNORE_MOUSE)
 			SDLdoGrabMouse();
 	}
 	else if (!mousefocus && !kbfocus)
@@ -635,11 +643,14 @@ static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type)
 
 static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 {
+	static boolean firstmove = true;
+
 	if (USE_MOUSEINPUT)
 	{
-		if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window))
+		if ((SDL_GetMouseFocus() != window && SDL_GetKeyboardFocus() != window) || (IGNORE_MOUSE && !firstmove))
 		{
 			SDLdoUngrabMouse();
+			firstmove = false;
 			return;
 		}
 
@@ -653,6 +664,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 				mousemovey += -evt.yrel;
 				SDL_SetWindowGrab(window, SDL_TRUE);
 			}
+			firstmove = false;
 			return;
 		}
 
@@ -660,6 +672,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 		// of the screen then ignore it.
 		if ((evt.x == realwidth/2) && (evt.y == realheight/2))
 		{
+			firstmove = false;
 			return;
 		}
 
@@ -672,6 +685,8 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt)
 			SDLdoGrabMouse();
 		}
 	}
+
+	firstmove = false;
 }
 
 static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type)
@@ -685,7 +700,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)
+	if (SDL_GetMouseFocus() != window || IGNORE_MOUSE)
 		return;
 
 	/// \todo inputEvent.button.which
@@ -1067,7 +1082,7 @@ void I_StartupMouse(void)
 	}
 	else
 		firsttimeonmouse = SDL_FALSE;
-	if (cv_usemouse.value)
+	if (cv_usemouse.value && !IGNORE_MOUSE)
 		SDLdoGrabMouse();
 	else
 		SDLdoUngrabMouse();
@@ -1207,7 +1222,6 @@ void I_FinishUpdate(void)
 		SDL_RenderCopy(renderer, texture, NULL, NULL);
 		SDL_RenderPresent(renderer);
 	}
-
 #ifdef HWRENDER
 	else if (rendermode == render_opengl)
 	{
@@ -1407,39 +1421,56 @@ void VID_PrepareModeList(void)
 #endif
 }
 
-INT32 VID_SetMode(INT32 modeNum)
+static SDL_bool Impl_CreateContext(void)
 {
-	SDLdoUngrabMouse();
-
-	vid.recalc = 1;
-	vid.bpp = 1;
-
-	if (modeNum >= 0 && modeNum < MAXWINMODES)
+	// Renderer-specific stuff
+#ifdef HWRENDER
+	if (rendermode == render_opengl)
 	{
-		vid.width = windowedModes[modeNum][0];
-		vid.height = windowedModes[modeNum][1];
-		vid.modenum = modeNum;
+		if (!sdlglcontext)
+			sdlglcontext = SDL_GL_CreateContext(window);
+		if (sdlglcontext == NULL)
+		{
+			SDL_DestroyWindow(window);
+			I_Error("Failed to create a GL context: %s\n", SDL_GetError());
+		}
+		SDL_GL_MakeCurrent(window, sdlglcontext);
 	}
 	else
+#endif
+	if (rendermode == render_soft)
 	{
-		// just set the desktop resolution as a fallback
-		SDL_DisplayMode mode;
-		SDL_GetWindowDisplayMode(window, &mode);
-		if (mode.w >= 2048)
-		{
-			vid.width = 1920;
-			vid.height = 1200;
-		}
-		else
+		int flags = 0; // Use this to set SDL_RENDERER_* flags now
+		if (usesdl2soft)
+			flags |= SDL_RENDERER_SOFTWARE;
+		else if (cv_vidwait.value)
+			flags |= SDL_RENDERER_PRESENTVSYNC;
+
+		if (!renderer)
+			renderer = SDL_CreateRenderer(window, -1, flags);
+		if (renderer == NULL)
 		{
-			vid.width = mode.w;
-			vid.height = mode.h;
+			CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
+			return SDL_FALSE;
 		}
-		vid.modenum = -1;
+		SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
+	}
+	return SDL_TRUE;
+}
+
+void VID_CheckRenderer(void)
+{
+	if (dedicated)
+		return;
+
+	if (setrenderneeded)
+	{
+		rendermode = setrenderneeded;
+		Impl_CreateContext();
 	}
-	//Impl_SetWindowName("SRB2 "VERSIONSTRING);
 
 	SDLSetMode(vid.width, vid.height, USE_FULLSCREEN);
+	Impl_VideoSetupBuffer();
 
 	if (rendermode == render_soft)
 	{
@@ -1448,10 +1479,39 @@ INT32 VID_SetMode(INT32 modeNum)
 			SDL_FreeSurface(bufSurface);
 			bufSurface = NULL;
 		}
-
-		Impl_VideoSetupBuffer();
+#ifdef HWRENDER
+		HWR_FreeTextureCache();
+#endif
+		SCR_SetDrawFuncs();
+	}
+#ifdef HWRENDER
+	else if (rendermode == render_opengl)
+	{
+		I_StartupHardwareGraphics();
+		R_InitHardwareMode();
+		HWR_Switch();
 	}
+#endif
+}
+
+INT32 VID_SetMode(INT32 modeNum)
+{
+	SDLdoUngrabMouse();
+
+	vid.recalc = 1;
+	vid.bpp = 1;
+
+	if (modeNum < 0)
+		modeNum = 0;
+	if (modeNum >= MAXWINMODES)
+		modeNum = MAXWINMODES-1;
 
+	vid.width = windowedModes[modeNum][0];
+	vid.height = windowedModes[modeNum][1];
+	vid.modenum = modeNum;
+
+	//Impl_SetWindowName("SRB2 "VERSIONSTRING);
+	VID_CheckRenderer();
 	return SDL_TRUE;
 }
 
@@ -1472,8 +1532,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 		flags |= SDL_WINDOW_BORDERLESS;
 
 #ifdef HWRENDER
-	if (rendermode == render_opengl)
-		flags |= SDL_WINDOW_OPENGL;
+	flags |= SDL_WINDOW_OPENGL;
 #endif
 
 	// Create a window
@@ -1486,38 +1545,7 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
 		return SDL_FALSE;
 	}
 
-	// Renderer-specific stuff
-#ifdef HWRENDER
-	if (rendermode == render_opengl)
-	{
-		sdlglcontext = SDL_GL_CreateContext(window);
-		if (sdlglcontext == NULL)
-		{
-			SDL_DestroyWindow(window);
-			I_Error("Failed to create a GL context: %s\n", SDL_GetError());
-		}
-		SDL_GL_MakeCurrent(window, sdlglcontext);
-	}
-	else
-#endif
-	if (rendermode == render_soft)
-	{
-		flags = 0; // Use this to set SDL_RENDERER_* flags now
-		if (usesdl2soft)
-			flags |= SDL_RENDERER_SOFTWARE;
-		else if (cv_vidwait.value)
-			flags |= SDL_RENDERER_PRESENTVSYNC;
-
-		renderer = SDL_CreateRenderer(window, -1, flags);
-		if (renderer == NULL)
-		{
-			CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
-			return SDL_FALSE;
-		}
-		SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
-	}
-
-	return SDL_TRUE;
+	return Impl_CreateContext();
 }
 
 /*
@@ -1572,17 +1600,14 @@ static void Impl_VideoSetupSDLBuffer(void)
 static void Impl_VideoSetupBuffer(void)
 {
 	// Set up game's software render buffer
-	if (rendermode == render_soft)
+	vid.rowbytes = vid.width * vid.bpp;
+	vid.direct = NULL;
+	if (vid.buffer)
+		free(vid.buffer);
+	vid.buffer = calloc(vid.rowbytes*vid.height, NUMSCREENS);
+	if (!vid.buffer)
 	{
-		vid.rowbytes = vid.width * vid.bpp;
-		vid.direct = NULL;
-		if (vid.buffer)
-			free(vid.buffer);
-		vid.buffer = calloc(vid.rowbytes*vid.height, NUMSCREENS);
-		if (!vid.buffer)
-		{
-			I_Error("%s", M_GetText("Not enough memory for video buffer\n"));
-		}
+		I_Error("%s", M_GetText("Not enough memory for video buffer\n"));
 	}
 }
 
@@ -1602,6 +1627,7 @@ void I_StartupGraphics(void)
 	COM_AddCommand ("vid_mode", VID_Command_Mode_f);
 	CV_RegisterVar (&cv_vidwait);
 	CV_RegisterVar (&cv_stretch);
+	CV_RegisterVar (&cv_alwaysgrabmouse);
 	disable_mouse = M_CheckParm("-nomouse");
 	disable_fullscreen = M_CheckParm("-win") ? 1 : 0;
 
@@ -1626,10 +1652,13 @@ void I_StartupGraphics(void)
 		))
 			framebuffer = SDL_TRUE;
 	}
-	if (M_CheckParm("-software"))
-	{
+
+#ifdef HWRENDER
+	if (M_CheckParm("-opengl"))
+		rendermode = render_opengl;
+	else if (M_CheckParm("-software"))
+#endif
 		rendermode = render_soft;
-	}
 
 	usesdl2soft = M_CheckParm("-softblit");
 	borderlesswindow = M_CheckParm("-borderless");
@@ -1637,44 +1666,7 @@ void I_StartupGraphics(void)
 	//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
 	VID_Command_ModeList_f();
 #ifdef HWRENDER
-	if (M_CheckParm("-opengl") || rendermode == render_opengl)
-	{
-		rendermode = render_opengl;
-		HWD.pfnInit             = hwSym("Init",NULL);
-		HWD.pfnFinishUpdate     = NULL;
-		HWD.pfnDraw2DLine       = hwSym("Draw2DLine",NULL);
-		HWD.pfnDrawPolygon      = hwSym("DrawPolygon",NULL);
-		HWD.pfnRenderSkyDome    = hwSym("RenderSkyDome",NULL);
-		HWD.pfnSetBlend         = hwSym("SetBlend",NULL);
-		HWD.pfnClearBuffer      = hwSym("ClearBuffer",NULL);
-		HWD.pfnSetTexture       = hwSym("SetTexture",NULL);
-		HWD.pfnReadRect         = hwSym("ReadRect",NULL);
-		HWD.pfnGClipRect        = hwSym("GClipRect",NULL);
-		HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL);
-		HWD.pfnSetSpecialState  = hwSym("SetSpecialState",NULL);
-		HWD.pfnSetPalette       = hwSym("SetPalette",NULL);
-		HWD.pfnGetTextureUsed   = hwSym("GetTextureUsed",NULL);
-		HWD.pfnDrawModel        = hwSym("DrawModel",NULL);
-		HWD.pfnCreateModelVBOs  = hwSym("CreateModelVBOs",NULL);
-		HWD.pfnSetTransform     = hwSym("SetTransform",NULL);
-		HWD.pfnGetRenderVersion = hwSym("GetRenderVersion",NULL);
-		HWD.pfnPostImgRedraw    = hwSym("PostImgRedraw",NULL);
-		HWD.pfnFlushScreenTextures=hwSym("FlushScreenTextures",NULL);
-		HWD.pfnStartScreenWipe  = hwSym("StartScreenWipe",NULL);
-		HWD.pfnEndScreenWipe    = hwSym("EndScreenWipe",NULL);
-		HWD.pfnDoScreenWipe     = hwSym("DoScreenWipe",NULL);
-		HWD.pfnDrawIntermissionBG=hwSym("DrawIntermissionBG",NULL);
-		HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
-		HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
-		HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
-		// check gl renderer lib
-		if (HWD.pfnGetRenderVersion() != VERSION)
-			I_Error("%s", M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n"));
-		if (!HWD.pfnInit(I_Error)) // let load the OpenGL library
-		{
-			rendermode = render_soft;
-		}
-	}
+	I_StartupHardwareGraphics();
 #endif
 
 	// Fury: we do window initialization after GL setup to allow
@@ -1724,16 +1716,55 @@ void I_StartupGraphics(void)
 	SDL_RaiseWindow(window);
 
 	if (mousegrabok && !disable_mouse)
-	{
-		SDL_ShowCursor(SDL_DISABLE);
-		SDL_SetRelativeMouseMode(SDL_TRUE);
-		wrapmouseok = SDL_TRUE;
-		SDL_SetWindowGrab(window, SDL_TRUE);
-	}
+		SDLdoGrabMouse();
 
 	graphics_started = true;
 }
 
+void I_StartupHardwareGraphics(void)
+{
+#ifdef HWRENDER
+	static boolean glstartup = false;
+	if (!glstartup)
+	{
+		HWD.pfnInit             = hwSym("Init",NULL);
+		HWD.pfnFinishUpdate     = NULL;
+		HWD.pfnDraw2DLine       = hwSym("Draw2DLine",NULL);
+		HWD.pfnDrawPolygon      = hwSym("DrawPolygon",NULL);
+		HWD.pfnRenderSkyDome    = hwSym("RenderSkyDome",NULL);
+		HWD.pfnSetBlend         = hwSym("SetBlend",NULL);
+		HWD.pfnClearBuffer      = hwSym("ClearBuffer",NULL);
+		HWD.pfnSetTexture       = hwSym("SetTexture",NULL);
+		HWD.pfnReadRect         = hwSym("ReadRect",NULL);
+		HWD.pfnGClipRect        = hwSym("GClipRect",NULL);
+		HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL);
+		HWD.pfnSetSpecialState  = hwSym("SetSpecialState",NULL);
+		HWD.pfnSetPalette       = hwSym("SetPalette",NULL);
+		HWD.pfnGetTextureUsed   = hwSym("GetTextureUsed",NULL);
+		HWD.pfnDrawModel        = hwSym("DrawModel",NULL);
+		HWD.pfnCreateModelVBOs  = hwSym("CreateModelVBOs",NULL);
+		HWD.pfnSetTransform     = hwSym("SetTransform",NULL);
+		HWD.pfnGetRenderVersion = hwSym("GetRenderVersion",NULL);
+		HWD.pfnPostImgRedraw    = hwSym("PostImgRedraw",NULL);
+		HWD.pfnFlushScreenTextures=hwSym("FlushScreenTextures",NULL);
+		HWD.pfnStartScreenWipe  = hwSym("StartScreenWipe",NULL);
+		HWD.pfnEndScreenWipe    = hwSym("EndScreenWipe",NULL);
+		HWD.pfnDoScreenWipe     = hwSym("DoScreenWipe",NULL);
+		HWD.pfnDrawIntermissionBG=hwSym("DrawIntermissionBG",NULL);
+		HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL);
+		HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL);
+		HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL);
+		// check gl renderer lib
+		if (HWD.pfnGetRenderVersion() != VERSION)
+			I_Error("%s", M_GetText("The version of the renderer doesn't match the version of the executable\nBe sure you have installed SRB2 properly.\n"));
+		if (!HWD.pfnInit(I_Error)) // let load the OpenGL library
+			rendermode = render_soft;
+		else
+			glstartup = true;
+	}
+#endif
+}
+
 void I_ShutdownGraphics(void)
 {
 	const rendermode_t oldrendermode = rendermode;
diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
index 9297833e26b3baa3ef726d41f87887643bb47dc1..ab3157c44c5eab916918c6370074c70910cfb901 100644
--- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -1219,7 +1219,7 @@
 		C01FCF4B08A954540054247B /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				CURRENT_PROJECT_VERSION = 2.1.25;
+				CURRENT_PROJECT_VERSION = 2.2.0;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					NORMALSRB2,
@@ -1231,7 +1231,7 @@
 		C01FCF4C08A954540054247B /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
-				CURRENT_PROJECT_VERSION = 2.1.25;
+				CURRENT_PROJECT_VERSION = 2.2.0;
 				GCC_ENABLE_FIX_AND_CONTINUE = NO;
 				GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
 				GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index c4b2f98f4d73dbbe85df4f0ef7933568cc7fcbd2..b60dab14c9e7c0e90bd2afa2b78141df1f01099b 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2014-2018 by Sonic Team Junior.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -9,6 +9,26 @@
 /// \file
 /// \brief SDL Mixer interface for sound
 
+#ifdef HAVE_LIBGME
+#ifdef HAVE_ZLIB
+#ifndef _MSC_VER
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+#endif
+
+#ifndef _LFS64_LARGEFILE
+#define _LFS64_LARGEFILE
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 0
+#endif
+
+#include <zlib.h>
+#endif // HAVE_ZLIB
+#endif // HAVE_LIBGME
+
 #include "../doomdef.h"
 #include "../doomstat.h" // menuactive
 
@@ -57,24 +77,6 @@
 #include "gme/gme.h"
 #define GME_TREBLE 5.0f
 #define GME_BASS 1.0f
-
-#ifdef HAVE_ZLIB
-#ifndef _MSC_VER
-#ifndef _LARGEFILE64_SOURCE
-#define _LARGEFILE64_SOURCE
-#endif
-#endif
-
-#ifndef _LFS64_LARGEFILE
-#define _LFS64_LARGEFILE
-#endif
-
-#ifndef _FILE_OFFSET_BITS
-#define _FILE_OFFSET_BITS 0
-#endif
-
-#include "zlib.h"
-#endif // HAVE_ZLIB
 #endif // HAVE_LIBGME
 
 static UINT16 BUFFERSIZE = 2048;
@@ -1251,7 +1253,7 @@ boolean I_LoadSong(char *data, size_t len)
 
 #ifdef HAVE_OPENMPT
 	/*
-		If the size of the data to be checked is bigger than the recommended size (> 2048)
+		If the size of the data to be checked is bigger than the recommended size (> 2048 bytes)
 		Let's just set the probe size to the recommended size
 		Otherwise let's give it the full data size
 	*/
diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c
index 4347b35b22b2e07682e5ced0e89bd76636366a04..6c0dd35a59bef2ecbd1ca524cd8b474f71608edc 100644
--- a/src/sdl/ogl_sdl.c
+++ b/src/sdl/ogl_sdl.c
@@ -2,6 +2,7 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -221,20 +222,15 @@ void OglSdlFinishUpdate(boolean waitvbl)
 	HWR_DrawScreenFinalTexture(realwidth, realheight);
 }
 
-EXPORT void HWRAPI( OglSdlSetPalette) (RGBA_t *palette, RGBA_t *pgamma)
+EXPORT void HWRAPI( OglSdlSetPalette) (RGBA_t *palette)
 {
-	INT32 i = -1;
-	UINT32 redgamma = pgamma->s.red, greengamma = pgamma->s.green,
-		bluegamma = pgamma->s.blue;
-
-	for (i = 0; i < 256; i++)
+	size_t palsize = (sizeof(RGBA_t) * 256);
+	// on a palette change, you have to reload all of the textures
+	if (memcmp(&myPaletteData, palette, palsize))
 	{
-		myPaletteData[i].s.red   = (UINT8)MIN((palette[i].s.red   * redgamma)  /127, 255);
-		myPaletteData[i].s.green = (UINT8)MIN((palette[i].s.green * greengamma)/127, 255);
-		myPaletteData[i].s.blue  = (UINT8)MIN((palette[i].s.blue  * bluegamma) /127, 255);
-		myPaletteData[i].s.alpha = palette[i].s.alpha;
+		memcpy(&myPaletteData, palette, palsize);
+		Flush();
 	}
-	Flush();
 }
 
 #endif //HWRENDER
diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h
index 2d6209f2bb94c1e28e35e96e949c5ddb641f567c..88d7d0b6cc7c1ebb7671b672cac33981e0e5bc80 100644
--- a/src/sdl/ogl_sdl.h
+++ b/src/sdl/ogl_sdl.h
@@ -2,6 +2,7 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -30,5 +31,5 @@ extern Uint16      realwidth;
 extern Uint16      realheight;
 
 #ifdef _CREATE_DLL_
-EXPORT void HWRAPI( OglSdlSetPalette ) (RGBA_t *palette, RGBA_t *pgamma);
+EXPORT void HWRAPI( OglSdlSetPalette ) (RGBA_t *palette);
 #endif
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index a371c4d374e5f026b611378e7e9f828e91312ddf..b8991293c344b65a8bc63ca356512896e3ffede6 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -2,6 +2,7 @@
 //-----------------------------------------------------------------------------
 //
 // Copyright (C) 1993-1996 by id Software, Inc.
+// Copyright (C) 2014-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -1431,6 +1432,8 @@ static void I_ResumeGME(void)
 
 boolean I_LoadSong(char *data, size_t len)
 {
+	(void)data;
+	(void)len;
 	return false;
 }
 
@@ -1492,6 +1495,7 @@ boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms
 	(void)target_volume;
 	(void)source_volume;
 	(void)ms;
+	(void)callback;
 	return false;
 }
 
@@ -1499,6 +1503,7 @@ boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void))
 {
 	(void)target_volume;
 	(void)ms;
+	(void)callback;
 	return false;
 }
 
diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h
index 810c7dce1f0cbb5a052104671f135cecbf03fa31..bdafebfe9963f51c7b4e70ad0accd26c23f8bfd8 100644
--- a/src/sdl/sdlmain.h
+++ b/src/sdl/sdlmain.h
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*-
 //-----------------------------------------------------------------------------
 //
-// Copyright (C) 2006-2018 by Sonic Team Jr.
+// Copyright (C) 2006-2019 by Sonic Team Junior.
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
diff --git a/src/sounds.c b/src/sounds.c
index ba2402eb9119acf6b348b5d1505bb27cc4f66bf0..720ba851e4a88ed4c6f0ab08b8dfcb904de96082 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -92,9 +92,9 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"pstop",  false, 100,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Crusher stomp"},
   {"steam1", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001
   {"steam2", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001
-  {"wbreak", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Wood breaking"},
+  {"wbreak",  true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Wood breaking"},
   {"ambmac", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Machinery"},
-  {"spsmsh", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Heavy impact"},
+  {"spsmsh",  true,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Heavy impact"},
 
   {"rainin",  true,  24,  4, -1, NULL, 0,        -1,  -1, LUMPERROR, "Rain"},
   {"litng1", false,  16,  2, -1, NULL, 0,        -1,  -1, LUMPERROR, "Lightning"},
@@ -187,6 +187,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"shield", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pity Shield"}, // generic GET!
   {"wirlsg", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Whirlwind Shield"}, // Whirlwind GET!
   {"forcsg", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Force Shield"}, // Force GET!
+  {"frcssg", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Weak Force Shield"}, // Force GET...? (consider making a custom shield with this instead of a single-hit force shield!)
   {"elemsg", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Elemental Shield"}, // Elemental GET!
   {"armasg", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Armageddon Shield"}, // Armaggeddon GET!
   {"attrsg", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Attraction Shield"}, // Attract GET!
@@ -208,7 +209,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"shrpsp",  true,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spincushion"},
   {"shrpgo", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Launch"},
   {"mswarp", false,  60, 16, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spinning out"},
-  {"mspogo", false,  60,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Breaking through"},
+  {"mspogo",  true,  60,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Breaking through"},
   {"boingf", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bouncing"},
   {"corkp",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Cork fired"},
   {"corkh",  false,  32,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Cork hit"},
@@ -220,6 +221,9 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"sprong", false, 112,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Power spring"},
   {"lvfal1",  true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Rumble"},
   {"pscree", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "SCREE!"},
+  {"iceb",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Ice crack"},
+  {"shattr",  true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Shattering"},
+  {"antiri",  true, 255,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Depletion"},
 
   // Menu, interface
   {"chchng", false, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Score"},
@@ -233,6 +237,9 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"wepchg",  true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Weapon change"}, // Weapon switch is identical to menu for now
   {"wtrdng",  true, 212,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Aquaphobia"}, // make sure you can hear the DING DING! Tails 03-08-2000
   {"zelda",  false, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Discovery"},
+  {"adderr",  true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Error"},
+  {"notadd",  true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Reject"},
+  {"addfil",  true,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Accept"},
 
   // NiGHTS
   {"ideya",  false, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Success"},
@@ -427,24 +434,9 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s25e",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"s25f",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
   {"s260",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s261",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s262",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s263",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s264",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s265",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s266",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s267",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s268",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s269",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s26a",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s26b",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s26c",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s26d",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s26e",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s26f",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
-  {"s270",   false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, ""},
 
   // S3&K sounds
+  {"s3k2b",   true, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got Emerald"}, // Got Emerald!
   {"s3k33",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Sparkle"}, // stereo in original game, identical to latter
   {"s3k34",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Sparkle"}, // mono in original game, identical to previous
   {"s3k35",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Hurt"},
@@ -528,7 +520,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3k83",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Collapsing"},
   {"s3k84",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Powering up"},
   {"s3k85",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Powering down"},
-  {"s3k86",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Alarm"},
+  {"s3k86",  false, 128,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Alarm"},
   {"s3k87",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bounce"},
   {"s3k88",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Metallic squeak"},
   {"s3k89",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Advanced tech"},
@@ -566,6 +558,21 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3ka9",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Aquaphobia"},
   {"s3kaa",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Bumper"},
   {"s3kab",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab1", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab2", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab3", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab4", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab5", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab6", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab7", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab8", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kab9", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kaba", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kabb", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kabc", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kabd", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kabe", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
+  {"s3kabf", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Spindash"},
   {"s3kac",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Got Continue"},
   {"s3kad",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "GO!"},
   {"s3kae",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Pinball flipper"},
@@ -604,7 +611,8 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3kc5l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Revving up"}, // ditto
   {"s3kc6s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Orbiting"},
   {"s3kc6l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Orbiting"}, // ditto
-  {"s3kc7",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Aiming"},
+  {"s3kc7s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Aiming"},
+  {"s3kc7l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Aiming"}, // ditto
   {"s3kc8s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Sliding"},
   {"s3kc8l", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Sliding"}, // ditto
   {"s3kc9s", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Swinging"},
diff --git a/src/sounds.h b/src/sounds.h
index d48deb5404d8e2a279e1794ba85e0ff114bdecc8..039349d4f2ee0a84e63bfdc85a29381a0c2d2f39 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -236,6 +236,7 @@ typedef enum
 	sfx_shield,
 	sfx_wirlsg,
 	sfx_forcsg,
+	sfx_frcssg,
 	sfx_elemsg,
 	sfx_armasg,
 	sfx_attrsg,
@@ -269,6 +270,9 @@ typedef enum
 	sfx_sprong,
 	sfx_lvfal1,
 	sfx_pscree,
+	sfx_iceb,
+	sfx_shattr,
+	sfx_antiri,
 
 	// Menu, interface
 	sfx_chchng,
@@ -282,6 +286,9 @@ typedef enum
 	sfx_wepchg,
 	sfx_wtrdng,
 	sfx_zelda,
+	sfx_adderr,
+	sfx_notadd,
+	sfx_addfil,
 
 	// NiGHTS
 	sfx_ideya,
@@ -476,24 +483,9 @@ typedef enum
 	sfx_s25e,
 	sfx_s25f,
 	sfx_s260,
-	sfx_s261,
-	sfx_s262,
-	sfx_s263,
-	sfx_s264,
-	sfx_s265,
-	sfx_s266,
-	sfx_s267,
-	sfx_s268,
-	sfx_s269,
-	sfx_s26a,
-	sfx_s26b,
-	sfx_s26c,
-	sfx_s26d,
-	sfx_s26e,
-	sfx_s26f,
-	sfx_s270,
 
 	// S3&K sounds
+	sfx_s3k2b,
 	sfx_s3k33,
 	sfx_s3k34,
 	sfx_s3k35,
@@ -615,6 +607,21 @@ typedef enum
 	sfx_s3ka9,
 	sfx_s3kaa,
 	sfx_s3kab,
+	sfx_s3kab1,
+	sfx_s3kab2,
+	sfx_s3kab3,
+	sfx_s3kab4,
+	sfx_s3kab5,
+	sfx_s3kab6,
+	sfx_s3kab7,
+	sfx_s3kab8,
+	sfx_s3kab9,
+	sfx_s3kaba,
+	sfx_s3kabb,
+	sfx_s3kabc,
+	sfx_s3kabd,
+	sfx_s3kabe,
+	sfx_s3kabf,
 	sfx_s3kac,
 	sfx_s3kad,
 	sfx_s3kae,
@@ -653,7 +660,8 @@ typedef enum
 	sfx_s3kc5l,
 	sfx_s3kc6s,
 	sfx_s3kc6l,
-	sfx_s3kc7,
+	sfx_s3kc7s,
+	sfx_s3kc7l,
 	sfx_s3kc8s,
 	sfx_s3kc8l,
 	sfx_s3kc9s,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 6f75a25e7ed216876fb3551a5803a1833a45e4c1..9a1505ce3129cd7aa189020632cbba1986f1958b 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -353,12 +353,12 @@ 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)
+	if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_LIFEPIC)
 	{
 		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)
+		if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes > XTRA_LIFEPIC)
 		{
 			sprdef = &skins[skinnum].sprites[SPR2_XTRA|FF_SPR2SUPER];
 			sprframe = &sprdef->spriteframes[0];
@@ -694,34 +694,36 @@ static void ST_drawTime(void)
 	else
 	{
 		// Counting down the hidetime?
-		if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime <= (hidetime*TICRATE)))
+		if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime <= (hidetime*TICRATE)))
 		{
 			tics = (hidetime*TICRATE - stplyr->realtime);
 			if (tics < 3*TICRATE)
 				ST_drawRaceNum(tics);
+			tics += (TICRATE-1); // match the race num
 			downwards = true;
 		}
 		else
 		{
 			// Hidetime finish!
-			if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime < ((hidetime+1)*TICRATE)))
+			if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime < ((hidetime+1)*TICRATE)))
 				ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime);
 
 			// Time limit?
-			if (gametype != GT_COOP && gametype != GT_RACE && gametype != GT_COMPETITION && cv_timelimit.value && timelimitintics > 0)
+			if ((gametyperules & GTR_TIMELIMIT) && cv_timelimit.value && timelimitintics > 0)
 			{
-				if (timelimitintics >= stplyr->realtime)
+				if (timelimitintics > stplyr->realtime)
 				{
-					tics = (timelimitintics + (TICRATE-1) - stplyr->realtime);
+					tics = (timelimitintics - stplyr->realtime);
 					if (tics < 3*TICRATE)
 						ST_drawRaceNum(tics);
+					tics += (TICRATE-1); // match the race num
 				}
 				else // Overtime!
 					tics = 0;
 				downwards = true;
 			}
 			// Post-hidetime normal.
-			else if (gametype == GT_TAG || gametype == GT_HIDEANDSEEK)
+			else if (gametyperules & GTR_STARTCOUNTDOWN)
 				tics = stplyr->realtime - hidetime*TICRATE;
 			// "Shadow! What are you doing? Hurry and get back here
 			// right now before the island blows up with you on it!"
@@ -843,74 +845,18 @@ static void ST_drawLivesArea(void)
 			hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap);
 	}
 
-	// Lives number
+	// Metal Sonic recording
 	if (metalrecording)
 	{
 		if (((2*leveltime)/TICRATE) & 1)
 			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
 				hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC");
 	}
-	else if (G_GametypeUsesLives() || gametype == GT_RACE)
-	{
-		// x
-		V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10,
-			hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
-
-		// lives number
-		if (gametype == GT_RACE)
-		{
-			livescount = INFLIVES;
-			notgreyedout = true;
-		}
-		else if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3)
-		{
-			INT32 i;
-			livescount = 0;
-			notgreyedout = (stplyr->lives > 0);
-			for (i = 0; i < MAXPLAYERS; i++)
-			{
-				if (!playeringame[i])
-					continue;
-
-				if (players[i].lives < 1)
-					continue;
-
-				if (players[i].lives > 1)
-					notgreyedout = true;
-
-				if (players[i].lives == INFLIVES)
-				{
-					livescount = INFLIVES;
-					break;
-				}
-				else if (livescount < 99)
-					livescount += (players[i].lives);
-			}
-		}
-		else
-		{
-			livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
-			notgreyedout = true;
-		}
-
-		if (livescount == INFLIVES)
-			V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
-				'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
-		else
-		{
-			if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
-				livescount++;
-			if (livescount > 99)
-				livescount = 99;
-			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
-				hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
-		}
-	}
 	// Spectator
 	else if (stplyr->spectator)
 		v_colmap = V_GRAYMAP;
 	// Tag
-	else if (gametype == GT_TAG || gametype == GT_HIDEANDSEEK)
+	else if (gametyperules & GTR_TAG)
 	{
 		if (stplyr->pflags & PF_TAGIT)
 		{
@@ -932,11 +878,89 @@ static void ST_drawLivesArea(void)
 			v_colmap = V_BLUEMAP;
 		}
 	}
+	// Lives number
+	else
+	{
+		boolean candrawlives = true;
+
+		// Co-op and Competition, normal life counter
+		if (G_GametypeUsesLives())
+		{
+			// Handle cooplives here
+			if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 3)
+			{
+				INT32 i;
+				livescount = 0;
+				notgreyedout = (stplyr->lives > 0);
+				for (i = 0; i < MAXPLAYERS; i++)
+				{
+					if (!playeringame[i])
+						continue;
+
+					if (players[i].lives < 1)
+						continue;
+
+					if (players[i].lives > 1)
+						notgreyedout = true;
+
+					if (players[i].lives == INFLIVES)
+					{
+						livescount = INFLIVES;
+						break;
+					}
+					else if (livescount < 99)
+						livescount += (players[i].lives);
+				}
+			}
+			else
+			{
+				livescount = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
+				notgreyedout = true;
+			}
+		}
+		// Infinity symbol (Race)
+		else if (G_PlatformGametype() && !(gametyperules & GTR_LIVES))
+		{
+			livescount = INFLIVES;
+			notgreyedout = true;
+		}
+		// Otherwise nothing, sorry.
+		// Special Stages keep not showing lives,
+		// as G_GametypeUsesLives() returns false in
+		// Special Stages, and the infinity symbol
+		// cannot show up because Special Stages
+		// still have the GTR_LIVES gametype rule
+		// by default.
+		else
+			candrawlives = false;
+
+		// Draw the lives counter here.
+		if (candrawlives)
+		{
+			// x
+			V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10, hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
+			if (livescount == INFLIVES)
+				V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
+					'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
+			else
+			{
+				if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
+					livescount++;
+				if (livescount > 99)
+					livescount = 99;
+				V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
+					hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
+			}
+		}
+#undef ST_drawLivesX
+	}
 
 	// 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);
 	else
@@ -1175,21 +1199,33 @@ tic_t lt_exitticker = 0, lt_endtime = 0;
 
 //
 // Load the graphics for the title card.
+// Don't let LJ see this
 //
 static void ST_cacheLevelTitle(void)
 {
-	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_WARNINGTITLE))
-	{
-		lt_patches[0] = (patch_t *)W_CachePatchName("LTACTBLU", PU_HUDGFX);
-		lt_patches[1] = (patch_t *)W_CachePatchName("LTZIGZAG", PU_HUDGFX);
-		lt_patches[2] = (patch_t *)W_CachePatchName("LTZZTEXT", PU_HUDGFX);
-	}
-	else // boss map
-	{
-		lt_patches[0] = (patch_t *)W_CachePatchName("LTACTRED", PU_HUDGFX);
-		lt_patches[1] = (patch_t *)W_CachePatchName("LTZIGRED", PU_HUDGFX);
-		lt_patches[2] = (patch_t *)W_CachePatchName("LTZZWARN", PU_HUDGFX);
-	}
+#define SETPATCH(default, warning, custom, idx) \
+{ \
+	lumpnum_t patlumpnum = LUMPERROR; \
+	if (mapheaderinfo[gamemap-1]->custom[0] != '\0') \
+	{ \
+		patlumpnum = W_CheckNumForName(mapheaderinfo[gamemap-1]->custom); \
+		if (patlumpnum != LUMPERROR) \
+			lt_patches[idx] = (patch_t *)W_CachePatchNum(patlumpnum, PU_HUDGFX); \
+	} \
+	if (patlumpnum == LUMPERROR) \
+	{ \
+		if (!(mapheaderinfo[gamemap-1]->levelflags & LF_WARNINGTITLE)) \
+			lt_patches[idx] = (patch_t *)W_CachePatchName(default, PU_HUDGFX); \
+		else \
+			lt_patches[idx] = (patch_t *)W_CachePatchName(warning, PU_HUDGFX); \
+	} \
+}
+
+	SETPATCH("LTACTBLU", "LTACTRED", ltactdiamond, 0)
+	SETPATCH("LTZIGZAG", "LTZIGRED", ltzzpatch, 1)
+	SETPATCH("LTZZTEXT", "LTZZWARN", ltzztext, 2)
+
+#undef SETPATCH
 }
 
 //
@@ -1214,6 +1250,9 @@ void ST_startTitleCard(void)
 //
 void ST_preDrawTitleCard(void)
 {
+	if (!G_IsTitleCardAvailable())
+		return;
+
 	if (lt_ticker >= (lt_endtime + TICRATE))
 		return;
 
@@ -1229,6 +1268,9 @@ void ST_preDrawTitleCard(void)
 //
 void ST_runTitleCard(void)
 {
+	if (!G_IsTitleCardAvailable())
+		return;
+
 	if (lt_ticker >= (lt_endtime + TICRATE))
 		return;
 
@@ -1282,6 +1324,9 @@ void ST_drawTitleCard(void)
 	INT32 zzticker;
 	patch_t *actpat, *zigzag, *zztext;
 
+	if (!G_IsTitleCardAvailable())
+		return;
+
 #ifdef HAVE_BLUA
 	if (!LUA_HudEnabled(hud_stagetitle))
 		goto luahook;
@@ -1346,16 +1391,12 @@ luahook:
 //
 // Drawer for G_PreLevelTitleCard.
 //
-void ST_preLevelTitleCardDrawer(tic_t ticker, boolean update)
+void ST_preLevelTitleCardDrawer(void)
 {
 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
-	if (ticker < PRELEVELTIME-1)
-		ST_drawWipeTitleCard();
-
+	ST_drawWipeTitleCard();
 	I_OsPolling();
 	I_UpdateNoBlit();
-	if (update)
-		I_FinishUpdate(); // page flip or blit buffer
 }
 
 //
@@ -1764,7 +1805,7 @@ static void ST_drawNiGHTSHUD(void)
 		ST_drawNiGHTSLink();
 	}
 
-	if (gametype == GT_RACE || gametype == GT_COMPETITION)
+	if (gametyperules & GTR_RACE)
 	{
 		ST_drawScore();
 		ST_drawTime();
@@ -2183,7 +2224,7 @@ static void ST_drawTextHUD(void)
 			donef12 = true;
 		}
 	}
-	else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text.
+	else if ((gametyperules & GTR_RESPAWNDELAY) && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text.
 	{
 		INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE;
 
@@ -2205,34 +2246,37 @@ static void ST_drawTextHUD(void)
 
 		if (G_IsSpecialStage(gamemap))
 			textHUDdraw(M_GetText("\x82""Wait for the stage to end..."))
-		else if (gametype == GT_COOP)
+		else if (G_PlatformGametype())
 		{
-			if (stplyr->lives <= 0
-			&& cv_cooplives.value == 2
-			&& (netgame || multiplayer))
+			if (G_GametypeUsesCoopLives())
 			{
-				INT32 i;
-				for (i = 0; i < MAXPLAYERS; i++)
+				if (stplyr->lives <= 0
+				&& cv_cooplives.value == 2
+				&& (netgame || multiplayer))
 				{
-					if (!playeringame[i])
-						continue;
-
-					if (&players[i] == stplyr)
-						continue;
-
-					if (players[i].lives > 1)
-						break;
-					}
-
-				if (i != MAXPLAYERS)
-					textHUDdraw(M_GetText("You'll steal a life on respawn..."))
+					INT32 i;
+					for (i = 0; i < MAXPLAYERS; i++)
+					{
+						if (!playeringame[i])
+							continue;
+
+						if (&players[i] == stplyr)
+							continue;
+
+						if (players[i].lives > 1)
+							break;
+						}
+
+					if (i != MAXPLAYERS)
+						textHUDdraw(M_GetText("You'll steal a life on respawn..."))
+					else
+						textHUDdraw(M_GetText("Wait to respawn..."))
+				}
 				else
 					textHUDdraw(M_GetText("Wait to respawn..."))
 			}
-			else
-				textHUDdraw(M_GetText("Wait to respawn..."))
 		}
-		else
+		else if (G_GametypeHasSpectators())
 			textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game"))
 	}
 
@@ -2275,13 +2319,14 @@ static void ST_drawTextHUD(void)
 			}
 		}
 	}
-	else if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (!stplyr->spectator))
+	else if ((gametyperules & GTR_TAG) && (!stplyr->spectator))
 	{
 		if (leveltime < hidetime * TICRATE)
 		{
 			if (stplyr->pflags & PF_TAGIT)
 			{
-				textHUDdraw(M_GetText("\x82""You are blindfolded!"))
+				if (gametyperules & GTR_BLINDFOLDED)
+					textHUDdraw(M_GetText("\x82""You are blindfolded!"))
 				textHUDdraw(M_GetText("Waiting for players to hide..."))
 			}
 			else if (gametype == GT_HIDEANDSEEK)
@@ -2296,7 +2341,8 @@ static void ST_drawTextHUD(void)
 				textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
 				donef12 = true;
 			}
-			textHUDdraw(M_GetText("You cannot move while hiding."))
+			if (gametyperules & GTR_HIDEFROZEN)
+				textHUDdraw(M_GetText("You cannot move while hiding."))
 		}
 	}
 
@@ -2318,21 +2364,27 @@ static void ST_drawTeamHUD(void)
 	if (F_GetPromptHideHud(0)) // y base is 0
 		return;
 
-	if (gametype == GT_CTF)
+	if (gametyperules & GTR_TEAMFLAGS)
 		p = bflagico;
 	else
 		p = bmatcico;
 
+#ifdef HAVE_BLUA
+	if (LUA_HudEnabled(hud_teamscores))
+#endif
 	V_DrawSmallScaledPatch(BASEVIDWIDTH/2 - SEP - SHORT(p->width)/4, 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, p);
 
-	if (gametype == GT_CTF)
+	if (gametyperules & GTR_TEAMFLAGS)
 		p = rflagico;
 	else
 		p = rmatcico;
 
+#ifdef HAVE_BLUA
+	if (LUA_HudEnabled(hud_teamscores))
+#endif
 	V_DrawSmallScaledPatch(BASEVIDWIDTH/2 + SEP - SHORT(p->width)/4, 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, p);
 
-	if (gametype != GT_CTF)
+	if (!(gametyperules & GTR_TEAMFLAGS))
 		goto num;
 	{
 		INT32 i;
@@ -2341,28 +2393,53 @@ static void ST_drawTeamHUD(void)
 		// Show which flags aren't at base.
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
-			if (players[i].gotflag & GF_BLUEFLAG) // Blue flag isn't at base
+			if (players[i].gotflag & GF_BLUEFLAG // Blue flag isn't at base
+#ifdef HAVE_BLUA
+			&& LUA_HudEnabled(hud_teamscores)
+#endif
+			)
 				V_DrawScaledPatch(BASEVIDWIDTH/2 - SEP - SHORT(nonicon->width)/2, 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon);
-			if (players[i].gotflag & GF_REDFLAG) // Red flag isn't at base
+
+			if (players[i].gotflag & GF_REDFLAG // Red flag isn't at base
+#ifdef HAVE_BLUA
+			&& LUA_HudEnabled(hud_teamscores)
+#endif
+			)
 				V_DrawScaledPatch(BASEVIDWIDTH/2 + SEP - SHORT(nonicon2->width)/2, 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon2);
 
 			whichflag |= players[i].gotflag;
+
 			if ((whichflag & (GF_REDFLAG|GF_BLUEFLAG)) == (GF_REDFLAG|GF_BLUEFLAG))
 				break; // both flags were found, let's stop early
 		}
 
 		// Display a countdown timer showing how much time left until the flag returns to base.
 		{
-			if (blueflag && blueflag->fuse > 1)
+			if (blueflag && blueflag->fuse > 1
+#ifdef HAVE_BLUA
+			&& LUA_HudEnabled(hud_teamscores)
+#endif
+			)
 				V_DrawCenteredString(BASEVIDWIDTH/2 - SEP, 8, V_YELLOWMAP|V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", (blueflag->fuse / TICRATE)));
 
-			if (redflag && redflag->fuse > 1)
+			if (redflag && redflag->fuse > 1
+#ifdef HAVE_BLUA
+			&& LUA_HudEnabled(hud_teamscores)
+#endif
+			)
 				V_DrawCenteredString(BASEVIDWIDTH/2 + SEP, 8, V_YELLOWMAP|V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", (redflag->fuse / TICRATE)));
 		}
 	}
 
 num:
+#ifdef HAVE_BLUA
+	if (LUA_HudEnabled(hud_teamscores))
+#endif
 	V_DrawCenteredString(BASEVIDWIDTH/2 - SEP, 16, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", bluescore));
+
+#ifdef HAVE_BLUA
+	if (LUA_HudEnabled(hud_teamscores))
+#endif
 	V_DrawCenteredString(BASEVIDWIDTH/2 + SEP, 16, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", redscore));
 
 #undef SEP
@@ -2582,7 +2659,7 @@ static void ST_overlayDrawer(void)
 		INT32 i = MAXPLAYERS;
 		INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1));
 
-		if ((gametype == GT_COOP)
+		if (G_GametypeUsesCoopLives()
 		&& (netgame || multiplayer)
 		&& (cv_cooplives.value != 1))
 		{
@@ -2635,7 +2712,7 @@ static void ST_overlayDrawer(void)
 		}
 
 		// If you are in overtime, put a big honkin' flashin' message on the screen.
-		if (G_RingSlingerGametype() && cv_overtime.value
+		if (((gametyperules & GTR_TIMELIMIT) && (gametyperules & GTR_OVERTIME)) && cv_overtime.value
 		&& (leveltime > (timelimitintics + TICRATE/2)) && cv_timelimit.value && (leveltime/TICRATE % 2 == 0))
 			V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_PERPLAYER, M_GetText("OVERTIME!"));
 
@@ -2650,7 +2727,7 @@ static void ST_overlayDrawer(void)
 			ST_drawMatchHUD();
 
 		// Race HUD Stuff
-		if (gametype == GT_RACE || gametype == GT_COMPETITION)
+		if (gametyperules & GTR_RACE)
 			ST_drawRaceHUD();
 
 		// Emerald Hunt Indicators
@@ -2711,6 +2788,9 @@ static void ST_overlayDrawer(void)
 
 void ST_Drawer(void)
 {
+	if (needpatchrecache)
+		R_ReloadHUDGraphics();
+
 #ifdef SEENAMES
 	if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo)
 	{
@@ -2755,7 +2835,7 @@ void ST_Drawer(void)
 		if (rendermode != render_none) ST_doPaletteStuff();
 
 	// Blindfold!
-	if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK)
+	if ((gametyperules & GTR_BLINDFOLDED)
 	&& (leveltime < hidetime * TICRATE))
 	{
 		if (players[displayplayer].pflags & PF_TAGIT)
diff --git a/src/st_stuff.h b/src/st_stuff.h
index 33ffa957a8002f2798e9c3c0fb7459c95d1c197c..e4b21b2b0ee166f37805553df741409b9c8a0bc0 100644
--- a/src/st_stuff.h
+++ b/src/st_stuff.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -52,7 +52,7 @@ void ST_startTitleCard(void);
 void ST_runTitleCard(void);
 void ST_drawTitleCard(void);
 void ST_preDrawTitleCard(void);
-void ST_preLevelTitleCardDrawer(tic_t ticker, boolean update);
+void ST_preLevelTitleCardDrawer(void);
 void ST_drawWipeTitleCard(void);
 
 extern tic_t lt_ticker, lt_lasttic;
diff --git a/src/strcasestr.c b/src/strcasestr.c
index 86c7ec5b0bc02e2519fe3b871d108a89c9514a7a..4ff778bf1495ce60d5f7338ca56dc49af4e8dfcf 100644
--- a/src/strcasestr.c
+++ b/src/strcasestr.c
@@ -23,13 +23,6 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#define SWAP( a, b ) \
-(\
-		(a) ^= (b),\
-		(b) ^= (a),\
-		(a) ^= (b)\
-)
-
 static inline int
 trycmp (char **pp, char *cp,
 		const char *q, size_t qn)
@@ -45,8 +38,16 @@ trycmp (char **pp, char *cp,
 static inline void
 swapp (char ***ppap, char ***ppbp, char **cpap, char **cpbp)
 {
-	SWAP(*(intptr_t *)ppap, *(intptr_t *)ppbp);
-	SWAP(*(intptr_t *)cpap, *(intptr_t *)cpbp);
+	char **pp;
+	char  *p;
+
+	pp    = *ppap;
+	*ppap = *ppbp;
+	*ppbp =  pp;
+
+	p     = *cpap;
+	*cpap = *cpbp;
+	*cpbp =   p;
 }
 
 char *
diff --git a/src/string.c b/src/string.c
index c415e5245a85acf2d57940bc53f7ed4891d46671..dc30529cc23c8a01bada8db4ef0134e54265d0f0 100644
--- a/src/string.c
+++ b/src/string.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by Graue.
-// Copyright (C) 2006-2018 by Sonic Team Junior.
+// Copyright (C) 2006-2019 by Sonic Team Junior.
 // Copyright (C) 2019      by James R.
 //
 // This program is free software distributed under the
diff --git a/src/tables.c b/src/tables.c
index a31572d706183cdf56c0e6e7caa9a143a03c5369..72dfaf0c3a7c2c19aaf9282d5cda068440b8f0f4 100644
--- a/src/tables.c
+++ b/src/tables.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tables.h b/src/tables.h
index 60db2b5a6750a0314f128a14698ed79aba00f073..07e3cdf0998f5ff36db2ec44336ebe089b86cec9 100644
--- a/src/tables.h
+++ b/src/tables.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tmap.nas b/src/tmap.nas
index c72c1890aab39ed44d2739ab2a26f8877cdcdba5..79452ab8c83f4a4b16aa7ae05d7e3da7f1e34266 100644
--- a/src/tmap.nas
+++ b/src/tmap.nas
@@ -1,7 +1,7 @@
 ;; SONIC ROBO BLAST 2
 ;;-----------------------------------------------------------------------------
 ;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2018 by Sonic Team Junior.
+;; Copyright (C) 1999-2019 by Sonic Team Junior.
 ;;
 ;; This program is free software distributed under the
 ;; terms of the GNU General Public License, version 2.
diff --git a/src/tmap.s b/src/tmap.s
index 05c644798f445e2377f6ef92db323913180a6f37..cdd65eee3e20de3e311b193d33dbf69cdf9dd00b 100644
--- a/src/tmap.s
+++ b/src/tmap.s
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tmap_asm.s b/src/tmap_asm.s
index 99cb0b627abe6703ef7ebdc785f93d101ef6140c..067318f308a86f0058dfa7c5f24c83d5b1a88cc9 100644
--- a/src/tmap_asm.s
+++ b/src/tmap_asm.s
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/tmap_mmx.nas b/src/tmap_mmx.nas
index c512de8e9d6b62411157e24941a49b990a3bf1d7..843419c655abdf3349bb685c802de8fe07a31597 100644
--- a/src/tmap_mmx.nas
+++ b/src/tmap_mmx.nas
@@ -1,7 +1,7 @@
 ;; SONIC ROBO BLAST 2
 ;;-----------------------------------------------------------------------------
 ;; Copyright (C) 1998-2000 by DOSDOOM.
-;; Copyright (C) 2010-2018 by Sonic Team Junior.
+;; Copyright (C) 2010-2019 by Sonic Team Junior.
 ;;
 ;; This program is free software distributed under the
 ;; terms of the GNU General Public License, version 2.
diff --git a/src/tmap_vc.nas b/src/tmap_vc.nas
index e943d48d860209200bda8434925e3b45333f1b10..72ed9a530ab1de2055e26374200b0eeb8bfa4257 100644
--- a/src/tmap_vc.nas
+++ b/src/tmap_vc.nas
@@ -1,7 +1,7 @@
 ;; SONIC ROBO BLAST 2
 ;;-----------------------------------------------------------------------------
 ;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2018 by Sonic Team Junior.
+;; Copyright (C) 1999-2019 by Sonic Team Junior.
 ;;
 ;; This program is free software distributed under the
 ;; terms of the GNU General Public License, version 2.
diff --git a/src/v_video.c b/src/v_video.c
index e9237563072df7f8917dd59b446dee57cdacae97..4785a154186c91a8421ce89027a2b9362b0c38d2 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -84,39 +84,6 @@ static CV_PossibleValue_t constextsize_cons_t[] = {
 static void CV_constextsize_OnChange(void);
 consvar_t cv_constextsize = {"con_textsize", "Medium", CV_SAVE|CV_CALL, constextsize_cons_t, CV_constextsize_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
-#ifdef HWRENDER
-static void CV_Gammaxxx_ONChange(void);
-// Saved hardware mode variables
-// - You can change them in software,
-// but they won't do anything.
-static CV_PossibleValue_t grgamma_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}};
-static CV_PossibleValue_t grsoftwarefog_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "LightPlanes"}, {0, NULL}};
-static CV_PossibleValue_t grmodelinterpolation_cons_t[] = {{0, "Off"}, {1, "Sometimes"}, {2, "Always"}, {0, NULL}};
-
-consvar_t cv_grfovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grfog = {"gr_fog", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grfogcolor = {"gr_fogcolor", "AAAAAA", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grsoftwarefog = {"gr_softwarefog", "Off", CV_SAVE, grsoftwarefog_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grgammared = {"gr_gammared", "127", CV_SAVE|CV_CALL, grgamma_cons_t,
-                           CV_Gammaxxx_ONChange, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grgammagreen = {"gr_gammagreen", "127", CV_SAVE|CV_CALL, grgamma_cons_t,
-                             CV_Gammaxxx_ONChange, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grgammablue = {"gr_gammablue", "127", CV_SAVE|CV_CALL, grgamma_cons_t,
-                            CV_Gammaxxx_ONChange, 0, NULL, NULL, 0, 0, NULL};
-#ifdef ALAM_LIGHTING
-consvar_t cv_grdynamiclighting = {"gr_dynamiclighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grstaticlighting  = {"gr_staticlighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grcoronas = {"gr_coronas", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grcoronasize = {"gr_coronasize", "1", CV_SAVE| CV_FLOAT, 0, NULL, 0, NULL, NULL, 0, 0, NULL};
-#endif
-
-consvar_t cv_grmodels = {"gr_models", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grmodelinterpolation = {"gr_modelinterpolation", "Sometimes", CV_SAVE, grmodelinterpolation_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-
-consvar_t cv_grspritebillboarding = {"gr_spritebillboarding", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grskydome = {"gr_skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-#endif
-
 // local copy of the palette for V_GetColor()
 RGBA_t *pLocalPalette = NULL;
 RGBA_t *pMasterPalette = NULL;
@@ -128,6 +95,7 @@ Please check it out if you're trying to maintain this.
 toast 18/04/17
 */
 float Cubepal[2][2][2][3];
+boolean Cubeapply = false;
 
 // returns whether to apply cube, selectively avoiding expensive operations
 static boolean InitCube(void)
@@ -343,11 +311,12 @@ const UINT8 correctiontable[256] =
 // keep a copy of the palette so that we can get the RGB value for a color index at any time.
 static void LoadPalette(const char *lumpname)
 {
-	boolean cube = InitCube();
 	lumpnum_t lumpnum = W_GetNumForName(lumpname);
 	size_t i, palsize = W_LumpLength(lumpnum)/3;
 	UINT8 *pal;
 
+	Cubeapply = InitCube();
+
 	Z_Free(pLocalPalette);
 	Z_Free(pMasterPalette);
 
@@ -369,43 +338,49 @@ static void LoadPalette(const char *lumpname)
 		pMasterPalette[i].s.alpha = pLocalPalette[i].s.alpha = 0xFF;
 
 		// lerp of colour cubing! if you want, make it smoother yourself
-		if (cube)
-		{
-			float working[4][3];
-			float linear;
-			UINT8 q;
+		if (Cubeapply)
+			V_CubeApply(&pLocalPalette[i].s.red, &pLocalPalette[i].s.green, &pLocalPalette[i].s.blue);
+	}
+}
+
+void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue)
+{
+	float working[4][3];
+	float linear;
+	UINT8 q;
+
+	if (!Cubeapply)
+		return;
 
-			linear = (pLocalPalette[i].s.red/255.0);
+	linear = (*red/255.0);
 #define dolerp(e1, e2) ((1 - linear)*e1 + linear*e2)
-			for (q = 0; q < 3; q++)
-			{
-				working[0][q] = dolerp(Cubepal[0][0][0][q], Cubepal[1][0][0][q]);
-				working[1][q] = dolerp(Cubepal[0][1][0][q], Cubepal[1][1][0][q]);
-				working[2][q] = dolerp(Cubepal[0][0][1][q], Cubepal[1][0][1][q]);
-				working[3][q] = dolerp(Cubepal[0][1][1][q], Cubepal[1][1][1][q]);
-			}
-			linear = (pLocalPalette[i].s.green/255.0);
-			for (q = 0; q < 3; q++)
-			{
-				working[0][q] = dolerp(working[0][q], working[1][q]);
-				working[1][q] = dolerp(working[2][q], working[3][q]);
-			}
-			linear = (pLocalPalette[i].s.blue/255.0);
-			for (q = 0; q < 3; q++)
-			{
-				working[0][q] = 255*dolerp(working[0][q], working[1][q]);
-				if (working[0][q] > 255.0)
-					working[0][q] = 255.0;
-				else if (working[0][q]  < 0.0)
-					working[0][q] = 0.0;
-			}
+	for (q = 0; q < 3; q++)
+	{
+		working[0][q] = dolerp(Cubepal[0][0][0][q], Cubepal[1][0][0][q]);
+		working[1][q] = dolerp(Cubepal[0][1][0][q], Cubepal[1][1][0][q]);
+		working[2][q] = dolerp(Cubepal[0][0][1][q], Cubepal[1][0][1][q]);
+		working[3][q] = dolerp(Cubepal[0][1][1][q], Cubepal[1][1][1][q]);
+	}
+	linear = (*green/255.0);
+	for (q = 0; q < 3; q++)
+	{
+		working[0][q] = dolerp(working[0][q], working[1][q]);
+		working[1][q] = dolerp(working[2][q], working[3][q]);
+	}
+	linear = (*blue/255.0);
+	for (q = 0; q < 3; q++)
+	{
+		working[0][q] = 255*dolerp(working[0][q], working[1][q]);
+		if (working[0][q] > 255.0)
+			working[0][q] = 255.0;
+		else if (working[0][q]  < 0.0)
+			working[0][q] = 0.0;
+	}
 #undef dolerp
 
-			pLocalPalette[i].s.red = (UINT8)(working[0][0]);
-			pLocalPalette[i].s.green = (UINT8)(working[0][1]);
-			pLocalPalette[i].s.blue = (UINT8)(working[0][2]);
-		}
-	}
+	*red = (UINT8)(working[0][0]);
+	*green = (UINT8)(working[0][1]);
+	*blue = (UINT8)(working[0][2]);
 }
 
 const char *R_GetPalname(UINT16 num)
@@ -473,16 +448,6 @@ static void CV_palette_OnChange(void)
 	V_SetPalette(0);
 }
 
-// change the palette directly to see the change
-#ifdef HWRENDER
-static void CV_Gammaxxx_ONChange(void)
-{
-	if (rendermode != render_soft && rendermode != render_none)
-		V_SetPalette(0);
-}
-#endif
-
-
 #if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__) && !defined (NORUSEASM)
 void VID_BlitLinearScreen_ASM(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
 	size_t destrowbytes);
@@ -1076,17 +1041,17 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
 //
 void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT8 skincolor)
 {
-	if (skinnum >= 0 && skinnum < numskins && skins[skinnum].sprites[SPR2_XTRA].numframes >= 4)
+	if (skinnum >= 0 && skinnum < numskins && skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_CONTINUE)
 	{
 		spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
 		spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CONTINUE];
-		patch_t *patch = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL);
+		patch_t *patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
 		const UINT8 *colormap = R_GetTranslationColormap(skinnum, skincolor, GTC_CACHE);
 
 		V_DrawMappedPatch(x, y, flags, patch, colormap);
 	}
 	else
-		V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE));
+		V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_PATCH));
 }
 
 //
@@ -1902,14 +1867,15 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color)
 {
 	UINT8 *deststop, *buf;
 
-	boxheight = ((boxheight * 4) + (boxheight/2)*5);
-
 	if (color >= 256 && color < 512)
 	{
+		boxheight = ((boxheight * 4) + (boxheight/2)*5);
 		V_DrawFill((BASEVIDWIDTH-(vid.width/vid.dupx))/2, BASEVIDHEIGHT-boxheight, (vid.width/vid.dupx),boxheight, (color-256)|V_SNAPTOBOTTOM);
 		return;
 	}
 
+	boxheight *= vid.dupy;
+
 	if (color == INT32_MAX)
 		color = cons_backcolor.value;
 
@@ -1951,7 +1917,7 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color)
 	// heavily simplified -- we don't need to know x or y position,
 	// just the start and stop positions
 	deststop = screens[0] + vid.rowbytes * vid.height;
-	buf = deststop - vid.rowbytes * boxheight * vid.dupy; // 4 lines of space plus gaps between and some leeway
+	buf = deststop - vid.rowbytes * ((boxheight * 4) + (boxheight/2)*5); // 4 lines of space plus gaps between and some leeway
 	for (; buf < deststop; ++buf)
 		*buf = promptbgmap[*buf];
 }
@@ -2083,7 +2049,7 @@ char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
 	for (i = 0; i < slen; ++i)
 	{
 		c = newstring[i];
-		if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
+		if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
 			continue;
 
 		if (c == '\n')
@@ -2450,6 +2416,8 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 	fixed_t cx = x, cy = y;
 	INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0;
 	const char *ch = string;
+	INT32 charflags = 0;
+	const UINT8 *colormap = NULL;
 	INT32 spacewidth = 4, charwidth = 0;
 
 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
@@ -2469,6 +2437,8 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 		scrwidth -= left;
 	}
 
+	charflags = (option & V_CHARCOLORMASK);
+
 	switch (option & V_SPACINGMASK)
 	{
 		case V_MONOSPACE:
@@ -2488,7 +2458,12 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 		if (!*ch)
 			break;
 		if (*ch & 0x80) //color ignoring
+		{
+			// manually set flags override color codes
+			if (!(option & V_CHARCOLORMASK))
+				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
 			continue;
+		}
 		if (*ch == '\n')
 		{
 			cx = x;
@@ -2529,7 +2504,8 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
 			continue;
 		}
 
-		V_DrawSciencePatch(cx + (center<<FRACBITS), cy, option, hu_font[c], FRACUNIT);
+		colormap = V_GetStringColormap(charflags);
+		V_DrawFixedPatch(cx + (center<<FRACBITS), cy, FRACUNIT, option, hu_font[c], colormap);
 
 		cx += w<<FRACBITS;
 	}
@@ -3268,6 +3244,29 @@ Unoptimized version
 #endif
 }
 
+// Taken from my videos-in-SRB2 project
+// Generates a color look-up table
+// which has up to 64 colors at each channel
+// (see the defines in v_video.h)
+
+UINT8 colorlookup[CLUTSIZE][CLUTSIZE][CLUTSIZE];
+
+void InitColorLUT(RGBA_t *palette)
+{
+	UINT8 r, g, b;
+	static boolean clutinit = false;
+	static RGBA_t *lastpalette = NULL;
+	if ((!clutinit) || (lastpalette != palette))
+	{
+		for (r = 0; r < CLUTSIZE; r++)
+			for (g = 0; g < CLUTSIZE; g++)
+				for (b = 0; b < CLUTSIZE; b++)
+					colorlookup[r][g][b] = NearestColor(r << SHIFTCOLORBITS, g << SHIFTCOLORBITS, b << SHIFTCOLORBITS);
+		clutinit = true;
+		lastpalette = palette;
+	}
+}
+
 // V_Init
 // old software stuff, buffers are allocated at video mode setup
 // here we set the screens[x] pointers accordingly
@@ -3279,13 +3278,9 @@ void V_Init(void)
 	const INT32 screensize = vid.rowbytes * vid.height;
 
 	LoadMapPalette();
-	// hardware modes do not use screens[] pointers
+
 	for (i = 0; i < NUMSCREENS; i++)
 		screens[i] = NULL;
-	if (rendermode != render_soft)
-	{
-		return; // be sure to cause a NULL read/write error so we detect it, in case of..
-	}
 
 	// start address of NUMSCREENS * width*height vidbuffers
 	if (base)
diff --git a/src/v_video.h b/src/v_video.h
index e3dbb75dc0617c79bec46b43aecadc462c6ad494..eb75a414ff3e31acfd8c48ded41c6d1b94ec35bb 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -37,6 +37,18 @@ cv_allcaps;
 // Allocates buffer screens, call before R_Init.
 void V_Init(void);
 
+// Taken from my videos-in-SRB2 project
+// Generates a color look-up table
+// which has up to 64 colors at each channel
+
+#define COLORBITS 6
+#define SHIFTCOLORBITS (8-COLORBITS)
+#define CLUTSIZE (1<<COLORBITS)
+
+extern UINT8 colorlookup[CLUTSIZE][CLUTSIZE][CLUTSIZE];
+
+void InitColorLUT(RGBA_t *palette);
+
 // Set the current RGB palette lookup to use for palettized graphics
 void V_SetPalette(INT32 palettenum);
 
@@ -48,8 +60,11 @@ const char *GetPalette(void);
 extern RGBA_t *pLocalPalette;
 extern RGBA_t *pMasterPalette;
 
+void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue);
+
 // Retrieve the ARGB value from a palette color index
 #define V_GetColor(color) (pLocalPalette[color&0xFF])
+#define V_GetMasterColor(color) (pMasterPalette[color&0xFF])
 
 // Bottom 8 bits are used for parameter (screen or character)
 #define V_PARAMMASK          0x000000FF
@@ -137,7 +152,7 @@ extern RGBA_t *pMasterPalette;
 #define V_DrawMappedPatch(x,y,s,p,c) V_DrawFixedPatch((x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT, s, p, c)
 #define V_DrawSmallMappedPatch(x,y,s,p,c) V_DrawFixedPatch((x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/2, s, p, c)
 #define V_DrawTinyMappedPatch(x,y,s,p,c) V_DrawFixedPatch((x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/4, s, p, c)
-#define V_DrawScaledPatch(x,y,s,p) V_DrawFixedPatch((x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT, s, p, NULL)
+#define V_DrawScaledPatch(x,y,s,p) V_DrawFixedPatch((x)*FRACUNIT, (y)<<FRACBITS, FRACUNIT, s, p, NULL)
 #define V_DrawSmallScaledPatch(x,y,s,p) V_DrawFixedPatch((x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/2, s, p, NULL)
 #define V_DrawTinyScaledPatch(x,y,s,p) V_DrawFixedPatch((x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT/4, s, p, NULL)
 #define V_DrawTranslucentPatch(x,y,s,p) V_DrawFixedPatch((x)<<FRACBITS, (y)<<FRACBITS, FRACUNIT, s, p, NULL)
diff --git a/src/vid_copy.s b/src/vid_copy.s
index 050a80999c86a29408affa4e017301e2d42fc537..d40510208b12547dc5219feedc670f9184236448 100644
--- a/src/vid_copy.s
+++ b/src/vid_copy.s
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
diff --git a/src/w_wad.c b/src/w_wad.c
index 0cd2175ecf394af5144e5bb75fd94c1842ec9505..1aa2814264935ac92aa3c11d425142fe5795836a 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -11,32 +11,34 @@
 /// \file  w_wad.c
 /// \brief Handles WAD file header, directory, lump I/O
 
-#ifdef __GNUC__
-#include <unistd.h>
+#ifdef HAVE_ZLIB
+#ifndef _MSC_VER
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
 #endif
 
-#define ZWAD
-
-#ifdef ZWAD
-#include <errno.h>
-#include "lzf.h"
+#ifndef _LFS64_LARGEFILE
+#define _LFS64_LARGEFILE
 #endif
 
 #ifndef _FILE_OFFSET_BITS
 #define _FILE_OFFSET_BITS 0
 #endif
 
-#ifndef _LARGEFILE64_SOURCE
-#define _LARGEFILE64_SOURCE
+#include <zlib.h>
 #endif
 
-#ifndef _LFS64_LARGEFILE
-#define _LFS64_LARGEFILE
+#ifdef __GNUC__
+#include <unistd.h>
 #endif
 
-//#ifdef HAVE_ZLIB
-#include "zlib.h"
-//#endif // HAVE_ZLIB
+#define ZWAD
+
+#ifdef ZWAD
+#include <errno.h>
+#include "lzf.h"
+#endif
 
 #include "doomdef.h"
 #include "doomstat.h"
@@ -53,6 +55,7 @@
 #include "dehacked.h"
 #include "d_clisrv.h"
 #include "r_defs.h"
+#include "r_data.h"
 #include "i_system.h"
 #include "md5.h"
 #include "lua_script.h"
@@ -77,24 +80,6 @@ int	snprintf(char *str, size_t n, const char *fmt, ...);
 #define O_BINARY 0
 #endif
 
-#ifdef HAVE_ZLIB
-#ifndef _MSC_VER
-#ifndef _LARGEFILE64_SOURCE
-#define _LARGEFILE64_SOURCE
-#endif
-#endif
-
-#ifndef _LFS64_LARGEFILE
-#define _LFS64_LARGEFILE
-#endif
-
-#ifndef _FILE_OFFSET_BITS
-#define _FILE_OFFSET_BITS 0
-#endif
-
-#include "zlib.h"
-#endif
-
 
 typedef struct
 {
@@ -1229,7 +1214,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 		{
 			size_t bytesread = fread(dest, 1, size, handle);
 			if (R_IsLumpPNG((UINT8 *)dest, bytesread))
-				I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", l->name2, wadfiles[wad]->filename);
+				W_ThrowPNGError(l->name2, wadfiles[wad]->filename);
 			return bytesread;
 		}
 #else
@@ -1270,7 +1255,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 			Z_Free(rawData);
 			Z_Free(decData);
 #ifdef NO_PNG_LUMPS
-			ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->name2);
+			if (R_IsLumpPNG((UINT8 *)dest, size))
+				W_ThrowPNGError(l->name2, wadfiles[wad]->filename);
 #endif
 			return size;
 #else
@@ -1332,7 +1318,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
 			Z_Free(decData);
 
 #ifdef NO_PNG_LUMPS
-			ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->name2);
+			if (R_IsLumpPNG((UINT8 *)dest, size))
+				W_ThrowPNGError(l->name2, wadfiles[wad]->filename);
 #endif
 			return size;
 		}
@@ -1389,7 +1376,6 @@ void *W_CacheLumpNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 
 void *W_CacheLumpNum(lumpnum_t lumpnum, INT32 tag)
 {
-
 	return W_CacheLumpNumPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum),tag);
 }
 
@@ -1502,20 +1488,39 @@ void *W_CacheLumpName(const char *name, INT32 tag)
 // Cache a patch into heap memory, convert the patch format as necessary
 //
 
+void W_FlushCachedPatches(void)
+{
+	if (needpatchflush)
+	{
+		Z_FreeTag(PU_CACHE);
+		Z_FreeTag(PU_PATCH);
+		Z_FreeTag(PU_HUDGFX);
+		Z_FreeTag(PU_HWRPATCHINFO);
+		Z_FreeTag(PU_HWRMODELTEXTURE);
+		Z_FreeTag(PU_HWRCACHE);
+		Z_FreeTags(PU_HWRCACHE_UNLOCKED, PU_HWRPATCHINFO_UNLOCKED);
+	}
+	needpatchflush = false;
+}
+
+// Software-only compile cache the data without conversion
 void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 {
 #ifdef HWRENDER
 	GLPatch_t *grPatch;
 #endif
 
+	if (needpatchflush)
+		W_FlushCachedPatches();
+
 	if (!TestValidLump(wad, lump))
 		return NULL;
 
 #ifdef HWRENDER
 	// Software-only compile cache the data without conversion
 	if (rendermode == render_soft || rendermode == render_none)
-	{
 #endif
+	{
 		lumpcache_t *lumpcache = wadfiles[wad]->patchcache;
 		if (!lumpcache[lump])
 		{
@@ -1549,8 +1554,8 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 			Z_ChangeTag(lumpcache[lump], tag);
 
 		return lumpcache[lump];
-#ifdef HWRENDER
 	}
+#ifdef HWRENDER
 
 	grPatch = HWR_GetCachedGLPatchPwad(wad, lump);
 
@@ -1668,12 +1673,12 @@ void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5)
 	{
 		char actualmd5text[2*MD5_LEN+1];
 		PrintMD5String(wadfiles[wadfilenum]->md5sum, actualmd5text);
-/*#ifdef _DEBUG
+#ifdef _DEBUG
 		CONS_Printf
 #else
 		I_Error
 #endif
-			(M_GetText("File is corrupt or has been modified: %s (found md5: %s, wanted: %s)\n"), wadfiles[wadfilenum]->filename, actualmd5text, matchmd5);*/
+			(M_GetText("File is corrupt or has been modified: %s (found md5: %s, wanted: %s)\n"), wadfiles[wadfilenum]->filename, actualmd5text, matchmd5);
 	}
 #endif
 }
@@ -1886,3 +1891,101 @@ int W_VerifyNMUSlumps(const char *filename)
 	};
 	return W_VerifyFile(filename, NMUSlist, false);
 }
+
+/** \brief Generates a virtual resource used for level data loading.
+ *
+ * \param lumpnum_t reference
+ * \return Virtual resource
+ *
+ */
+virtres_t* vres_GetMap(lumpnum_t lumpnum)
+{
+	UINT32 i;
+	virtres_t* vres = NULL;
+	virtlump_t* vlumps = NULL;
+	size_t numlumps = 0;
+
+	if (W_IsLumpWad(lumpnum))
+	{
+		// Remember that we're assuming that the WAD will have a specific set of lumps in a specific order.
+		UINT8 *wadData = W_CacheLumpNum(lumpnum, PU_LEVEL);
+		filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs);
+		numlumps = ((wadinfo_t *)wadData)->numlumps;
+		vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL);
+
+		// Build the lumps.
+		for (i = 0; i < numlumps; i++)
+		{
+			vlumps[i].size = (size_t)(((filelump_t *)(fileinfo + i))->size);
+			// Play it safe with the name in this case.
+			memcpy(vlumps[i].name, (fileinfo + i)->name, 8);
+			vlumps[i].name[8] = '\0';
+			vlumps[i].data = Z_Malloc(vlumps[i].size, PU_LEVEL, NULL); // This is memory inefficient, sorry about that.
+			memcpy(vlumps[i].data, wadData + (fileinfo + i)->filepos, vlumps[i].size);
+		}
+
+		Z_Free(wadData);
+	}
+	else
+	{
+		// Count number of lumps until the end of resource OR up until next "MAPXX" lump.
+		lumpnum_t lumppos = lumpnum + 1;
+		for (i = LUMPNUM(lumppos); i < wadfiles[WADFILENUM(lumpnum)]->numlumps; i++, lumppos++, numlumps++)
+			if (memcmp(W_CheckNameForNum(lumppos), "MAP", 3) == 0)
+				break;
+		numlumps++;
+
+		vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL);
+		for (i = 0; i < numlumps; i++, lumpnum++)
+		{
+			vlumps[i].size = W_LumpLength(lumpnum);
+			memcpy(vlumps[i].name, W_CheckNameForNum(lumpnum), 8);
+			vlumps[i].name[8] = '\0';
+			vlumps[i].data = W_CacheLumpNum(lumpnum, PU_LEVEL);
+		}
+	}
+	vres = Z_Malloc(sizeof(virtres_t), PU_LEVEL, NULL);
+	vres->vlumps = vlumps;
+	vres->numlumps = numlumps;
+
+	return vres;
+}
+
+/** \brief Frees zone memory for a given virtual resource.
+ *
+ * \param Virtual resource
+ */
+void vres_Free(virtres_t* vres)
+{
+	while (vres->numlumps--)
+		Z_Free(vres->vlumps[vres->numlumps].data);
+	Z_Free(vres->vlumps);
+	Z_Free(vres);
+}
+
+/** (Debug) Prints lumps from a virtual resource into console.
+ */
+/*
+static void vres_Diag(const virtres_t* vres)
+{
+	UINT32 i;
+	for (i = 0; i < vres->numlumps; i++)
+		CONS_Printf("%s\n", vres->vlumps[i].name);
+}
+*/
+
+/** \brief Finds a lump in a given virtual resource.
+ *
+ * \param Virtual resource
+ * \param Lump name to look for
+ * \return Virtual lump if found, NULL otherwise
+ *
+ */
+virtlump_t* vres_Find(const virtres_t* vres, const char* name)
+{
+	UINT32 i;
+	for (i = 0; i < vres->numlumps; i++)
+		if (fastcmp(name, vres->vlumps[i].name))
+			return &vres->vlumps[i];
+	return NULL;
+}
diff --git a/src/w_wad.h b/src/w_wad.h
index 91d4e733e94ecdf1bfbeab2dd73edc71798bc0d1..aca67c00f3df5daafe0bf92a7d6203f147427b19 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -72,6 +72,25 @@ typedef struct
 	compmethod compression; // lump compression method
 } lumpinfo_t;
 
+// =========================================================================
+//                         'VIRTUAL' RESOURCES
+// =========================================================================
+
+typedef struct {
+	char name[9];
+	UINT8* data;
+	size_t size;
+} virtlump_t;
+
+typedef struct {
+	size_t numlumps;
+	virtlump_t* vlumps;
+} virtres_t;
+
+virtres_t* vres_GetMap(lumpnum_t);
+void vres_Free(virtres_t*);
+virtlump_t* vres_Find(const virtres_t*, const char*);
+
 // =========================================================================
 //                         DYNAMIC WAD LOADING
 // =========================================================================
@@ -177,6 +196,7 @@ void *W_CachePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag); // return a patch
 void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag); // return a patch_t
 
 void W_UnlockCachedPatch(void *patch);
+void W_FlushCachedPatches(void);
 
 void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5);
 
diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj
index 7a79861b3899f9fffcdd4da9248556c54e9e7bba..387d65da9074249929ac24044d38942d2cd26f04 100644
--- a/src/win32/Srb2win-vc10.vcxproj
+++ b/src/win32/Srb2win-vc10.vcxproj
@@ -295,6 +295,9 @@
     <ClCompile Include="..\r_draw8.c">
       <ExcludedFromBuild>true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="..\r_draw8_npo2.c">
+      <ExcludedFromBuild>true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="..\r_main.c" />
     <ClCompile Include="..\r_plane.c" />
     <ClCompile Include="..\r_patch.c" />
diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters
index 3d5c021d4b915f1950b831d2f67ec2a6a3ac78ba..2f380c47343f8a15be0f02393970086af4b74b40 100644
--- a/src/win32/Srb2win-vc10.vcxproj.filters
+++ b/src/win32/Srb2win-vc10.vcxproj.filters
@@ -369,6 +369,9 @@
     <ClCompile Include="..\r_draw16.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
+    <ClCompile Include="..\r_draw8_npo2.c">
+      <Filter>R_Rend</Filter>
+    </ClCompile>
     <ClCompile Include="..\r_main.c">
       <Filter>R_Rend</Filter>
     </ClCompile>
diff --git a/src/win32/win_dll.c b/src/win32/win_dll.c
index 5378bb52f645bc10abc0c58dcf27d38f15d9b85a..986eb32dcdf2af6b6bd971452ac445677582b01f 100644
--- a/src/win32/win_dll.c
+++ b/src/win32/win_dll.c
@@ -98,7 +98,7 @@ static loadfunc_t hwdFuncTable[] = {
 	{"Init@4",              &hwdriver.pfnInit},
 	{"Shutdown@0",          &hwdriver.pfnShutdown},
 	{"GetModeList@8",       &hwdriver.pfnGetModeList},
-	{"SetPalette@8",        &hwdriver.pfnSetPalette},
+	{"SetPalette@4",        &hwdriver.pfnSetPalette},
 	{"FinishUpdate@4",      &hwdriver.pfnFinishUpdate},
 	{"Draw2DLine@12",       &hwdriver.pfnDraw2DLine},
 	{"DrawPolygon@16",      &hwdriver.pfnDrawPolygon},
@@ -110,7 +110,7 @@ static loadfunc_t hwdFuncTable[] = {
 	{"GClipRect@20",        &hwdriver.pfnGClipRect},
 	{"ClearMipMapCache@0",  &hwdriver.pfnClearMipMapCache},
 	{"SetSpecialState@8",   &hwdriver.pfnSetSpecialState},
-	{"DrawModel@16",          &hwdriver.pfnDrawModel},
+	{"DrawModel@16",        &hwdriver.pfnDrawModel},
 	{"SetTransform@4",      &hwdriver.pfnSetTransform},
 	{"GetTextureUsed@0",    &hwdriver.pfnGetTextureUsed},
 	{"GetRenderVersion@0",  &hwdriver.pfnGetRenderVersion},
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index c9fdb1c9742fa87a803aed9e88fb1578aceac904..42733c30909897d2437387dbe3b6365a096b40d8 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -1354,6 +1354,8 @@ getBufferedData:
 	}
 }
 
+void I_UpdateMouseGrab(void) {}
+
 // ===========================================================================================
 //                                                                       DIRECT INPUT JOYSTICK
 // ===========================================================================================
diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c
index 39a60cd93b936ba3e524d18c4614a8834128a8a4..d0aab92b3e16a4d13fb4cb57039869a732bfaef1 100644
--- a/src/win32/win_vid.c
+++ b/src/win32/win_vid.c
@@ -239,6 +239,11 @@ void I_StartupGraphics(void)
 	if (!dedicated) graphics_started = true;
 }
 
+void I_StartupHardwareGraphics(void)
+{
+	// oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo yeah oh yeah woo y
+}
+
 // ------------------
 // I_ShutdownGraphics
 // Close the screen, restore previous video mode.
@@ -946,6 +951,11 @@ INT32 VID_SetMode(INT32 modenum)
 	return 1;
 }
 
+void VID_CheckRenderer(void)
+{
+	// ..............
+}
+
 // ========================================================================
 // Free the video buffer of the last video mode,
 // allocate a new buffer for the video mode to set.
diff --git a/src/y_inter.c b/src/y_inter.c
index 32548d26369e7c7afe1509ef5b7213045c9dbaaa..214dee92ee354e0ab236a6242047545a57be0f40 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2004-2018 by Sonic Team Junior.
+// Copyright (C) 2004-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -37,6 +37,10 @@
 #include "m_cond.h" // condition sets
 #include "lua_hook.h" // IntermissionThinker hook
 
+#ifdef HAVE_BLUA
+#include "lua_hud.h"
+#endif
+
 #ifdef HWRENDER
 #include "hardware/hw_main.h"
 #endif
@@ -157,6 +161,7 @@ typedef struct
 
 boolean usebuffer = false;
 static boolean useinterpic;
+static boolean safetorender = true;
 static y_buffer_t *y_buffer;
 
 static INT32 intertic;
@@ -164,6 +169,7 @@ static INT32 tallydonetic = -1;
 static INT32 endtic = -1;
 
 intertype_t intertype = int_none;
+intertype_t intermissiontypes[NUMGAMETYPES];
 
 static void Y_RescaleScreenBuffer(void);
 static void Y_AwardCoopBonuses(void);
@@ -172,6 +178,7 @@ static void Y_CalculateCompetitionWinners(void);
 static void Y_CalculateTimeRaceWinners(void);
 static void Y_CalculateMatchWinners(void);
 static void Y_UnloadData(void);
+static void Y_CleanupData(void);
 
 // Stuff copy+pasted from st_stuff.c
 #define ST_DrawNumFromHud(h,n)        V_DrawTallNum(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f, n)
@@ -318,12 +325,31 @@ void Y_IntermissionDrawer(void)
 	// Bonus loops
 	INT32 i;
 
-	if (intertype == int_none || rendermode == render_none)
+	if (rendermode == render_none)
 		return;
 
+	if (intertype == int_none)
+	{
+#ifdef HAVE_BLUA
+		LUAh_IntermissionHUD();
+#endif
+		return;
+	}
+
 	if (!usebuffer)
+	// Lactozilla: Renderer switching
+	if (needpatchrecache)
+	{
+		Y_CleanupData();
+		safetorender = false;
+	}
+
+	if (!usebuffer || !safetorender)
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
+	if (!safetorender)
+		goto dontdrawbg;
+
 	if (useinterpic)
 		V_DrawScaledPatch(0, 0, 0, interpic);
 	else if (!usetile)
@@ -358,6 +384,13 @@ void Y_IntermissionDrawer(void)
 	else
 		V_DrawPatchFill(bgtile);
 
+#ifdef HAVE_BLUA
+	LUAh_IntermissionHUD();
+	if (!LUA_HudEnabled(hud_intermissiontally))
+		goto skiptallydrawer;
+#endif
+
+dontdrawbg:
 	if (intertype == int_coop)
 	{
 		INT32 bonusy;
@@ -407,14 +440,17 @@ void Y_IntermissionDrawer(void)
 
 		bonusy = 150;
 		// Total
-		V_DrawScaledPatch(152, bonusy, 0, data.coop.ptotal);
-		V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.total);
+		if (safetorender)
+		{
+			V_DrawScaledPatch(152, bonusy, 0, data.coop.ptotal);
+			V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.total);
+		}
 		bonusy -= (3*SHORT(tallnum[0]->height)/2) + 1;
 
 		// Draw bonuses
 		for (i = 3; i >= 0; --i)
 		{
-			if (data.coop.bonuses[i].display)
+			if (data.coop.bonuses[i].display && safetorender)
 			{
 				V_DrawScaledPatch(152, bonusy, 0, data.coop.bonuspatches[i]);
 				V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.bonuses[i].points);
@@ -603,8 +639,7 @@ void Y_IntermissionDrawer(void)
 					{
 						if (emeraldbounces < 3)
 						{
-							emeraldmomy += 1;
-							emeraldy += emeraldmomy;
+							emeraldy += (++emeraldmomy);
 							if (emeraldy > 74)
 							{
 								S_StartSound(NULL, sfx_tink); // tink
@@ -616,9 +651,11 @@ void Y_IntermissionDrawer(void)
 					}
 					else
 					{
-						emeraldmomy += 1;
-						emeraldy += emeraldmomy;
-						emeraldx += intertic - 6;
+						if (emeraldy < (vid.height/vid.dupy)+16)
+						{
+							emeraldy += (++emeraldmomy);
+							emeraldx += intertic - 6;
+						}
 						if (emeraldbounces < 1 && emeraldy > 74)
 						{
 							S_StartSound(NULL, sfx_shldls); // nope
@@ -642,7 +679,8 @@ void Y_IntermissionDrawer(void)
 		char strtime[10];
 
 		// draw the header
-		V_DrawScaledPatch(112, 2, 0, data.match.result);
+		if (safetorender)
+			V_DrawScaledPatch(112, 2, 0, data.match.result);
 
 		// draw the level name
 		V_DrawCenteredString(BASEVIDWIDTH/2, 20, 0, data.match.levelstring);
@@ -906,6 +944,12 @@ void Y_IntermissionDrawer(void)
 		}
 	}
 
+#ifdef HAVE_BLUA
+skiptallydrawer:
+	if (!LUA_HudEnabled(hud_intermissionmessages))
+		return;
+#endif
+
 	if (timer)
 		V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP,
 			va("start in %d seconds", timer/TICRATE));
@@ -1168,6 +1212,8 @@ void Y_StartIntermission(void)
 		I_Error("endtic is dirty");
 #endif
 
+	safetorender = true;
+
 	if (!multiplayer)
 	{
 		timer = 0;
@@ -1186,7 +1232,9 @@ void Y_StartIntermission(void)
 				timer = 1;
 		}
 
-		if (gametype == GT_COOP)
+		if (intermissiontypes[gametype] != int_none)
+			intertype = intermissiontypes[gametype];
+		else if (gametype == GT_COOP)
 			intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop;
 		else if (gametype == GT_TEAMMATCH)
 			intertype = int_teammatch;
@@ -1221,20 +1269,20 @@ void Y_StartIntermission(void)
 			data.coop.tics = players[consoleplayer].realtime;
 
 			for (i = 0; i < 4; ++i)
-				data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_STATIC);
-			data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC);
+				data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_PATCH);
+			data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_PATCH);
 
 			// get act number
 			data.coop.actnum = mapheaderinfo[gamemap-1]->actnum;
 
 			// get background patches
-			widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC);
-			bgpatch = W_CachePatchName("INTERSCR", PU_STATIC);
+			widebgpatch = W_CachePatchName("INTERSCW", PU_PATCH);
+			bgpatch = W_CachePatchName("INTERSCR", PU_PATCH);
 
 			// grab an interscreen if appropriate
 			if (mapheaderinfo[gamemap-1]->interscreen[0] != '#')
 			{
-				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_STATIC);
+				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH);
 				useinterpic = true;
 				usebuffer = false;
 			}
@@ -1292,18 +1340,18 @@ void Y_StartIntermission(void)
 			Y_AwardSpecialStageBonus();
 
 			for (i = 0; i < 2; ++i)
-				data.spec.bonuspatches[i] = W_CachePatchName(data.spec.bonuses[i].patch, PU_STATIC);
+				data.spec.bonuspatches[i] = W_CachePatchName(data.spec.bonuses[i].patch, PU_PATCH);
 
-			data.spec.pscore = W_CachePatchName("YB_SCORE", PU_STATIC);
-			data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_STATIC);
+			data.spec.pscore = W_CachePatchName("YB_SCORE", PU_PATCH);
+			data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_PATCH);
 
 			// get background tile
-			bgtile = W_CachePatchName("SPECTILE", PU_STATIC);
+			bgtile = W_CachePatchName("SPECTILE", PU_PATCH);
 
 			// grab an interscreen if appropriate
 			if (mapheaderinfo[gamemap-1]->interscreen[0] != '#')
 			{
-				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_STATIC);
+				interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH);
 				useinterpic = true;
 			}
 			else
@@ -1315,14 +1363,14 @@ void Y_StartIntermission(void)
 			// get special stage specific patches
 /*			if (!stagefailed && ALL7EMERALDS(emeralds))
 			{
-				data.spec.cemerald = W_CachePatchName("GOTEMALL", PU_STATIC);
+				data.spec.cemerald = W_CachePatchName("GOTEMALL", PU_PATCH);
 				data.spec.headx = 70;
 				data.spec.nowsuper = players[consoleplayer].skin
-					? NULL : W_CachePatchName("NOWSUPER", PU_STATIC);
+					? NULL : W_CachePatchName("NOWSUPER", PU_PATCH);
 			}
 			else
 			{
-				data.spec.cemerald = W_CachePatchName("CEMERALD", PU_STATIC);
+				data.spec.cemerald = W_CachePatchName("CEMERALD", PU_PATCH);
 				data.spec.headx = 48;
 				data.spec.nowsuper = NULL;
 			} */
@@ -1400,9 +1448,9 @@ void Y_StartIntermission(void)
 
 			// get RESULT header
 			data.match.result =
-				W_CachePatchName("RESULT", PU_STATIC);
+				W_CachePatchName("RESULT", PU_PATCH);
 
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
@@ -1428,9 +1476,9 @@ void Y_StartIntermission(void)
 			data.match.levelstring[sizeof data.match.levelstring - 1] = '\0';
 
 			// get RESULT header
-			data.match.result = W_CachePatchName("RESULT", PU_STATIC);
+			data.match.result = W_CachePatchName("RESULT", PU_PATCH);
 
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
@@ -1467,7 +1515,7 @@ void Y_StartIntermission(void)
 				data.match.blueflag = bmatcico;
 			}
 
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
@@ -1493,7 +1541,7 @@ void Y_StartIntermission(void)
 			data.competition.levelstring[sizeof data.competition.levelstring - 1] = '\0';
 
 			// get background tile
-			bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
+			bgtile = W_CachePatchName("SRB2BACK", PU_PATCH);
 			usetile = true;
 			useinterpic = false;
 			break;
@@ -1622,11 +1670,14 @@ static void Y_CalculateCompetitionWinners(void)
 		for (j = 0; j < 5; j++)
 			bestat[j] = true;
 
+		if ((players[i].pflags & PF_GAMETYPEOVER) || players[i].lives <= 0)
+			players[i].rings = 0;
+
 		times[i]    = players[i].realtime;
 		rings[i]    = (UINT32)max(players[i].rings, 0);
 		maxrings[i] = (UINT32)players[i].totalring;
 		monitors[i] = (UINT32)players[i].numboxes;
-		scores[i]   = (UINT32)min(players[i].score, 99999990);
+		scores[i]   = (UINT32)min(players[i].score, MAXSCORE);
 
 		for (j = 0; j < MAXPLAYERS; j++)
 		{
@@ -1946,7 +1997,7 @@ static void Y_AwardCoopBonuses(void)
 
 		if (i == consoleplayer)
 		{
-			data.coop.gotlife = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0 : ptlives);
+			data.coop.gotlife = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? 0 : ptlives);
 			M_Memcpy(&data.coop.bonuses, &localbonuses, sizeof(data.coop.bonuses));
 		}
 	}
@@ -2001,7 +2052,7 @@ static void Y_AwardSpecialStageBonus(void)
 
 		if (i == consoleplayer)
 		{
-			data.spec.gotlife = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0 : ptlives);
+			data.spec.gotlife = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? 0 : ptlives);
 			M_Memcpy(&data.spec.bonuses, &localbonuses, sizeof(data.spec.bonuses));
 
 			// Continues related
@@ -2028,7 +2079,8 @@ void Y_EndIntermission(void)
 	usebuffer = false;
 }
 
-#define UNLOAD(x) Z_ChangeTag(x, PU_CACHE); x = NULL
+#define UNLOAD(x) if (x) {Z_ChangeTag(x, PU_CACHE);} x = NULL;
+#define CLEANUP(x) x = NULL;
 
 //
 // Y_UnloadData
@@ -2079,5 +2131,47 @@ static void Y_UnloadData(void)
 			//are not handled
 			break;
 	}
+}
+
+static void Y_CleanupData(void)
+{
+	// unload the background patches
+	CLEANUP(bgpatch);
+	CLEANUP(widebgpatch);
+	CLEANUP(bgtile);
+	CLEANUP(interpic);
 
+	switch (intertype)
+	{
+		case int_coop:
+			// unload the coop and single player patches
+			CLEANUP(data.coop.bonuspatches[3]);
+			CLEANUP(data.coop.bonuspatches[2]);
+			CLEANUP(data.coop.bonuspatches[1]);
+			CLEANUP(data.coop.bonuspatches[0]);
+			CLEANUP(data.coop.ptotal);
+			break;
+		case int_spec:
+			// unload the special stage patches
+			//CLEANUP(data.spec.cemerald);
+			//CLEANUP(data.spec.nowsuper);
+			CLEANUP(data.spec.bonuspatches[1]);
+			CLEANUP(data.spec.bonuspatches[0]);
+			CLEANUP(data.spec.pscore);
+			CLEANUP(data.spec.pcontinues);
+			break;
+		case int_match:
+		case int_race:
+			CLEANUP(data.match.result);
+			break;
+		case int_ctf:
+			CLEANUP(data.match.blueflag);
+			CLEANUP(data.match.redflag);
+			break;
+		default:
+			//without this default,
+			//int_none, int_tag, int_chaos, and int_classicrace
+			//are not handled
+			break;
+	}
 }
diff --git a/src/y_inter.h b/src/y_inter.h
index b47f3b157e8d3eb0b03ca408c0818aa873b97a81..5cf2cc1b84ee8c9ac75923cac9fc9e99481e5e6f 100644
--- a/src/y_inter.h
+++ b/src/y_inter.h
@@ -1,6 +1,6 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
-// Copyright (C) 2004-2018 by Sonic Team Junior.
+// Copyright (C) 2004-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -31,3 +31,4 @@ typedef enum
 	int_comp,     // Competition
 } intertype_t;
 extern intertype_t intertype;
+extern intertype_t intermissiontypes[NUMGAMETYPES];
diff --git a/src/z_zone.c b/src/z_zone.c
index d5146f7a3a6ee7d8346ae2180421832455aee9c2..5a0ff638bfd7e09512af622abf46d66565251029 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -1,7 +1,7 @@
 // SONIC ROBO BLAST 2
 //-----------------------------------------------------------------------------
 // Copyright (C) 2006      by Graue.
-// Copyright (C) 2006-2018 by Sonic Team Junior.
+// Copyright (C) 2006-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -27,6 +27,7 @@
 
 #include "doomdef.h"
 #include "doomstat.h"
+#include "r_patch.h"
 #include "i_system.h" // I_GetFreeMem
 #include "i_video.h" // rendermode
 #include "z_zone.h"
@@ -497,6 +498,35 @@ void Z_FreeTags(INT32 lowtag, INT32 hightag)
 // Utility functions
 // -----------------
 
+// for renderer switching, free a bunch of stuff
+boolean needpatchflush = false;
+boolean needpatchrecache = false;
+
+// flush all patches from memory
+// (also frees memory tagged with PU_CACHE)
+// (which are not necessarily patches but I don't care)
+void Z_FlushCachedPatches(void)
+{
+	CONS_Debug(DBG_RENDER, "Z_FlushCachedPatches()...\n");
+	Z_FreeTag(PU_CACHE);
+	Z_FreeTag(PU_PATCH);
+	Z_FreeTag(PU_HUDGFX);
+	Z_FreeTag(PU_HWRPATCHINFO);
+	Z_FreeTag(PU_HWRMODELTEXTURE);
+	Z_FreeTag(PU_HWRCACHE);
+	Z_FreeTag(PU_HWRCACHE_UNLOCKED);
+	Z_FreeTag(PU_HWRPATCHINFO_UNLOCKED);
+}
+
+// happens before a renderer switch
+void Z_PreparePatchFlush(void)
+{
+	CONS_Debug(DBG_RENDER, "Z_PreparePatchFlush()...\n");
+#ifdef ROTSPRITE
+	R_FreeAllRotSprite();
+#endif
+}
+
 // starting value of nextcleanup
 #define CLEANUPCOUNT 2000
 
diff --git a/src/z_zone.h b/src/z_zone.h
index 8d32e74f11a87466bab95d2e507f9cf37b03b6ac..9e5f74343aa0b009956ad13af57fa8e01be089a0 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -2,7 +2,7 @@
 //-----------------------------------------------------------------------------
 // Copyright (C) 1993-1996 by id Software, Inc.
 // Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2018 by Sonic Team Junior.
+// Copyright (C) 1999-2019 by Sonic Team Junior.
 //
 // This program is free software distributed under the
 // terms of the GNU General Public License, version 2.
@@ -43,9 +43,11 @@ enum
 	PU_SOUND                 = 11, // static while playing
 	PU_MUSIC                 = 12, // static while playing
 	PU_HUDGFX                = 13, // static until WAD added
+	PU_PATCH                 = 14, // static until renderer change
 
 	PU_HWRPATCHINFO          = 21, // Hardware GLPatch_t struct for OpenGL texture cache
 	PU_HWRPATCHCOLMIPMAP     = 22, // Hardware GLMipmap_t struct colormap variation of patch
+	PU_HWRMODELTEXTURE       = 23, // Hardware model texture
 
 	PU_HWRCACHE              = 48, // static until unlocked
 	PU_CACHE                 = 49, // static until unlocked
@@ -141,4 +143,10 @@ size_t Z_TagsUsage(INT32 lowtag, INT32 hightag);
 char *Z_StrDup(const char *in);
 #define Z_Unlock(p) (void)p // TODO: remove this now that NDS code has been removed
 
+// For renderer switching
+extern boolean needpatchflush;
+extern boolean needpatchrecache;
+void Z_FlushCachedPatches(void);
+void Z_PreparePatchFlush(void);
+
 #endif