[trigger-rally] 03/14: New upstream version 0.6.5+dfsg

Markus Koschany apo at moszumanska.debian.org
Mon Dec 19 14:25:40 UTC 2016


This is an automated email from the git hooks/post-receive script.

apo pushed a commit to branch master
in repository trigger-rally.

commit 1821951bce70160d6c32b2fd0e3962d1849789bd
Author: Markus Koschany <apo at debian.org>
Date:   Mon Dec 19 14:17:04 2016 +0100

    New upstream version 0.6.5+dfsg
---
 bin/trigger-rally.config.defs                |  10 +-
 data/data.md5                                |   2 +-
 data/data.zip                                | Bin 69045256 -> 84336391 bytes
 data/defplayers/AB.player                    | Bin 21419 -> 0 bytes
 data/icon/trigger-128.png                    | Bin 14902 -> 14903 bytes
 data/icon/trigger-16.png                     | Bin 577 -> 503 bytes
 data/icon/trigger-192.png                    | Bin 24546 -> 24621 bytes
 data/icon/trigger-22.png                     | Bin 864 -> 790 bytes
 data/icon/trigger-24.png                     | Bin 1025 -> 951 bytes
 data/icon/trigger-256.png                    | Bin 34438 -> 34579 bytes
 data/icon/trigger-32.png                     | Bin 2123 -> 2049 bytes
 data/icon/trigger-36.png                     | Bin 2764 -> 2690 bytes
 data/icon/trigger-48.png                     | Bin 4147 -> 4074 bytes
 data/icon/trigger-64.png                     | Bin 6200 -> 6137 bytes
 data/icon/trigger-72.png                     | Bin 7281 -> 7224 bytes
 data/icon/trigger-96.png                     | Bin 10459 -> 10445 bytes
 doc/BUILDING.txt                             | 391 +++++------------------
 doc/DATA_AUTHORS.txt                         |  42 ++-
 src/GNUmakefile                              |  20 +-
 src/GNUmakefile.MSYS                         |  62 ++--
 src/{GNUmakefile.MSYS => GNUmakefile.MSYS64} |  79 ++---
 src/PEngine/app.cpp                          | 131 ++++++--
 src/PEngine/model.cpp                        |   2 +-
 src/PEngine/physfs_rw.cpp                    |  22 +-
 src/PEngine/terrain.cpp                      | 453 +++++++++++++++++++--------
 src/PEngine/texture.cpp                      |   2 +-
 src/PEngine/util.cpp                         | 170 +++++-----
 src/PSim/sim.cpp                             |  28 +-
 src/PSim/vehicle.cpp                         |  14 +-
 src/Trigger/game.cpp                         |   8 +-
 src/Trigger/main.cpp                         | 333 +++++++++++++-------
 src/Trigger/menu.cpp                         |   4 +-
 src/Trigger/render.cpp                       |   4 +-
 src/TriggerRally.dev                         | 442 --------------------------
 src/TriggerRally.mak                         |  46 ---
 src/_clean.cmd                               |   4 -
 src/include/app.h                            |  39 ++-
 src/include/codriver.h                       |   5 +-
 src/include/hiscore1.h                       |  19 +-
 src/include/main.h                           | 165 +++++-----
 src/include/pengine.h                        |  46 +--
 src/include/psim.h                           |  36 +--
 src/include/render.h                         | 142 ++++++---
 43 files changed, 1235 insertions(+), 1486 deletions(-)

diff --git a/bin/trigger-rally.config.defs b/bin/trigger-rally.config.defs
index d7f0a78..5d27589 100644
--- a/bin/trigger-rally.config.defs
+++ b/bin/trigger-rally.config.defs
@@ -45,7 +45,7 @@
 		stereoswapeyes="no"
 		/>
 		<!-- Automatic video mode ("automatic"):
-			yes - ignore user values and run fullscreen at native resolution and color depth
+			yes - ignore user values and run fullscreen at desktop resolution and color depth
 			no - run with user-provided values for width, height and bpp
 		-->
 		<!-- Possible values for stereo:
@@ -71,6 +71,7 @@
 	<graphics
 		anisotropy="4"
 		foliage="yes"
+		roadsigns="yes"
 		weather="yes"
 		snowflaketype="textured"
 		dirteffect="yes"
@@ -89,6 +90,11 @@
 			"yes" means that bushes, grass and trees will be displayed.
 			"no" means that they won't be.
 
+		roadsigns:
+			Turns road signs on and off.
+			"yes" means that road signs and other sprites will be displayed.
+			"no" means that they won't be.
+
 		weather:
 			Turns weather effects on and off.
 			"yes" means that rain and snowfall will be displayed.
@@ -177,7 +183,7 @@
 			<key action="next" id="SDLK_COMMA" />
 		</keyboard>
 		<!-- For a list of SDLKey ID's visit:
-			http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlkey.html
+			http://wiki.libsdl.org/SDL_Keycode
 		-->
 
 		<!-- Typical joystick or wheel & pedals configuration -->
diff --git a/data/data.md5 b/data/data.md5
index 3974432..7d5d07c 100644
--- a/data/data.md5
+++ b/data/data.md5
@@ -1 +1 @@
-c074582417b5087d010135939154f119 *data.zip
+f47671e43c5e88d4a953366c7f4f98fc *data.zip
diff --git a/data/data.zip b/data/data.zip
index d3f6a3f..cd53c07 100644
Binary files a/data/data.zip and b/data/data.zip differ
diff --git a/data/defplayers/AB.player b/data/defplayers/AB.player
deleted file mode 100644
index f9c82da..0000000
Binary files a/data/defplayers/AB.player and /dev/null differ
diff --git a/data/icon/trigger-128.png b/data/icon/trigger-128.png
index 8b19af6..4241c43 100644
Binary files a/data/icon/trigger-128.png and b/data/icon/trigger-128.png differ
diff --git a/data/icon/trigger-16.png b/data/icon/trigger-16.png
index e4686af..a077277 100644
Binary files a/data/icon/trigger-16.png and b/data/icon/trigger-16.png differ
diff --git a/data/icon/trigger-192.png b/data/icon/trigger-192.png
index 7706a3c..57a984d 100644
Binary files a/data/icon/trigger-192.png and b/data/icon/trigger-192.png differ
diff --git a/data/icon/trigger-22.png b/data/icon/trigger-22.png
index b206641..ca367c9 100644
Binary files a/data/icon/trigger-22.png and b/data/icon/trigger-22.png differ
diff --git a/data/icon/trigger-24.png b/data/icon/trigger-24.png
index 56da411..c619ac4 100644
Binary files a/data/icon/trigger-24.png and b/data/icon/trigger-24.png differ
diff --git a/data/icon/trigger-256.png b/data/icon/trigger-256.png
index 406caa5..2ee11fd 100644
Binary files a/data/icon/trigger-256.png and b/data/icon/trigger-256.png differ
diff --git a/data/icon/trigger-32.png b/data/icon/trigger-32.png
index 7526c0e..8da77e2 100644
Binary files a/data/icon/trigger-32.png and b/data/icon/trigger-32.png differ
diff --git a/data/icon/trigger-36.png b/data/icon/trigger-36.png
index d209129..8a7b0ba 100644
Binary files a/data/icon/trigger-36.png and b/data/icon/trigger-36.png differ
diff --git a/data/icon/trigger-48.png b/data/icon/trigger-48.png
index 86d3cc4..9385b13 100644
Binary files a/data/icon/trigger-48.png and b/data/icon/trigger-48.png differ
diff --git a/data/icon/trigger-64.png b/data/icon/trigger-64.png
index 89bb650..3ce0663 100644
Binary files a/data/icon/trigger-64.png and b/data/icon/trigger-64.png differ
diff --git a/data/icon/trigger-72.png b/data/icon/trigger-72.png
index 2594e5b..5f8bf89 100644
Binary files a/data/icon/trigger-72.png and b/data/icon/trigger-72.png differ
diff --git a/data/icon/trigger-96.png b/data/icon/trigger-96.png
index 0fa282a..e1f8db0 100644
Binary files a/data/icon/trigger-96.png and b/data/icon/trigger-96.png differ
diff --git a/doc/BUILDING.txt b/doc/BUILDING.txt
index 5c85e73..3eb9317 100644
--- a/doc/BUILDING.txt
+++ b/doc/BUILDING.txt
@@ -4,9 +4,8 @@ How to build Trigger Rally
 
 1. Linux users
 2. Packaging for Linux
-3. Windows users - the easy way
-4. Windows users - the hard way
-5. Packaging for Windows
+3. Windows users
+4. Packaging for Windows
 A. Appendix
    A0. Developer aids
    A1. List of software used to build Trigger Rally (Windows)
@@ -21,18 +20,30 @@ To build Trigger Rally, your system must satisfy the following requirements:
 * have the GNU Make utility
 * have these development libraries installed:
 
-  LIBRARY NAME      POSSIBLE PACKAGE NAME       OFFICIAL DOWNLOAD LINK
+  LIBRARY NAME          OFFICIAL DOWNLOAD LINK
   --------------------------------------------------------------------
-  GL                libgl1-mesa-dev             N/A
-  GLU               libglu1-mesa-dev            N/A
-  GLEW              libglew-dev                 http://sourceforge.net/projects/glew/files/glew/
-  OpenAL            libopenal-dev               N/A
-  ALUT              libalut-dev                 N/A
-  PhysFS            libphysfs-dev               http://icculus.org/physfs/downloads/
-  SDL 1.2           libsdl1.2-dev               http://www.libsdl.org/download-1.2.php
-  SDL_image 1.2     libsdl-image1.2-dev         http://www.libsdl.org/projects/SDL_image/release-1.2.html
+  GL                    N/A
+  GLU                   N/A
+  GLEW                  http://sourceforge.net/projects/glew/files/glew/
+  OpenAL                N/A (?) http://openal-soft.org/#download
+  ALUT                  N/A (?) https://github.com/vancegroup/freealut/releases
+  PhysFS                http://icculus.org/physfs/downloads/
+  SDL2                  https://www.libsdl.org/download-2.0.php
+  SDL2_image            https://www.libsdl.org/projects/SDL_image/
   --------------------------------------------------------------------
 
+  LIBRARY NAME      DEB DISTRO              RPM DISTRO
+  ----------------------------------------------------
+  GL                libgl1-mesa-dev         N/A
+  GLU               libglu1-mesa-dev        mesa-libGLU-devel
+  GLEW              libglew-dev             glew-devel
+  OpenAL            libopenal-dev           openal-soft-devel
+  ALUT              libalut-dev             freealut-devel
+  PhysFS            libphysfs-dev           physfs-devel
+  SDL2              libsdl2-dev             SDL2-devel + SDL2-static
+  SDL2_image        libsdl2-image-dev       SDL2_image-devel
+  ----------------------------------------------------
+
 To build Trigger Rally you must run "make" in the "src" directory where
 "GNUmakefile" is located:
 
@@ -69,7 +80,7 @@ Note that the "install" target will build Trigger Rally first, if needed.
 
 Also note that you should probably set the OPTIMS variable to more conservative values,
 to ensure that users of older hardware can still run the game.
-As an example, the official binary release for Windows is built with:
+As an example, the official 32-bit binary release for Windows is built with:
 
   $ OPTIMS="-march=i686 -mtune=generic -O2" make build
 
@@ -87,325 +98,53 @@ Finally be sure to check the Trigger Rally default configuration file at:
 and edit the default data paths accordingly. And yes this config file needs to stay
 in the binary folder, for compatibility with the Windows build and for code simplicity.
 
--------------------------------
-3. Windows users - the easy way
--------------------------------
-
-The easy way to build on Windows is to download and install the following software:
-
-  SOFTWARE NAME                 OFFICIAL DOWNLOAD LINK
-  ----------------------------------------------------
-  Orwell Dev-C++                http://sourceforge.net/projects/orwelldevcpp/
-  Prebuilt libs for Win32       http://sourceforge.net/projects/trigger-rally/files/devkit/libraries-win32/
-  FMOD Studio API               http://www.fmod.org/download/#StudioAPI
-  ----------------------------------------------------
-
-It is very important that the FMOD Studio API be installed in its default location
-which, at the time of this writing, is:
-
-    "C:\Program Files\FMOD SoundSystem\FMOD Studio API Windows"
-
-Also note that the version of the prebuilt libraries package must coincide
-with the version of Trigger Rally that you have downloaded.
-
-For example, "trigger-rally-0.6.3.tar.gz" needs "libraries-win32-0.6.3.zip".
-
-You must extract the prebuilt libraries inside the parent folder of Trigger Rally,
-such that "libraries-win32" and "trigger-rally-VER" are side-by-side.
-Then all you need to do is open the "TriggerRally.dev" file (the .DEV file
-extension should be associated with Dev-C++) and click the "Rebuild All" button.
-
-You should now be able to play the game by running "trigger-rally.exe" which
-should be located in the binary folder "bin".
-
--------------------------------
-4. Windows users - the hard way
--------------------------------
-
-The hard way to build on Windows is to build not only Trigger Rally, but
-also all the libraries that it uses (except FMOD): SDL, SDL_image, PhysFS and GLEW.
-
-The benefit of doing all this work is that you can enable more aggressive
-optimizations tailored to your machine: in theory your build should run faster
-on your computer than the official build which uses the prebuilt libraries.
-
-The official Trigger Rally builds and libraries-win32 addons have been optimized
-with the following settings, in an attempt to balance compatibility and performance:
-
-    -march=i686 -mtune=generic -O2
+----------------
+3. Windows users
+----------------
 
-If you build for your own machine only, you can use these for everything:
-
-    -march=native -mtune=native -Ofast
-
-In order to build you need to download and install the following software:
+Building for Windows is supported officially with GNU Makefiles and Shell scripts.
+You will need to download and install MSYS2, CMake, TDM-GCC and the TR build scripts,
+then download the development libraries and finally run the TR build scripts:
 
   SOFTWARE NAME                 OFFICIAL DOWNLOAD LINK
   ----------------------------------------------------
-  Orwell Dev-C++                http://sourceforge.net/projects/orwelldevcpp/
-  MinGW                         http://mingw.org/
-  CMake                         http://www.cmake.org/download/
-  FMOD Studio API               http://www.fmod.org/download/#StudioAPI
+  MSYS2                         https://msys2.github.io/
+  CMake                         https://cmake.org/download/
+  TDM-GCC                       http://tdm-gcc.tdragon.net/download/
+  TR Build Scripts              https://sourceforge.net/projects/trigger-rally/files/devkit/build_scripts/
   ----------------------------------------------------
 
-Be sure to install Dev-C++ in a way such that there are no spaces in the path
-to its "bin" folder. The buggy configuration scripts of some libraries that we
-need to build will generate broken makefiles due to spaces in compiler paths!
-So DO NOT install Dev-C++ in the usual "C:\Program Files\Dev-Cpp" folder.
-Recommended installation folder: "C:\Dev-Cpp".
-
-After installing Dev-C++ you must add its "bin" folder to the system path.
-In Windows XP and later this is done by right-clicking on "My Computer",
-    Properties -> Advanced -> Environment Variables
-Edit the PATH variable, adding the location of the Dev-C++ bin folder, e.g.:
-    C:\Dev-Cpp\MinGW64\bin
-
-When installing MinGW we are only interested in MSYS, so be sure to install the
-package named "mingw-developer-toolkit" and nothing else.
-
-Install the FMOD Studio API in its default location which (currently) is:
-
-    "C:\Program Files\FMOD SoundSystem\FMOD Studio API Windows"
-
-Then you need to download the source code tarballs for the libraries listed below.
-I personally prefer Linux-style archives (.TAR.GZ and .TAR.BZ2) so those are the source
-code packages that were tested to build successfully with the methods described below.
+Of course, you're expected to read the "build_readme.txt" file provided with the build scripts.
 
   LIBRARY NAME                  OFFICIAL DOWNLOAD LINK
   ----------------------------------------------------
   GLEW                          http://sourceforge.net/projects/glew/files/glew/
   PhysFS                        http://icculus.org/physfs/downloads/
-  SDL 1.2                       http://www.libsdl.org/download-1.2.php
-  SDL_image 1.2                 http://www.libsdl.org/projects/SDL_image/release-1.2.html
+  SDL2                          https://www.libsdl.org/download-2.0.php
+  SDL2_image                    https://www.libsdl.org/projects/SDL_image/
   libjpeg                       http://ijg.org/
   libpng                        http://libpng.org/pub/png/libpng.html
   zlib                          http://zlib.net/
+  FMOD Studio API 1.06.XX       http://www.fmod.org/browse-studio-api/#FMODStudio106
   ----------------------------------------------------
 
-Extract all the archives into your MSYS home folder, which by default should be:
-
-    "C:\MinGW\msys\1.0\home\UserName\"
-
-Also extract the Trigger Rally archive in there. The end result should be a clean
-collection of folders, each named after the original archive you extracted.
-
-Start MSYS by running its batch file, which by default should be located at:
-
-    "C:\MinGW\msys\1.0\msys.bat"
-
-Now it's time to build and install the libraries, then build Trigger Rally.
-Note that the build order is important, because of the dependencies:
-
-  1. libjpeg
-  2. zlib
-  3. libpng (depends on zlib)
-  4. GLEW
-  5. SDL
-  6. SDL_image (depends on libpng, libjpeg and SDL)
-  7. PhysFS
-  8. Trigger Rally (depends on all the above)
-
-A quick reminder about optimizations, depending on who it is you are building for:
-
-  1. "I build for myself only" = best performance on your machine
-    -march=native -mtune=native -Ofast
-
-  2. "I build for everybody" = balance compatibility and performance
-    -march=i686 -mtune=generic -O2
-
-For each of the libraries you are supposed to read their README files to figure
-out how to properly build them on your own. However, I will now provide a complete
-build walkthrough to illustrate the diversity of quirks in the build process of
-Trigger Rally 0.6.4 for Win32.
-
-Note to Win32 users: because modern versions of Dev-C++ use a "tdm64" compiler
-you also need to pass the "-m32" switch to ensure that the compiler doesn't generate
-64-bit code by default, and specify the 32-bit PE target to WINDRES by way of
-the "-F pe-i386" option.
-(If you want to build for Win64 you can disregard these problematic 32-bit options.)
-
-Let's start by building libjpeg.
-
-Some of the libraries require you to execute their "configure" script so that they
-generate a proper "Makefile" that you can then use to build and install.
-
-  $ #
-  $ # building libjpeg
-  $ #
-  $ cd jpeg-9b/
-  $ ./configure CFLAGS="-march=native -mtune=native -Ofast -m32"
-  $ make
-  $ make install-strip
-
-In cases where the "install-strip" target is missing, "install" is a good enough
-substitute. Stripping binaries reduces their size by removing debugging information,
-which is useless to the average user anyway.
-
-Some of the libraries will require you to use makefiles directly.
-Because of the aforementioned 64-bit issue, things can get a bit nasty.
-
-  $ #
-  $ # building zlib
-  $ #
-  $ cd zlib-1.2.8/
-  $ make \
-  > SHARED_MODE=1 \
-  > CFLAGS="-march=native -mtune=native -Ofast -m32" \
-  > LDFLAGS="-m32" \
-  > RCFLAGS="--define GCC_WINDRES -F pe-i386" \
-  > -f win32/Makefile.gcc
-  $ make \
-  > SHARED_MODE=1 \
-  > INCLUDE_PATH="/local/include" \
-  > LIBRARY_PATH="/local/lib" \
-  > BINARY_PATH="/local/bin" \
-  > -f win32/Makefile.gcc install
-
-Shared libraries and their headers are installed in "/local/lib" and "/local/include",
-respectively. So when we build libraries which depend on other libraries, we must
-tell them to search those directories.
-
-  $ #
-  $ # building libpng
-  $ #
-  $ cd libpng-1.6.21/
-  $ ./configure \
-  > CFLAGS="-march=native -mtune=native -Ofast -m32" \
-  > CPPFLAGS="-I/local/include" \
-  > LDFLAGS="-L/local/lib"
-  $ make
-  $ make install-strip
-
-Some libraries have non-standard makefiles which expect non-standard variables.
-
-  $ #
-  $ # building GLEW
-  $ #
-  $ cd glew-1.13.0/
-  $ make \
-  > POPT="-march=native -mtune=native -Ofast" \
-  > CFLAGS.EXTRA="-m32" \
-  > LDFLAGS.EXTRA="-m32"
-  $ make GLEW_DEST="" DESTDIR="/local" install
-
-Next up are the SDL and SDL_image libraries, the build process of which requires
-a minor WINDRES hack so that each library is linked for 32-bit machines.
-
-  $ #
-  $ # building SDL
-  $ #
-  $ cd SDL-1.2.15/
-  $ ./configure \
-  > CFLAGS="-march=native -mtune=native -Ofast -m32" \
-  > LDFLAGS="-m32" \
-  > WINDRES="windres -F pe-i386"
-  $ make
-  $ make install
-
-  $ #
-  $ # building SDL_image
-  $ #
-  $ cd SDL_image-1.2.12/
-  $ ./configure \
-  > CFLAGS="-march=native -mtune=native -Ofast -m32" \
-  > CPPFLAGS="-I/local/include" \
-  > LDFLAGS="-m32 -L/local/lib" \
-  > WINDRES="windres -F pe-i386"
-  $ make
-  $ make install-strip
-
-Finally there is PhysFS which is configured with CMake:
-
-1. Start "cmake-gui" from the link found in the Start Menu, or on the Desktop
-   (or in the Classic Shell, God help you).
-2. Select the PhysFS folder in MSYS /home as the source code folder,
-   and a new empty folder next to it for the binaries, to prevent any
-   filename conflicts. The name I used for the new folder was:
-    "C:/MinGW/msys/1.0/home/UserName/physfs-2.0.3-build"
-3. Click "Configure", select "Specify native compilers", click "Next",
-   and browse to the Dev-C++ TDM C and C++ compilers, which should be at:
-    "C:/Dev-Cpp/MinGW64/bin/gcc.exe"
-    "C:/Dev-Cpp/MinGW64/bin/g++.exe"
-   and click "Finish".
-4. Enable the "Advanced" mode if you haven't already.
-5. Edit the following variables to the values on the right:
-
-    CMAKE_BUILD_TYPE            Release
-    CMAKE_CXX_FLAGS             -m32 -march=native -mtune=native
-    CMAKE_C_FLAGS               -m32 -march=native -mtune=native
-    CMAKE_CXX_FLAGS_RELEASE     -Ofast -DNDEBUG
-    CMAKE_C_FLAGS_RELEASE       -Ofast -DNDEBUG
-    CMAKE_RC_FLAGS              -F pe-i386
-    CMAKE_INSTALL_PREFIX        C:/MinGW/msys/1.0/local
-
-6. Click "Generate".
-7. Back in MSYS, run the following:
-
-  $ #
-  $ # building PhysFS
-  $ #
-  $ cd physfs-2.0.3-build/
-  $ mingw32-make
-  $ mingw32-make install/strip
-
-Now we're finally ready to build Trigger Rally, and it is the easiest bit.
-(If you haven't already, install the FMOD Studio API to its default location now.)
-
-  $ #
-  $ # building Trigger Rally
-  $ #
-  $ cd trigger-rally-0.6.4/src/
-  $ make -f GNUmakefile.MSYS
+If you're using Visual Studio you're on your own for the time being, sorry.
+That said, it shouldn't be too difficult to build the aforementioned dev libraries after
+you read their ReadMe files (some may provide Solution files, while others may support NMAKE)
+and then create a Trigger Rally C++11 Solution in which you include all the source files
+from the "trigger-rally-VERSION\src\" folder.
 
 ------------------------
-5. Packaging for Windows
+4. Packaging for Windows
 ------------------------
 
-Trigger Rally is distributed for Windows in a self-sufficient installer
-package created with the Nullsoft Scriptable Install System (NSIS).
-To rebuild that package you need to download and install the following software:
+Refer to the Trigger Rally Discussion forums if you have questions about packaging
+the game for Windows. At the time of this writing, NSIS is used for the 32-bit build and
+the WiX Toolset is planned to be used for future 64-bit builds:
 
-  SOFTWARE NAME                 OFFICIAL DOWNLOAD LINK
-  ----------------------------------------------------
-  NSIS 2.51                     http://nsis.sourceforge.net/Download
-  Trigger Rally NSIS pack       http://sourceforge.net/projects/trigger-rally/files/devkit/TR_NSIS/
-  ----------------------------------------------------
-
-After installing NSIS, unzip the TR NSIS pack to a folder of your choice.
-The pack contains the following:
-
-  FILENAME                      PURPOSE
-  -------------------------------------
-  artwork\                      folder which contains the TR installer branding
-  dist\                         folder which contains the data to be packed
-  generate_DelFilesRmDirs.cmd   batch file to generate NSIS headers
-  gpl-2.0.txt                   the GNU GPL 2 license
-  PACKAGING.txt                 ReadMe file containing this information
-  TriggerRally.nsi              NSIS main script
-  -------------------------------------
-
-1. Copy the Trigger Rally build that you wish to distribute to the "dist" folder.
-2. Run the batch file "generate_DelFilesRmDirs.cmd" to generate the "DelFiles.nsh"
-   and "RmDirs.nsh" NSIS headers. These headers are needed by the "TriggerRally.nsi"
-   NSIS script to create a proper uninstaller.
-3. Open "TriggerRally.nsi" and check that the version information is correct:
-   !define COMP_NAME "Packager Name (and Username)"
-   !define VERSION "X.X.X"
-4. Right-click on "TriggerRally.nsi" and choose "Compile NSIS Script".
-5. If the installer was compiled successfully as "trigger-rally-X.X.X-win32-setup.exe"
-   then test it, test its uninstaller, scan it for viruses*, and distribute it.
-
- * For obvious reasons, you must scan it with a freshly downloaded virus scanner
-   instead of your resident antivirus. Such tools are freely available at the time
-   of this writing, with the following examples (in no particular order):
-
-  VIRUS SCANNER NAME                OFFICIAL DOWNLOAD LINK
-  --------------------------------------------------------
-  Kaspersky Virus Removal Tool      http://www.kaspersky.com/antivirus-removal-tool?form=1
-  Microsoft Safety Scanner          http://www.microsoft.com/security/scanner/en-us/default.aspx
-  Emsisoft Emergency Kit            https://www.emsisoft.com/en/software/eek/
-  Dr.Web CureIt!                    http://www.freedrweb.com/cureit/?lng=en
-  --------------------------------------------------------
+    https://sourceforge.net/p/trigger-rally/discussion/
+    https://sourceforge.net/projects/trigger-rally/files/devkit/TR_NSIS/
+    https://sourceforge.net/projects/trigger-rally/files/devkit/TR_WiX/
 
 ##################
 A0. Developer aids
@@ -414,20 +153,34 @@ A0. Developer aids
 The release version of Trigger Rally suppresses terrain information and codriver checkpoint visuals.
 Developers can turn these on by defining the INDEVEL macro before building.
 
-Linux:
-
-  $ cd trigger-rally-0.6.4/src/
+  $ cd trigger-rally-0.6.5/src/
   $ OPTIMS="-DINDEVEL" make
 
-Windows (Dev-C++):
-
-  Project -> Project Options... -> Parameters -> C++ compiler:
-  (add "-DINDEVEL" to the list, without the quotes)
-
 ##########################################################
 A1. List of software used to build Trigger Rally (Windows)
 ##########################################################
 
+-------------------------------
+Trigger Rally 0.6.5 Win32/Win64
+-------------------------------
+
+  SOFTWARE                      VERSION
+  -------------------------------------
+  MSYS2                         20160205
+  TDM-GCC                       5.1.0
+  CMake                         3.6.1
+  NSIS                          3.0
+  WiX Toolset                   3.10
+  GLEW                          1.13.0
+  SDL2                          2.0.5
+  SDL2_image                    2.0.1
+  libjpeg                       9b
+  libpng                        1.6.26
+  PhysFS                        2.0.3
+  zlib                          1.2.8
+  FMOD Studio API Windows       1.06.20
+  -------------------------------------
+
 -------------------------
 Trigger Rally 0.6.4 Win32
 -------------------------
diff --git a/doc/DATA_AUTHORS.txt b/doc/DATA_AUTHORS.txt
index 6a8c2c3..6853ccf 100644
--- a/doc/DATA_AUTHORS.txt
+++ b/doc/DATA_AUTHORS.txt
@@ -2,6 +2,7 @@ Author and licence info for files under data/ directory.
 
 GPL = GNU General Public License v2 or later ( http://www.gnu.org/licenses/gpl.txt )
 CC0 (PD) = Creative Commons Zero 1.0 Universal Public Domain Dedication ( http://creativecommons.org/publicdomain/zero/1.0/ )
+CCBY 3.0 = Creative Commons Attribution 3.0 Unported ( http://creativecommons.org/licenses/by/3.0/ )
 
 DATA OPTIMIZATION NOTICE:
 	Starting from version 0.6.4, the released game data is optimized for smaller size and faster loading.
@@ -83,6 +84,11 @@ Andrei Bondor
 	
 Andrei Bondor, Onsemeliot
 	textures/CodriverSigns/abon/* (CC0, PD)
+	
+Bleed - http://remusprites.carbonmade.com
+	textures/veg/brown-tree.png (CCBY 3.0), source: http://opengameart.org/content/tree-collection-v26-bleeds-game-art
+	textures/veg/fine-tree.png (CCBY 3.0), source: http://opengameart.org/content/tree-collection-v26-bleeds-game-art
+	
 
 Bruno "Fuddl" Kleinert, Andrei Bondor, Onsemeliot
 	plugins/map-banana.zip (unlicensed)
@@ -135,7 +141,10 @@ Iwan 'qubodup' Gabovitch, Onsemeliot
 	
 Jared Buckner, Onsemeliot
 	plugins/map-delta.zip (GPL)
-	
+
+Keistutis, Onsemeliot
+	textures/veg/bush-low.png (CC0) https://openclipart.org/detail/202745/bush
+
 kougloff, Iwan Gabovitch, Liviu Andronic
 	sounds/gear.wav (CC0, PD) source: http://www.freesound.org/people/kougloff/sounds/117558/
 
@@ -274,6 +283,7 @@ Onsemeliot
 	events/coy/undergrowth/mm.png (GPL)
 	events/coy/undergrowth/ss.png (GPL)
 	events/coy/undergrowth/t.png (GPL)
+	events/flood (GPL)
 	events/highstakes/crossing/c.png (GPL)
 	events/highstakes/crossing/f.png (GPL)
 	events/highstakes/crossing/h.png (GPL)
@@ -441,6 +451,7 @@ Onsemeliot
 	events/triggercup/warmup/mm.png (GPL)
 	events/triggercup/warmup/ss.png (GPL)
 	events/triggercup/warmup/t.png (GPL)
+	events/tight (GPL)
 	events/weird/china/c.png (GPL)
 	events/weird/china/h.png (GPL)
 	events/weird/china/mm.png (GPL)
@@ -521,6 +532,7 @@ Onsemeliot
 	maps/between/m.png (GPL)
 	maps/between/mm.png (GPL)
 	maps/between/ss.png (GPL)
+	maps/bitethedust/ (GPL)
 	maps/blunt/c.png (GPL)
 	maps/blunt/f.png (GPL)
 	maps/blunt/h.png (GPL)
@@ -546,7 +558,7 @@ Onsemeliot
 	maps/cracks/ss.png (GPL)
 	maps/crossmountain/mm.png (GPL)
 	maps/crossmountain/ss.png (GPL)
-	maps/darkbushes/* (GPL)
+	maps/darkbushes (GPL)
 	maps/deltarevised/c.png (GPL)
 	maps/deltarevised/f.png (GPL)
 	maps/deltarevised/h.png (GPL)
@@ -561,8 +573,8 @@ Onsemeliot
 	maps/dry/h.png (GPL)
 	maps/dry/mm.png (GPL)
 	maps/dry/ss.png (GPL)
-	maps/duned/* (GPL)
-	maps/dusty/* (GPL)
+	maps/duned (GPL)
+	maps/dusty (GPL)
 	maps/edge/c.png (GPL)
 	maps/edge/f.png (GPL)
 	maps/edge/h.png (GPL)
@@ -571,6 +583,7 @@ Onsemeliot
 	maps/edge/ss.png (GPL)
 	maps/eversnake/mm.png (GPL)
 	maps/eversnake/ss.png (GPL)
+	maps/fineline (GPL)
 	maps/forced/c.png (GPL)
 	maps/forced/f.png (GPL)
 	maps/forced/h.png (GPL)
@@ -616,6 +629,7 @@ Onsemeliot
 	maps/highland/mm.png (GPL)
 	maps/highland/ss.png (GPL)
 	maps/holiday (GPL)
+	maps/humid (GPL)
 	maps/iced/c.png (GPL)
 	maps/iced/h.png (GPL)
 	maps/iced/mm.png (GPL)
@@ -633,6 +647,8 @@ Onsemeliot
 	maps/kraut/h.png (GPL)
 	maps/kraut/mm.png (GPL)
 	maps/kraut/ss.png (GPL)
+	maps/lazyhills (GPL)
+	maps/leaves (GPL)
 	maps/littleisland/c.png (GPL)
 	maps/littleisland/f.png (GPL)
 	maps/littleisland/h.png (GPL)
@@ -652,6 +668,7 @@ Onsemeliot
 	maps/mudbath/m.png (GPL)
 	maps/mudbath/mm.png (GPL)
 	maps/mudbath/ss.png (GPL)
+	maps/narrowvalley/* (GPL)
 	maps/northern/c.png (GPL)
 	maps/northern/f.png (GPL)
 	maps/northern/h.png (GPL)
@@ -659,6 +676,7 @@ Onsemeliot
 	maps/northern/mm.png (GPL)
 	maps/northern/ss.png (GPL)
 	maps/northern/t.png (GPL)
+	maps/offgrid (GPL)
 	maps/offroad/c.jpg (GPL)
 	maps/offroad/f.png (GPL)
 	maps/offroad/h.png (GPL)
@@ -704,6 +722,7 @@ Onsemeliot
 	maps/quintessence/m.png (GPL)
 	maps/quintessence/mm.png (GPL)
 	maps/quintessence/ss.png (GPL)
+	maps/raw/ (GPL)
 	maps/redrock/c.png (GPL)
 	maps/redrock/f.png (GPL)
 	maps/redrock/h.png (GPL)
@@ -719,12 +738,15 @@ Onsemeliot
 	maps/ripe/h.png (GPL)
 	maps/ripe/ss.png (GPL)
 	maps/ripe/t.png (GPL)
+	maps/rural (GPL)
 	maps/rugged/c.png (GPL)
 	maps/rugged/h.png (GPL)
 	maps/rugged/mm.png (GPL)
 	maps/rugged/ss.png (GPL)
+	maps/sandstorm (GPL)
 	maps/serpentine/mm.png (GPL)
 	maps/serpentine/ss.png (GPL)
+	maps/shallow (GPL)
 	maps/smallswirl/mm.png (GPL)
 	maps/smallswirl/ss.png (GPL)
 	maps/snowlevel/mm.png (GPL)
@@ -766,6 +788,8 @@ Onsemeliot
 	maps/tropic/h.png (GPL)
 	maps/tropic/mm.png (GPL)
 	maps/tropic/ss.png (GPL)
+	maps/treeline (GPL)
+	maps/ulterior (GPL)
 	maps/unleashed/c.png (GPL)
 	maps/unleashed/f.png (GPL)
 	maps/unleashed/h.png (GPL)
@@ -809,14 +833,15 @@ Onsemeliot
 	maps/zone/m.png (GPL)
 	maps/zone/mm.png (GPL)
 	maps/zone/ss.png (GPL)
-	sounds/codriver/paula/*  (GPL)
-	sounds/codriver/tim/* (GPL)
+	sounds/codriver/paula  (GPL)
+	sounds/codriver/tim (GPL)
 	textures/button_next.png (CC0, PD)
 	textures/button_prev.png (CC0, PD)
 	textures/CodriverSigns/glossy/* (CC0, PD)
 	textures/CodriverSigns/plain/* (CC0, PD)
 	textures/CodriverSigns/white/* (CC0, PD)
 	textures/life_helmet.png (CC0, PD), source: https://openclipart.org/detail/172011/racing-helmet
+	textures/roadsigns/ (CC0, PD), raw material from: https://openclipart.org/search/?query=roadsigns
 	textures/sky/cloudy.png (GPL)
 	textures/sky/dark-blue.png (GPL)
 	textures/sky/dark-clouds.png (GPL)
@@ -841,12 +866,14 @@ Onsemeliot
 	textures/veg/mais.png (CC0, PD), source: http://opengameart.org/content/corn-resp-maize
 	textures/veg/olive-bush.png (CC0, PD)
 	textures/veg/palm-tree.png (CC0, PD)
+	textures/veg/palm-tree2.png (CC0, PD)
 	textures/veg/pear-tree.png (CC0, PD)
 	textures/veg/post.png (CC0, PD)
 	textures/veg/red-tree.png (CC0, PD)
 	textures/veg/snowy-tree.png (CC0, PD)
 	textures/veg/spruce.png (CC0, PD), source: https://openclipart.org/detail/227626/spruce
 	textures/veg/thick-leafes.png (CC0, PD)
+	textures/veg/thorn-bush.png (CC0, PD)
 	textures/veg/wide-tree.png (CC0, PD)
 	textures/water/black-water.png (CC0, PD)
 	textures/water/dark-ice-water.png (CC0, PD)
@@ -947,6 +974,9 @@ Onsemeliot, Andrei Bondor
 	maps/xerxes/xerxes.level (GPL)
 	maps/yearzero/yearzero.level (GPL)
 	maps/zone/zone.level (GPL)
+
+Rambo Tribble, Onsemeliot
+	textures/veg/fir.png (CC0) https://openclipart.org/detail/181473/douglas-fir
 	
 Pulp, Onsemeliot
 	plugins/map-pulp.zip (unlicenced)
diff --git a/src/GNUmakefile b/src/GNUmakefile
index 3f95c48..0832a9c 100644
--- a/src/GNUmakefile
+++ b/src/GNUmakefile
@@ -4,7 +4,7 @@
 
 # standard GNU and custom variables
 DISTNAME        := trigger-rally
-DISTVER         := 0.6.4
+DISTVER         := 0.6.5
 DISTDIR         := $(DISTNAME)-$(DISTVER)
 DISTARC         := $(DISTDIR).tar.gz
 TR_EXENAME      := trigger-rally
@@ -16,17 +16,18 @@ TR_EXEFILE      := $(TR_BINDIR)/$(TR_EXENAME)
 TR_CFGFILE      := $(TR_BINDIR)/$(TR_CFGNAME)
 TR_DATAFILES    := $(shell cd $(TR_DATADIR); find * -type f)
 TR_DOCFILES     := $(shell cd $(TR_DOCDIR); find * -type f)
-PROJDIRS        := PEngine PSim TinyXML Trigger
+PROJDIRS        := PEngine PSim TinyXML2 Trigger
 SRCFILES        := $(shell find $(PROJDIRS) -type f -name "*.cpp")
 OBJFILES        := $(patsubst %.cpp, %.o, $(SRCFILES))
 DEPFILES        := $(patsubst %.cpp, %.d, $(SRCFILES))
 WARNINGS        ?= -Wall -Wextra -pedantic
 OPTIMS          ?= -march=native -mtune=native -Ofast
 DMACROS         := -DNDEBUG -DUNIX -DPACKAGE_VERSION=\"$(DISTVER)\"
-INCDIRS         := -I"./include"
-CXXFLAGS        := -std=c++11 $(WARNINGS) $(OPTIMS) $(DMACROS) $(INCDIRS)
-EXTRA_LIBS      := -lGL -lGLU -lGLEW -lSDL -lSDL_image -lphysfs -lopenal -lalut -lpthread
-LDFLAGS         := $(EXTRA_LIBS)
+INCDIRS         := -I'./include'
+CXXFLAGS        += -std=c++11 $(WARNINGS) $(OPTIMS)
+CPPFLAGS        += $(DMACROS) $(INCDIRS)
+EXTRA_LIBS      := -lSDL2main -lGL -lGLU -lGLEW -lSDL2 -lSDL2_image -lphysfs -lopenal -lalut -lpthread
+LDFLAGS         += $(EXTRA_LIBS)
 INSTALL_PROGRAM := install --mode=0755
 INSTALL_DATA    := install --mode=0644
 
@@ -68,6 +69,11 @@ printvars:
 	@printf "\tOPTIMS       ?= %s\n" "$(OPTIMS)"
 	@printf "\tWARNINGS     ?= %s\n" "$(WARNINGS)"
 	@printf "\n"
+	@printf "resulting values of build variables:\n"
+	@printf "\tCXXFLAGS     += %s\n" "$(CXXFLAGS)"
+	@printf "\tCPPFLAGS     += %s\n" "$(CPPFLAGS)"
+	@printf "\tLDFLAGS      += %s\n" "$(LDFLAGS)"
+	@printf "\n"
 
 # installs the software (executable, data files and documentation)
 install: installdirs build
@@ -149,4 +155,4 @@ clean:
 #
 %.o: %.cpp GNUmakefile
 	@printf "%s\t%s -> %s\n" $(CXX) $< $@
-	@$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
+	@$(CXX) $(CXXFLAGS) $(CPPFLAGS) -MMD -MP -c $< -o $@
diff --git a/src/GNUmakefile.MSYS b/src/GNUmakefile.MSYS
index 3db2cdf..14afa73 100644
--- a/src/GNUmakefile.MSYS
+++ b/src/GNUmakefile.MSYS
@@ -1,5 +1,5 @@
 #
-# Trigger Rally GNUmakefile.MSYS for Dev-C++/MSYS users
+# Trigger Rally GNUmakefile.MSYS for MSYS2 users
 #
 # This is a watered-down version of the "real" GNUmakefile and it
 # doesn't support the `install` and `uninstall` targets.
@@ -10,9 +10,9 @@
 
 # standard GNU and custom variables
 DISTNAME        := trigger-rally
-DISTVER         := 0.6.4
+DISTVER         := 0.6.5
 DISTDIR         := $(DISTNAME)-$(DISTVER)
-DISTARC         := $(DISTDIR).zip
+DISTARC         := $(DISTDIR)-win32.zip
 TR_BINDIR       := ../bin
 TR_EXENAME      := trigger-rally.exe
 TR_EXEFILE      := $(TR_BINDIR)/$(TR_EXENAME)
@@ -22,11 +22,11 @@ TR_DLLFILES     :=                  \
     $(TR_BINDIR)/libjpeg-9.dll      \
     $(TR_BINDIR)/libphysfs.dll      \
     $(TR_BINDIR)/libpng16-16.dll    \
-    $(TR_BINDIR)/SDL.dll            \
-    $(TR_BINDIR)/SDL_image.dll      \
+    $(TR_BINDIR)/SDL2.dll           \
+    $(TR_BINDIR)/SDL2_image.dll     \
     $(TR_BINDIR)/zlib1.dll
-FMODAPIDIR      := /c/Program Files/FMOD SoundSystem/FMOD Studio API Windows/api/lowlevel
-PROJDIRS        := PEngine PSim TinyXML Trigger
+FMODAPIDIR      := /c/Program Files (x86)/FMOD SoundSystem/FMOD Studio API Windows/api/lowlevel
+PROJDIRS        := PEngine PSim TinyXML2 Trigger
 SRCFILES        := $(shell find $(PROJDIRS) -type f -name "*.cpp")
 OBJFILES        := $(patsubst %.cpp, %.o, $(SRCFILES))
 DEPFILES        := $(patsubst %.cpp, %.d, $(SRCFILES))
@@ -35,15 +35,16 @@ OPTIMS          ?= -march=native -mtune=native -Ofast
 MODENV          ?= -m32
 DMACROS         := -DNDEBUG -DWIN32 -DPACKAGE_VERSION=\"$(DISTVER)\"
 INCDIRS         :=                  \
-    -I"./include"                   \
-    -I"/local/include"              \
-    -I"$(FMODAPIDIR)/inc"
+    -I'./include'                   \
+    -I'/usr/local/include'          \
+    -I'$(FMODAPIDIR)/inc'
 LIBDIRS         :=                  \
-    -L"/local/lib"                  \
-    -L"$(FMODAPIDIR)/lib"
-CXXFLAGS        := -std=c++11 $(MODENV) $(WARNINGS) $(OPTIMS) $(DMACROS) $(INCDIRS)
-EXTRA_LIBS      := -lopengl32 -lglu32 -lglew32 -lSDL -lSDL_image -lphysfs -lfmod
-LDFLAGS         := $(MODENV) $(LIBDIRS) $(EXTRA_LIBS)
+    -L'/usr/local/lib'              \
+    -L'$(FMODAPIDIR)/lib'
+CXXFLAGS        += -std=c++11 $(MODENV) $(WARNINGS) $(OPTIMS)
+CPPFLAGS        += $(DMACROS) $(INCDIRS)
+EXTRA_LIBS      := -lmingw32 -lSDL2main -lopengl32 -lglu32 -lglew32 -lSDL2 -lSDL2_image -lphysfs -lfmod
+LDFLAGS         += $(MODENV) $(LIBDIRS) $(EXTRA_LIBS)
 
 #
 # phony targets, whose names aren't names of resulting files
@@ -69,9 +70,14 @@ build: printvars $(TR_EXEFILE) copydlls
 #
 printvars:
 	@printf "\ncurrent values of user-set variables:\n"
-	@printf "\tOPTIMS   ?= %s\n" "$(OPTIMS)"
-	@printf "\tMODENV   ?= %s\n" "$(MODENV)"
-	@printf "\tWARNINGS ?= %s\n" "$(WARNINGS)"
+	@printf "\tOPTIMS       ?= %s\n" "$(OPTIMS)"
+	@printf "\tMODENV       ?= %s\n" "$(MODENV)"
+	@printf "\tWARNINGS     ?= %s\n" "$(WARNINGS)"
+	@printf "\n"
+	@printf "resulting values of build variables:\n"
+	@printf "\tCXXFLAGS     += %s\n" "$(CXXFLAGS)"
+	@printf "\tCPPFLAGS     += %s\n" "$(CPPFLAGS)"
+	@printf "\tLDFLAGS      += %s\n" "$(LDFLAGS)"
 	@printf "\n"
 
 #
@@ -145,7 +151,7 @@ winclean:
 #
 %.o: %.cpp GNUmakefile.MSYS
 	@printf "%s\t%s -> %s\n" $(CXX) $< $@
-	@$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
+	@$(CXX) $(CXXFLAGS) $(CPPFLAGS) -MMD -MP -c $< -o $@
 
 #
 # the following targets handle the copying of .DLL files to the binary directory
@@ -160,22 +166,22 @@ $(TR_BINDIR)/fmod.dll:
 	@cp --verbose "$(FMODAPIDIR)/lib/fmod.dll" $(TR_BINDIR)
 
 $(TR_BINDIR)/glew32.dll:
-	@cp --verbose /local/bin/glew32.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/glew32.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/libjpeg-9.dll:
-	@cp --verbose /local/bin/libjpeg-9.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/libjpeg-9.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/libphysfs.dll:
-	@cp --verbose /local/bin/libphysfs.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/libphysfs.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/libpng16-16.dll:
-	@cp --verbose /local/bin/libpng16-16.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/libpng16-16.dll $(TR_BINDIR)
 
-$(TR_BINDIR)/SDL.dll:
-	@cp --verbose /local/bin/SDL.dll $(TR_BINDIR)
+$(TR_BINDIR)/SDL2.dll:
+	@cp --verbose /usr/local/bin/SDL2.dll $(TR_BINDIR)
 
-$(TR_BINDIR)/SDL_image.dll:
-	@cp --verbose /local/bin/SDL_image.dll $(TR_BINDIR)
+$(TR_BINDIR)/SDL2_image.dll:
+	@cp --verbose /usr/local/bin/SDL2_image.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/zlib1.dll:
-	@cp --verbose /local/bin/zlib1.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/zlib1.dll $(TR_BINDIR)
diff --git a/src/GNUmakefile.MSYS b/src/GNUmakefile.MSYS64
similarity index 66%
copy from src/GNUmakefile.MSYS
copy to src/GNUmakefile.MSYS64
index 3db2cdf..cd25c82 100644
--- a/src/GNUmakefile.MSYS
+++ b/src/GNUmakefile.MSYS64
@@ -1,8 +1,7 @@
 #
-# Trigger Rally GNUmakefile.MSYS for Dev-C++/MSYS users
+# Trigger Rally GNUmakefile.MSYS64 for MSYS2 users
 #
-# This is a watered-down version of the "real" GNUmakefile and it
-# doesn't support the `install` and `uninstall` targets.
+# This is a modified version of GNUmakefile.MSYS for Windows 64-bit builds.
 #
 # TODO:
 #   * beautify printing of copied .DLL files
@@ -10,40 +9,41 @@
 
 # standard GNU and custom variables
 DISTNAME        := trigger-rally
-DISTVER         := 0.6.4
+DISTVER         := 0.6.5
 DISTDIR         := $(DISTNAME)-$(DISTVER)
-DISTARC         := $(DISTDIR).zip
+DISTARC         := $(DISTDIR)-win64.zip
 TR_BINDIR       := ../bin
-TR_EXENAME      := trigger-rally.exe
+TR_EXENAME      := trigger-rally-x64.exe
 TR_EXEFILE      := $(TR_BINDIR)/$(TR_EXENAME)
 TR_DLLFILES     :=                  \
-    $(TR_BINDIR)/fmod.dll           \
+    $(TR_BINDIR)/fmod64.dll         \
     $(TR_BINDIR)/glew32.dll         \
     $(TR_BINDIR)/libjpeg-9.dll      \
     $(TR_BINDIR)/libphysfs.dll      \
     $(TR_BINDIR)/libpng16-16.dll    \
-    $(TR_BINDIR)/SDL.dll            \
-    $(TR_BINDIR)/SDL_image.dll      \
+    $(TR_BINDIR)/SDL2.dll           \
+    $(TR_BINDIR)/SDL2_image.dll     \
     $(TR_BINDIR)/zlib1.dll
-FMODAPIDIR      := /c/Program Files/FMOD SoundSystem/FMOD Studio API Windows/api/lowlevel
-PROJDIRS        := PEngine PSim TinyXML Trigger
+FMODAPIDIR      := /c/Program Files (x86)/FMOD SoundSystem/FMOD Studio API Windows/api/lowlevel
+PROJDIRS        := PEngine PSim TinyXML2 Trigger
 SRCFILES        := $(shell find $(PROJDIRS) -type f -name "*.cpp")
 OBJFILES        := $(patsubst %.cpp, %.o, $(SRCFILES))
 DEPFILES        := $(patsubst %.cpp, %.d, $(SRCFILES))
 WARNINGS        ?= -Wall -Wextra -pedantic
 OPTIMS          ?= -march=native -mtune=native -Ofast
-MODENV          ?= -m32
+MODENV          ?= -m64
 DMACROS         := -DNDEBUG -DWIN32 -DPACKAGE_VERSION=\"$(DISTVER)\"
 INCDIRS         :=                  \
-    -I"./include"                   \
-    -I"/local/include"              \
-    -I"$(FMODAPIDIR)/inc"
+    -I'./include'                   \
+    -I'/usr/local/include'          \
+    -I'$(FMODAPIDIR)/inc'
 LIBDIRS         :=                  \
-    -L"/local/lib"                  \
-    -L"$(FMODAPIDIR)/lib"
-CXXFLAGS        := -std=c++11 $(MODENV) $(WARNINGS) $(OPTIMS) $(DMACROS) $(INCDIRS)
-EXTRA_LIBS      := -lopengl32 -lglu32 -lglew32 -lSDL -lSDL_image -lphysfs -lfmod
-LDFLAGS         := $(MODENV) $(LIBDIRS) $(EXTRA_LIBS)
+    -L'/usr/local/lib'              \
+    -L'$(FMODAPIDIR)/lib'
+CXXFLAGS        += -std=c++11 $(MODENV) $(WARNINGS) $(OPTIMS)
+CPPFLAGS        += $(DMACROS) $(INCDIRS)
+EXTRA_LIBS      := -lmingw32 -lSDL2main -lopengl32 -lglu32 -lglew32 -lSDL2 -lSDL2_image -lphysfs -lfmod64
+LDFLAGS         += $(MODENV) $(LIBDIRS) $(EXTRA_LIBS)
 
 #
 # phony targets, whose names aren't names of resulting files
@@ -69,9 +69,14 @@ build: printvars $(TR_EXEFILE) copydlls
 #
 printvars:
 	@printf "\ncurrent values of user-set variables:\n"
-	@printf "\tOPTIMS   ?= %s\n" "$(OPTIMS)"
-	@printf "\tMODENV   ?= %s\n" "$(MODENV)"
-	@printf "\tWARNINGS ?= %s\n" "$(WARNINGS)"
+	@printf "\tOPTIMS       ?= %s\n" "$(OPTIMS)"
+	@printf "\tMODENV       ?= %s\n" "$(MODENV)"
+	@printf "\tWARNINGS     ?= %s\n" "$(WARNINGS)"
+	@printf "\n"
+	@printf "resulting values of build variables:\n"
+	@printf "\tCXXFLAGS     += %s\n" "$(CXXFLAGS)"
+	@printf "\tCPPFLAGS     += %s\n" "$(CPPFLAGS)"
+	@printf "\tLDFLAGS      += %s\n" "$(LDFLAGS)"
 	@printf "\n"
 
 #
@@ -140,12 +145,12 @@ winclean:
 #
 # compiles source files to object files
 #
-# GNUmakefile.MSYS was added as a dependency in order to trigger a rebuild
+# GNUmakefile.MSYS64 was added as a dependency in order to trigger a rebuild
 # if this makefile is edited
 #
-%.o: %.cpp GNUmakefile.MSYS
+%.o: %.cpp GNUmakefile.MSYS64
 	@printf "%s\t%s -> %s\n" $(CXX) $< $@
-	@$(CXX) $(CXXFLAGS) -MMD -MP -c $< -o $@
+	@$(CXX) $(CXXFLAGS) $(CPPFLAGS) -MMD -MP -c $< -o $@
 
 #
 # the following targets handle the copying of .DLL files to the binary directory
@@ -156,26 +161,26 @@ winclean:
 
 copydlls: $(TR_DLLFILES)
 
-$(TR_BINDIR)/fmod.dll:
-	@cp --verbose "$(FMODAPIDIR)/lib/fmod.dll" $(TR_BINDIR)
+$(TR_BINDIR)/fmod64.dll:
+	@cp --verbose "$(FMODAPIDIR)/lib/fmod64.dll" $(TR_BINDIR)
 
 $(TR_BINDIR)/glew32.dll:
-	@cp --verbose /local/bin/glew32.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/glew32.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/libjpeg-9.dll:
-	@cp --verbose /local/bin/libjpeg-9.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/libjpeg-9.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/libphysfs.dll:
-	@cp --verbose /local/bin/libphysfs.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/libphysfs.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/libpng16-16.dll:
-	@cp --verbose /local/bin/libpng16-16.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/libpng16-16.dll $(TR_BINDIR)
 
-$(TR_BINDIR)/SDL.dll:
-	@cp --verbose /local/bin/SDL.dll $(TR_BINDIR)
+$(TR_BINDIR)/SDL2.dll:
+	@cp --verbose /usr/local/bin/SDL2.dll $(TR_BINDIR)
 
-$(TR_BINDIR)/SDL_image.dll:
-	@cp --verbose /local/bin/SDL_image.dll $(TR_BINDIR)
+$(TR_BINDIR)/SDL2_image.dll:
+	@cp --verbose /usr/local/bin/SDL2_image.dll $(TR_BINDIR)
 
 $(TR_BINDIR)/zlib1.dll:
-	@cp --verbose /local/bin/zlib1.dll $(TR_BINDIR)
+	@cp --verbose /usr/local/bin/zlib1.dll $(TR_BINDIR)
diff --git a/src/PEngine/app.cpp b/src/PEngine/app.cpp
index b39a099..d592191 100644
--- a/src/PEngine/app.cpp
+++ b/src/PEngine/app.cpp
@@ -240,10 +240,13 @@ int PApp::run(int argc, char *argv[])
 
   srand(SDL_GetTicks());
   
-  SDL_WM_SetCaption(apptitle.c_str(), nullptr);
-  
   PUtil::outLog() << "Create window and set video mode" << std::endl;
-  
+#if 0
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+    SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
+#endif
   SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
   
   if (reqRGB) {
@@ -268,11 +271,59 @@ int PApp::run(int argc, char *argv[])
   }
   
   if (cx <= 0 || cy <= 0) setScreenModeAutoWindow();
-  
-  screen = SDL_SetVideoMode(cx,cy,bpp,
-    SDL_OPENGL |
-    (fullscr ? SDL_FULLSCREEN : 0) |
-    (noframe ? SDL_NOFRAME : 0));
+
+    if (!autoVideo)
+    {
+        screen = SDL_CreateWindow(apptitle.c_str(),
+            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+            cx, cy,
+            SDL_WINDOW_OPENGL |
+            (fullscr ? SDL_WINDOW_FULLSCREEN : 0)
+            /* | (noframe ? SDL_NOFRAME : 0) */ ); // broken by SDL2, no more `SDL_NOFRAME`
+    }
+    else // automatic video mode
+    {
+        //
+        // NOTE:
+        //  The `SDL_GetDesktopMode()` function is used instead of the automatic
+        //  flag `SDL_WINDOW_FULLSCREEN_DESKTOP` because the latter fails to work
+        //  reliably on Linux in certain versions of SDL2.
+        //
+
+#define SDL_WINDOW_FULLSCREEN_DESKTOP_IS_STILL_BROKEN
+
+#ifdef SDL_WINDOW_FULLSCREEN_DESKTOP_IS_STILL_BROKEN
+        SDL_DisplayMode dm;
+
+        if (SDL_GetDesktopDisplayMode(0, &dm) == 0)
+        {
+            cx = dm.w;
+            cy = dm.h;
+            PUtil::outLog() << "Desktop video mode resolution: " << cx << 'x' << cy << std::endl;
+        }
+        else
+        {
+            PUtil::outLog() << "SDL error, SDL_GetDesktopDisplayMode(): " << SDL_GetError() << std::endl;
+            cx = 800;
+            cy = 600;
+        }
+
+        screen = SDL_CreateWindow(apptitle.c_str(),
+            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+            cx, cy,
+            SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
+#else
+        //
+        // NOTE:
+        //  This would be the "proper" way of doing things, but at the time of this
+        //  writing it only works reliably on Windows 7 using SDL2 version 2.0.5.
+        //
+        screen = SDL_CreateWindow(apptitle.c_str(),
+            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+            0, 0,
+            SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP);
+#endif
+    }
   
   if (!screen) {
     PUtil::outLog() << "Failed to create window or set video mode" << std::endl;
@@ -285,6 +336,21 @@ int PApp::run(int argc, char *argv[])
     return 1;
   }
   
+  context = SDL_GL_CreateContext(screen);
+  
+    if (context == NULL)
+    {
+        PUtil::outLog() << "Failed to create OpenGL context for game window\n";
+        PUtil::outLog() << "SDL error: " << SDL_GetError() << std::endl;
+        SDL_DestroyWindow(screen);
+        SDL_Quit();
+
+        if (PHYSFS_deinit() == 0)
+            PUtil::outLog() << "PhysFS: " << PHYSFS_getLastError() << std::endl;
+
+        return 1;
+    }
+  
   sdl_mousemap = 0;
   
   sdl_joy.resize(SDL_NumJoysticks());
@@ -297,13 +363,15 @@ int PApp::run(int argc, char *argv[])
     sdl_joy[i].sdl_joystick = SDL_JoystickOpen(i);
     if (sdl_joy[i].sdl_joystick == nullptr) {
       PUtil::outLog() << "failed to open joystick" << std::endl;
+      SDL_GL_DeleteContext(context);
+      SDL_DestroyWindow(screen);
       SDL_Quit();
       if (PHYSFS_deinit() == 0) {
         PUtil::outLog() << "PhysFS: " << PHYSFS_getLastError() << std::endl;
       }
       return 1;
     }
-    sdl_joy[i].name = SDL_JoystickName(i);
+    sdl_joy[i].name = SDL_JoystickName(sdl_joy[i].sdl_joystick);
     sdl_joy[i].axis.resize(SDL_JoystickNumAxes(sdl_joy[i].sdl_joystick));
     for (unsigned int j=0; j<sdl_joy[i].axis.size(); j++)
       sdl_joy[i].axis[j] = ((float)SDL_JoystickGetAxis(sdl_joy[i].sdl_joystick, j) + 0.5f) / 32767.5f;
@@ -329,16 +397,18 @@ int PApp::run(int argc, char *argv[])
   SDL_JoystickEventState(SDL_ENABLE);
   
   int err = glewInit();
-  
+
   if (err != GLEW_OK) {
     PUtil::outLog() << "GLEW failed to initialise: " << glewGetErrorString(err) << std::endl;
+    SDL_GL_DeleteContext(context);
+    SDL_DestroyWindow(screen);
     SDL_Quit();
     if (PHYSFS_deinit() == 0) {
       PUtil::outLog() << "PhysFS: " << PHYSFS_getLastError() << std::endl;
     }
     return 1;
   }
-  
+
   PUtil::outLog() << "GLEW initialized" << std::endl;
   
   PUtil::outLog() << "Graphics: " <<
@@ -389,12 +459,16 @@ int PApp::run(int argc, char *argv[])
       delete sslist.back();
       sslist.pop_back();
     }
-    
+
+    SDL_GL_DeleteContext(context);
+    SDL_DestroyWindow(screen);
     SDL_Quit();
+
     if (PHYSFS_deinit() == 0)
     {
       PUtil::outLog () << "PhysFS: " << PHYSFS_getLastError() << std::endl;
     }
+
     return 1;
   }
 
@@ -413,20 +487,25 @@ int PApp::run(int argc, char *argv[])
       sslist.pop_back();
     }
 
+    SDL_GL_DeleteContext(context);
+    SDL_DestroyWindow(screen);
     SDL_Quit();
+
     if (PHYSFS_deinit() == 0) {
       PUtil::outLog() << "PhysFS: " << PHYSFS_getLastError() << std::endl;
     }
+
     return 1;
   }
 
   //SDL_ShowCursor(SDL_DISABLE);
   //SDL_WM_GrabInput(SDL_GRAB_ON);
-  SDL_WM_GrabInput(SDL_GRAB_OFF);
-  SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+  //SDL_WM_GrabInput(SDL_GRAB_OFF);
+  //SDL_SetWindowGrab(screen, SDL_TRUE);
+  //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
   //SDL_EnableUNICODE(1);
 
-  sdl_keymap = SDL_GetKeyState(&sdl_numkeys);
+  sdl_keymap = SDL_GetKeyboardState(&sdl_numkeys);
 
   resize();
 
@@ -460,9 +539,12 @@ int PApp::run(int argc, char *argv[])
         break;
       */
 
+      // unavailable (and unneeded?) in SDL2
+#if 0
       case SDL_VIDEOEXPOSE:
         repaint = true;
         break;
+#endif
 
       case SDL_KEYDOWN:
       case SDL_KEYUP:
@@ -547,7 +629,7 @@ int PApp::run(int argc, char *argv[])
         
         render(0.0f);
         glFlush();
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(screen);
         break;
         
       case StereoQuadBuffer: // Hardware quad buffer stereo
@@ -560,7 +642,7 @@ int PApp::run(int argc, char *argv[])
         render(stereoEyeTranslation);
         glFlush();
         
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(screen);
         break;
         
       case StereoRedBlue: // Red-blue anaglyph stereo
@@ -578,7 +660,7 @@ int PApp::run(int argc, char *argv[])
         render(stereoEyeTranslation);
         glFlush();
         
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(screen);
         break;
         
       case StereoRedGreen: // Red-green anaglyph stereo
@@ -596,7 +678,7 @@ int PApp::run(int argc, char *argv[])
         render(stereoEyeTranslation);
         glFlush();
         
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(screen);
         break;
         
       case StereoRedCyan: // Red-cyan anaglyph stereo
@@ -609,7 +691,7 @@ int PApp::run(int argc, char *argv[])
         render(stereoEyeTranslation);
         glFlush();
         
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(screen);
         break;
         
       case StereoYellowBlue: // Yellow-blue anaglyph stereo
@@ -622,7 +704,7 @@ int PApp::run(int argc, char *argv[])
         render(stereoEyeTranslation);
         glFlush();
         
-        SDL_GL_SwapBuffers();
+        SDL_GL_SwapWindow(screen);
         break;
       }
       repaint = false;
@@ -675,10 +757,13 @@ int PApp::run(int argc, char *argv[])
     sslist.pop_back();
   }
   
-  SDL_WM_GrabInput(SDL_GRAB_OFF);
+  //SDL_WM_GrabInput(SDL_GRAB_OFF);
+  //SDL_SetWindowGrab(screen, SDL_FALSE);
   SDL_ShowCursor(SDL_ENABLE);
   
-  SDL_Quit();
+    SDL_GL_DeleteContext(context);
+    SDL_DestroyWindow(screen);
+    SDL_Quit();
   
   best_times.savePlayer();
   
diff --git a/src/PEngine/model.cpp b/src/PEngine/model.cpp
index 2c41241..6b91b3b 100644
--- a/src/PEngine/model.cpp
+++ b/src/PEngine/model.cpp
@@ -289,7 +289,7 @@ void PModel::loadOBJ(const std::string &filename, float globalScale)
          }
          else
          {
-            PUtil::outLog () << "Warning: unknow token \"" << tok
+            PUtil::outLog () << "Warning: unknown token \"" << tok
                << "\" in file \"" << filename << "\"" << std::endl;
          }
       }
diff --git a/src/PEngine/physfs_rw.cpp b/src/PEngine/physfs_rw.cpp
index 1f94c75..7716cef 100644
--- a/src/PEngine/physfs_rw.cpp
+++ b/src/PEngine/physfs_rw.cpp
@@ -7,15 +7,20 @@
 
 #include "pengine.h"
 
+Sint64 physfs_size(SDL_RWops *context)
+{
+    PHYSFS_file *pfile = (PHYSFS_file *)context->hidden.unknown.data1;
 
+    return PHYSFS_fileLength(pfile);
+}
 
-int physfs_seek(SDL_RWops *context, int offset, int whence)
+Sint64 physfs_seek(SDL_RWops *context, Sint64 offset, int whence)
 {
   PHYSFS_file *pfile = (PHYSFS_file *)context->hidden.unknown.data1;
   
-  int target;
+  Sint64 target;
   
-  int curpos = PHYSFS_tell(pfile);
+  Sint64 curpos = PHYSFS_tell(pfile);
   
   switch (whence) {
   default:
@@ -30,7 +35,7 @@ int physfs_seek(SDL_RWops *context, int offset, int whence)
     break;
   }
   
-    int result = PHYSFS_seek(pfile, target);
+    Sint64 result = PHYSFS_seek(pfile, target);
     if (! result) {
         throw MakePException("Error seeking: " + PHYSFS_getLastError());
     }
@@ -43,15 +48,18 @@ int physfs_seek(SDL_RWops *context, int offset, int whence)
 }
 
 
-int physfs_read(SDL_RWops *context, void *ptr, int size, int maxnum)
+size_t physfs_read(SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
 {
   PHYSFS_file *pfile = (PHYSFS_file *)context->hidden.unknown.data1;
   
-  return PHYSFS_read(pfile, ptr, size, maxnum);
+  const Sint64 r = PHYSFS_read(pfile, ptr, size, maxnum);
+  
+  // reading 0 bytes is considered an error now, thanks SDL2!
+  return r == -1 ? 0 : r;
 }
 
 
-int physfs_write(SDL_RWops *context, const void *ptr, int size, int num)
+size_t physfs_write(SDL_RWops *context, const void *ptr, size_t size, size_t num)
 {
   PHYSFS_file *pfile = (PHYSFS_file *)context->hidden.unknown.data1;
   
diff --git a/src/PEngine/terrain.cpp b/src/PEngine/terrain.cpp
index 1e4cb43..758d20a 100644
--- a/src/PEngine/terrain.cpp
+++ b/src/PEngine/terrain.cpp
@@ -25,62 +25,68 @@ void PTerrain::unload()
 }
 
 
-PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTexture &ssTexture) :
+PTerrain::PTerrain (XMLElement *element, const std::string &filepath, PSSTexture &ssTexture) :
   loaded (false)
 {
   unload();
-  
+
   std::string heightmap, colormap, terrainmap, roadmap, foliagemap, hudmap;
-  
+
   scale_hz = 1.0;
   scale_vt = 1.0;
-  
+
   const char *val;
-  
+
   val = element->Attribute("tilesize");
   if (val) tilesize = atoi(val);
-  
+
   val = element->Attribute("horizontalscale");
   if (val) scale_hz = atof(val);
-  
+
   val = element->Attribute("verticalscale");
   if (val) scale_vt = atof(val);
-  
+
   val = element->Attribute("heightmap");
   if (val) heightmap = val;
-  
+
   val = element->Attribute("colormap");
   if (val) colormap = val;
-  
+
   val = element->Attribute("terrainmap");
   if (val != nullptr) terrainmap = val;
-  
+
   val = element->Attribute("roadmap");
   if (val != nullptr) roadmap = val;
-  
+
   val = element->Attribute("foliagemap");
   if (val && MainApp::cfg_foliage) foliagemap = val;
-  
+
   val = element->Attribute("hudmap");
   if (val) hudmap = val;
 
-    TiXmlNode *bfnode;
+    XMLElement *node = element->FirstChildElement("blurfilter");
     std::vector<std::vector<float> > blurfilter;
 
-    if ((bfnode = element->FirstChild("blurfilter")) != nullptr)
+    if (node != nullptr)
     {
-        for (TiXmlElement *walk = bfnode->FirstChildElement(); walk; walk = walk->NextSiblingElement())
-            if (!strcmp(walk->Value(), "row"))
-            {
-                std::stringstream bfrow(std::string(walk->Attribute("data")));
-                float elem;
-                std::vector<float> row;
-
-                while (bfrow >> elem)
-                    row.push_back(elem);
-
-                blurfilter.push_back(row);
-            }
+        for (XMLElement *walk = node->FirstChildElement("row");
+            walk != nullptr;
+            walk = walk->NextSiblingElement("row"))
+        {
+            const char *srow = walk->Attribute("data");
+            
+            if (srow == nullptr)
+                continue;
+
+            std::stringstream bfrow(srow);
+            float coef;
+            std::vector<float> row;
+
+            while (bfrow >> coef)
+                row.push_back(coef);
+
+            blurfilter.push_back(row);
+        }
     }
     else
     {
@@ -91,11 +97,68 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
         };
     }
 
-  for (TiXmlElement *walk = element->FirstChildElement();
+  for (XMLElement *walk = element->FirstChildElement();
     walk; walk = walk->NextSiblingElement()) {
 
+    if (strcmp(walk->Value(), "roadsign") == 0 && MainApp::cfg_roadsigns)
+    {
+        road_sign temprs;
+/*
+        val = walk->Attribute("front");
+
+        if (val != nullptr)
+            temprs.front = ssTexture.loadTexture(PUtil::assemblePath(val, filepath));
+
+        val = walk->Attribute("back");
+
+        if (val != nullptr)
+            temprs.back = ssTexture.loadTexture(PUtil::assemblePath(val, filepath));
+*/
+        val = walk->Attribute("sprite");
+
+        if (val != nullptr)
+            temprs.sprite = ssTexture.loadTexture(PUtil::assemblePath(val, filepath));
+
+        val = walk->Attribute("scale");
+
+        if (val != nullptr)
+            temprs.scale = atof(val);
+
+        for (XMLElement *walk2 = walk->FirstChildElement();
+            walk2 != nullptr;
+            walk2 = walk2->NextSiblingElement())
+        {
+            if (strcmp(walk2->Value(), "location") == 0)
+            {
+                float deg = 0;
+
+                val = walk2->Attribute("oridegrees");
+
+                if (val != nullptr)
+                    deg = RADIANS(atof(val));
+
+                val = walk2->Attribute("coords");
+
+                if (val != nullptr)
+                {
+                    float x, y;
+
+                    if (sscanf(val, "%f, %f", &x, &y) == 2)
+                    {
+                        temprs.x = x;
+                        temprs.y = y;
+                        temprs.deg = deg;
+
+                        if (temprs.sprite != nullptr)
+                            roadsigns.push_back(temprs);
+                    }
+                }
+            }
+        }
+    }
+    else
     if (!strcmp(walk->Value(), "foliageband") && MainApp::cfg_foliage) {
-      
+
       PTerrainFoliageBand tfb;
       tfb.middle = 0.5f;
       tfb.range = 0.5f;
@@ -107,16 +170,16 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
       //tfb.modelscale = 1.0f;
       tfb.sprite_tex = nullptr;
       tfb.sprite_count = 1;
-      
+
       val = walk->Attribute("middle");
       if (val) tfb.middle = atof(val);
-      
+
       val = walk->Attribute("range");
       if (val) tfb.range = atof(val);
-      
+
       val = walk->Attribute("density");
       if (val) tfb.density = atof(val);
-      
+
       val = walk->Attribute("scale");
       if (val) tfb.scale = atof(val);
 
@@ -130,43 +193,43 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
       /*
       val = walk->Attribute("model");
       if (val) tfb.model = ssModel.loadModel(PUtil::assemblePath(val, filepath));
-      
+
       val = walk->Attribute("modelscale");
       if (val) tfb.modelscale = atof(val);
       */
-      
+
       val = walk->Attribute("sprite");
       if (val) tfb.sprite_tex = ssTexture.loadTexture(PUtil::assemblePath(val, filepath));
-      
+
       val = walk->Attribute("spritecount");
       if (val) tfb.sprite_count = atoi(val);
-      
+
       foliageband.push_back(tfb);
     }
   }
-  
-  
+
+
   if (!heightmap.length()) {
     throw MakePException ("Load failed: terrain has no heightmap");
   }
-  
+
   if (!colormap.length()) {
     throw MakePException ("Load failed: terrain has no colormap");
   }
-  
+
   if (tilesize != (tilesize & (-tilesize)) ||
     tilesize < 4) {
     throw MakePException ("Load failed: tile size not power of two dimension, or too small");
   }
-  
+
   if (scale_hz <= 0.0 || scale_vt == 0.0) {
     throw MakePException ("Load failed: invalid scale value");
   }
-  
+
   scale_hz_inv = 1.0 / scale_hz;
   scale_vt_inv = 1.0 / scale_vt;
   scale_tile_inv = scale_hz_inv / (float)tilesize;
-  
+
   PImage img;
   try
   {
@@ -177,25 +240,25 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
     PUtil::outLog() << "Load failed: couldn't open heightmap \"" << heightmap << "\"\n";
     throw;
   }
-  
+
   totsize = img.getcx();
   if (totsize != img.getcy() ||
     totsize != (totsize & (-totsize)) ||
     totsize < 16) {
     throw MakePException ("Load failed: heightmap not square, or not power of two dimension, or too small");
   }
-  
+
   totsizesq = totsize * totsize;
-  
+
   if (tilesize > totsize) tilesize = totsize;
-  
+
   tilecount = totsize / tilesize;
   totmask = totsize - 1;
-  
+
   //PUtil::outLog() << "img: " << totsize << " squared, " << img.getcc() << " cc\n";
-  
+
   hmap.resize(totsizesq);
-  
+
 #if 0
   if (img.getcc() != 1) {
     if (PUtil::isDebugLevel(DEBUGLEVEL_TEST))
@@ -211,10 +274,10 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
     if (PUtil::isDebugLevel(DEBUGLEVEL_TEST))
       PUtil::outLog() << "Warning: heightmap is not single channel\n";
   }
-  
+
   int cc = img.getcc();
   uint8 *dat = img.getData();
-  
+
   for (int y=0; y<totsize; ++y) {
     for (int x=0; x<totsize; ++x) {
       float accum = 0.0;
@@ -229,9 +292,9 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
     }
   }
 #endif
-  
+
   img.unload();
-  
+
   try
   {
     cmap.load(PUtil::assemblePath(colormap, filepath));
@@ -241,17 +304,17 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
     PUtil::outLog() << "Load failed: couldn't open colormap \"" << colormap << "\"\n";
     throw;
   }
-  
+
   cmaptotsize = cmap.getcx();
   if (cmaptotsize != cmap.getcy() ||
     cmaptotsize != (cmaptotsize & (-cmaptotsize)) ||
     cmaptotsize < tilecount) {
     throw MakePException ("Load failed: colormap not square, or not power of two dimension, or too small");
   }
-  
+
   cmaptilesize = cmaptotsize / tilecount;
   cmaptotmask = cmaptotsize - 1;
-  
+
   // load terrain map image
   try
   {
@@ -264,8 +327,8 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
     throw;
   }
 
-  if (tmap.getData() != nullptr && tmap.getcx() != tmap.getcy())
-    throw MakePException("Load failed: terrainmap not square");
+    if (tmap.getData() != nullptr && tmap.getcx() != tmap.getcy())
+        throw MakePException("Load failed: terrainmap not square");
 
     PImage rmap_img;
 
@@ -291,16 +354,16 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
     }
 
   // calculate foliage try counts for tile size
-  
+
   for (unsigned int b = 0; b < foliageband.size(); b++) {
     foliageband[b].trycount =
       (int) (foliageband[b].density * (float)totsizesq * scale_hz * scale_hz);
   }
-  
+
   // load foliage map
-  
+
   fmap.resize(totsizesq, 0.0f);
-  
+
   if (foliagemap.length()) {
     try
     {
@@ -311,39 +374,39 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
       PUtil::outLog() << "Load failed: couldn't open foliage map \"" << foliagemap << "\"\n";
       throw;
     }
-    
+
     if (totsize != img.getcy() ||
       totsize != img.getcx()) {
       throw MakePException ("Load failed: foliage map size doesn't match heightmap");
     }
-    
+
     int cc = img.getcc();
     uint8 *dat = img.getData();
-    
+
     if (cc != 1) {
       if (PUtil::isDebugLevel(DEBUGLEVEL_TEST))
         PUtil::outLog() << "Warning: foliage map is not single channel\n";
     }
-    
+
     for (int i = 0; i < totsizesq; i++) {
       fmap[i] = (float) dat[i * cc] / 255.0f;
     }
   }
-  
+
   // load hud map
-  
+
   tex_hud_map = nullptr;
-  
+
   if (hudmap.length()) {
     tex_hud_map = ssTexture.loadTexture(PUtil::assemblePath(hudmap, filepath));
   }
-  
+
   // prepare shared index buffers
-  
+
   int tilesizep1 = tilesize + 1;
-  
+
   numinds = 0;
-  
+
   PRamFile ramfile;
   uint16 index;
   for (int y=0; y<tilesize; ++y) {
@@ -366,10 +429,10 @@ PTerrain::PTerrain (TiXmlElement *element, const std::string &filepath, PSSTextu
       numinds += 1;
     }
   }
-  
+
   ind.create(ramfile.getSize(), PVBuffer::IndexContent, PVBuffer::StaticUsage, ramfile.getData());
   ramfile.clear();
-  
+
   loaded = true;
 }
 
@@ -409,15 +472,15 @@ PTerrainTile *PTerrain::getTile(int tilex, int tiley)
 
   tileptr->mins = vec3f((float)tilex * scale_hz, (float)tiley * scale_hz, 1000000000.0);
   tileptr->maxs = vec3f((float)(tilex+1) * scale_hz, (float)(tiley+1) * scale_hz, -1000000000.0);
-  
+
   // TODO: quadtree based thing
-  
+
   //std::vector<bool>
-  
+
   static PRamFile ramfile1, ramfile2;
-  
+
   ramfile1.clear();
-  
+
   int tileoffsety = tiley * tilesize;
   int tileoffsetx = tilex * tilesize;
   int tilesizep1 = tilesize + 1;
@@ -446,33 +509,33 @@ PTerrainTile *PTerrain::getTile(int tilex, int tiley)
   //tileptr->maxs = vec3f((float)(tilex+1) * scale_hz, (float)(tiley+1) * scale_hz, 100.0);
 
   tileptr->numverts = tilesizep1 * tilesizep1;
-  
+
   tileptr->tex.loadPiece(cmap,
     (tilex * cmaptilesize) & cmaptotmask, (tiley * cmaptilesize) & cmaptotmask,
     cmaptilesize, cmaptilesize, true, true);
-  
+
   // Create foliage
-  
+
   srand(1);
-  
+
   tileptr->foliage.resize(foliageband.size());
-  
+
   for (unsigned int b = 0; b < foliageband.size(); b++) {
-    
+
     tileptr->foliage[b].inst.clear();
-    
+
     // Create foliage instances
-    
+
     for (int i = 0; i < foliageband[b].trycount; i++) {
-      
+
       vec2f ftry = vec2f(
         (float)((tileptr->posx * tilesize) + rand01 * tilesize) * scale_hz,
         (float)((tileptr->posy * tilesize) + rand01 * tilesize) * scale_hz);
-        
+
       float fol = getFoliageLevel(ftry.x, ftry.y);
-      
+
       if ((1.0 - fabs((fol - foliageband[b].middle) / foliageband[b].range)) < rand01) continue;
-      
+
       tileptr->foliage[b].inst.push_back(PTerrainFoliage());
       tileptr->foliage[b].inst.back().pos.x = ftry.x;
       tileptr->foliage[b].inst.back().pos.y = ftry.y;
@@ -482,49 +545,49 @@ PTerrainTile *PTerrain::getTile(int tilex, int tiley)
       //tileptr->foliage[b].inst.back().scale = (foliageband[b].scalemin + fol * 0.5f) * (rand01 * rand01 + 0.5) * foliageband[b].scalemax;
       tileptr->foliage[b].inst.back().scale = (foliageband[b].scale + fol * 0.5f) * (rand01 * rand01 + 0.5) * 1.4;
     }
-    
+
     // Create vertex buffers for rendering
-    
+
 #define HMULT   1.0
 #define VMULT   2.0
-    
+
     ramfile1.clear();
     ramfile2.clear();
-    
+
     tileptr->foliage[b].numvert = 0;
     tileptr->foliage[b].numelem = 0;
-    
+
     float angincr = PI / (float)foliageband[b].sprite_count;
     for (unsigned int j=0; j<tileptr->foliage[b].inst.size(); j++) {
       for (float anga = 0.0f; anga < PI - 0.01f; anga += angincr) {
         float interang = tileptr->foliage[b].inst[j].ang + anga;
         int stv = tileptr->foliage[b].numvert;
         PVert_tv tmpv;
-        
+
         tmpv.xyz = tileptr->foliage[b].inst[j].pos +
           vec3f(cos(interang)*HMULT,sin(interang)*HMULT,0.0f) * tileptr->foliage[b].inst[j].scale;
         tmpv.st = vec2f(1.0f,0.0f);
         ramfile1.write(&tmpv,sizeof(PVert_tv));
         tileptr->foliage[b].numvert++;
-        
+
         tmpv.xyz = tileptr->foliage[b].inst[j].pos +
           vec3f(-cos(interang)*HMULT,-sin(interang)*HMULT,0.0f) * tileptr->foliage[b].inst[j].scale;
         tmpv.st = vec2f(0.0f,0.0f);
         ramfile1.write(&tmpv,sizeof(PVert_tv));
         tileptr->foliage[b].numvert++;
-        
+
         tmpv.xyz = tileptr->foliage[b].inst[j].pos +
           vec3f(-cos(interang)*HMULT,-sin(interang)*HMULT,VMULT) * tileptr->foliage[b].inst[j].scale;
-        tmpv.st = vec2f(0.0f,1.0f-1.0f/32.0f);
+        tmpv.st = vec2f(0.0f,1.0f/*-1.0f/32.0f*/);
         ramfile1.write(&tmpv,sizeof(PVert_tv));
         tileptr->foliage[b].numvert++;
-        
+
         tmpv.xyz = tileptr->foliage[b].inst[j].pos +
           vec3f(cos(interang)*HMULT,sin(interang)*HMULT,VMULT) * tileptr->foliage[b].inst[j].scale;
-        tmpv.st = vec2f(1.0f,1.0f-1.0f/32.0f);
+        tmpv.st = vec2f(1.0f,1.0f/*-1.0f/32.0f*/);
         ramfile1.write(&tmpv,sizeof(PVert_tv));
         tileptr->foliage[b].numvert++;
-        
+
         int ind;
         ind = stv + 0;
         ramfile2.write(&ind,sizeof(uint32));
@@ -546,7 +609,7 @@ PTerrainTile *PTerrain::getTile(int tilex, int tiley)
         tileptr->foliage[b].numelem++;
       }
     }
-    
+
     if (tileptr->foliage[b].numelem) {
       tileptr->foliage[b].buff[0].create(ramfile1.getSize(),
         PVBuffer::VertexContent, PVBuffer::StaticUsage, ramfile1.getData());
@@ -554,7 +617,103 @@ PTerrainTile *PTerrain::getTile(int tilex, int tiley)
         PVBuffer::IndexContent, PVBuffer::StaticUsage, ramfile2.getData());
     }
   }
-  
+
+    tileptr->roadsignset.resize(roadsigns.size());
+
+    for (unsigned int b=0; b < roadsigns.size(); ++b)
+    {
+/*
+        vec2f ftry = vec2f(
+            (float)((tileptr->posx * tilesize) + roadsigns[b].x) * scale_hz,
+            (float)((tileptr->posy * tilesize) + roadsigns[b].y) * scale_hz);
+*/
+        vec2f ftry = vec2f(
+            roadsigns[b].x * scale_hz,
+            roadsigns[b].y * scale_hz);
+
+        tileptr->roadsignset[b].inst.clear();
+        tileptr->roadsignset[b].inst.push_back(PTerrainFoliage());
+        tileptr->roadsignset[b].inst.back().pos.x    = ftry.x;
+        tileptr->roadsignset[b].inst.back().pos.y    = ftry.y;
+        tileptr->roadsignset[b].inst.back().pos.z    = getHeight(ftry.x, ftry.y);
+        tileptr->roadsignset[b].inst.back().ang      = roadsigns[b].deg;
+        tileptr->roadsignset[b].inst.back().scale    = roadsigns[b].scale;
+
+        ramfile1.clear();
+        ramfile2.clear();
+
+        tileptr->roadsignset[b].numvert = 0;
+        tileptr->roadsignset[b].numelem = 0;
+
+        float angincr = PI / 1.0f;
+
+        for (unsigned int j=0; j<tileptr->roadsignset[b].inst.size(); j++)
+        {
+            for (float anga = 0.0f; anga < PI - 0.01f; anga += angincr)
+            {
+                float interang = tileptr->roadsignset[b].inst[j].ang + anga;
+                int stv = tileptr->roadsignset[b].numvert;
+                PVert_tv tmpv;
+
+                tmpv.xyz = tileptr->roadsignset[b].inst[j].pos +
+                    vec3f(cos(interang)*HMULT,sin(interang)*HMULT,0.0f) *
+                    tileptr->roadsignset[b].inst[j].scale;
+                tmpv.st = vec2f(1.0f,0.0f);
+                ramfile1.write(&tmpv,sizeof(PVert_tv));
+                tileptr->roadsignset[b].numvert++;
+
+                tmpv.xyz = tileptr->roadsignset[b].inst[j].pos +
+                    vec3f(-cos(interang)*HMULT,-sin(interang)*HMULT,0.0f) *
+                    tileptr->roadsignset[b].inst[j].scale;
+                tmpv.st = vec2f(0.0f,0.0f);
+                ramfile1.write(&tmpv,sizeof(PVert_tv));
+                tileptr->roadsignset[b].numvert++;
+
+                tmpv.xyz = tileptr->roadsignset[b].inst[j].pos +
+                    vec3f(-cos(interang)*HMULT,-sin(interang)*HMULT,VMULT) *
+                    tileptr->roadsignset[b].inst[j].scale;
+                tmpv.st = vec2f(0.0f,1.0f/*-1.0f/32.0f*/);
+                ramfile1.write(&tmpv,sizeof(PVert_tv));
+                tileptr->roadsignset[b].numvert++;
+
+                tmpv.xyz = tileptr->roadsignset[b].inst[j].pos +
+                    vec3f(cos(interang)*HMULT,sin(interang)*HMULT,VMULT) *
+                    tileptr->roadsignset[b].inst[j].scale;
+                tmpv.st = vec2f(1.0f,1.0f/*-1.0f/32.0f*/);
+                ramfile1.write(&tmpv,sizeof(PVert_tv));
+                tileptr->roadsignset[b].numvert++;
+
+                int ind;
+                ind = stv + 0;
+                ramfile2.write(&ind,sizeof(uint32));
+                tileptr->roadsignset[b].numelem++;
+                ind = stv + 1;
+                ramfile2.write(&ind,sizeof(uint32));
+                tileptr->roadsignset[b].numelem++;
+                ind = stv + 2;
+                ramfile2.write(&ind,sizeof(uint32));
+                tileptr->roadsignset[b].numelem++;
+                ind = stv + 0;
+                ramfile2.write(&ind,sizeof(uint32));
+                tileptr->roadsignset[b].numelem++;
+                ind = stv + 2;
+                ramfile2.write(&ind,sizeof(uint32));
+                tileptr->roadsignset[b].numelem++;
+                ind = stv + 3;
+                ramfile2.write(&ind,sizeof(uint32));
+                tileptr->roadsignset[b].numelem++;
+            }
+        }
+
+        if (tileptr->roadsignset[b].numelem)
+        {
+            tileptr->roadsignset[b].buff[0].create(ramfile1.getSize(),
+                PVBuffer::VertexContent, PVBuffer::StaticUsage, ramfile1.getData());
+            tileptr->roadsignset[b].buff[1].create(ramfile2.getSize(),
+                PVBuffer::IndexContent, PVBuffer::StaticUsage, ramfile2.getData());
+        }
+    }
+
   //PUtil::outLog() << "2: " << tileptr->posx << " " << tileptr->posy << std::endl;
   return tileptr;
 }
@@ -563,7 +722,7 @@ PTerrainTile *PTerrain::getTile(int tilex, int tiley)
 void PTerrain::render(const vec3f &campos, const mat44f &camorim)
 {
   float blah = camorim.row[0][0]; blah = blah; // unused
-  
+
   // increase all lru counters
   for (std::list<PTerrainTile>::iterator iter = tile.begin();
     iter != tile.end(); ++iter) ++iter->lru_counter;
@@ -590,19 +749,19 @@ void PTerrain::render(const vec3f &campos, const mat44f &camorim)
     maxtx = ctx + 4,
     minty = cty - 3,
     maxty = cty + 4;
-  
+
   // Determine list of tiles to draw
-  
+
   std::list<PTerrainTile *> drawtile;
-  
+
   for (int ty = minty; ty < maxty; ++ty) {
     for (int tx = mintx; tx < maxtx; ++tx) {
       drawtile.push_back(getTile(tx,ty));
     }
   }
-  
+
   // Draw terrain
-  
+
   glEnable(GL_TEXTURE_GEN_S);
   glEnable(GL_TEXTURE_GEN_T);
 
@@ -658,28 +817,28 @@ void PTerrain::render(const vec3f &campos, const mat44f &camorim)
   glColor3f(1.0f, 1.0f, 1.0f);
   glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   glEnableClientState(GL_VERTEX_ARRAY);
-  
+
   for (unsigned int b = 0; b < foliageband.size(); b++) {
-    
+
     foliageband[b].sprite_tex->bind();
-    
+
     for (std::list<PTerrainTile *>::iterator t = drawtile.begin(); t != drawtile.end(); t++) {
-      
+
       if ((*t)->foliage[b].numelem) {
         (*t)->foliage[b].buff[0].bind(); // vert data
         (*t)->foliage[b].buff[1].bind(); // indices
-        
+
         glTexCoordPointer(2, GL_FLOAT, sizeof(PVert_tv), (*t)->foliage[b].buff[0].getPointer(0));
         glVertexPointer(3, GL_FLOAT, sizeof(PVert_tv), (*t)->foliage[b].buff[0].getPointer(sizeof(float)*2));
-        
+
         glDrawRangeElements(GL_TRIANGLES,
           0,(*t)->foliage[b].numvert,(*t)->foliage[b].numelem,
           GL_UNSIGNED_INT,(*t)->foliage[b].buff[1].getPointer(0));
       }
-      
+
       #if 0
       for (std::vector<PTerrainFoliage>::iterator f = (*t)->foliage.begin(); f != (*t)->foliage.end(); f++) {
-        
+
         #if 0
         glBegin(GL_LINES);
         vec3f pos = f->pos;
@@ -688,10 +847,10 @@ void PTerrain::render(const vec3f &campos, const mat44f &camorim)
         glVertex3fv(pos);
         glEnd();
         #endif
-        
+
         #if 0
         if (!f->tfb->model) continue;
-        
+
         glPushMatrix();
         vec3f &pos = f->pos;
         glTranslatef(pos.x, pos.y, pos.z);
@@ -703,8 +862,36 @@ void PTerrain::render(const vec3f &campos, const mat44f &camorim)
       #endif
     }
   }
+
+    PVBuffer::unbind();
+
+    // draw road signs
+    for (unsigned int b=0; b < roadsigns.size(); ++b)
+    {
+        roadsigns[b].sprite->bind();
+
+        if (!drawtile.empty())
+        //for (PTerrainTile *t: drawtile)
+        {
+            PTerrainTile *t = drawtile.front();
+
+            if (t->roadsignset[b].numelem)
+            {
+                t->roadsignset[b].buff[0].bind();
+                t->roadsignset[b].buff[1].bind();
+
+                glTexCoordPointer(2, GL_FLOAT, sizeof(PVert_tv), t->roadsignset[b].buff[0].getPointer(0));
+                glVertexPointer(3, GL_FLOAT, sizeof(PVert_tv), t->roadsignset[b].buff[0].getPointer(sizeof(float)*2));
+
+                glDrawRangeElements(GL_TRIANGLES, 0,
+                    t->roadsignset[b].numvert, t->roadsignset[b].numelem,
+                    GL_UNSIGNED_INT, t->roadsignset[b].buff[1].getPointer(0));
+            }
+        }
+    }
+
   #endif
-  
+
   PVBuffer::unbind();
   glDisableClientState(GL_TEXTURE_COORD_ARRAY);
   glDisableClientState(GL_VERTEX_ARRAY);
@@ -717,12 +904,12 @@ void PTerrain::drawSplat(float x, float y, float scale, float angle)
   float *hmd = &hmap[0];
   int cx = totsize;
   int cy = totsize;
-  
+
   x *= scale_hz_inv;
   y *= scale_hz_inv;
-  
+
   scale *= 0.5f;
-  
+
   int miny = (int)(y - scale);
   if ((y - scale) < 0.0f) miny--;
   int maxy = (int)(y + scale) + 1;
@@ -731,21 +918,21 @@ void PTerrain::drawSplat(float x, float y, float scale, float angle)
   if ((x - scale) < 0.0f) minx--;
   int maxx = (int)(x + scale) + 2;
   if ((x + scale) < 0.0f) maxx--;
-  
+
   glMatrixMode(GL_TEXTURE);
-  
+
   glPushMatrix();
   glTranslatef(0.5f, 0.5f, 0.0f);
   glRotatef(DEGREES(angle), 0.0f, 0.0f, 1.0f);
   glScalef(0.5f / scale, 0.5f / scale, 1.0f);
   glTranslatef(-x, -y, 0.0f);
-  
+
   for (int y2=miny; y2<maxy; y2++) {
     int yc = y2 & (cy-1),
       ycp1 = (yc+1) & (cy-1),
       yc_cx = yc * cx,
       ycp1_cx = ycp1 * cx;
-    
+
     glBegin(GL_TRIANGLE_STRIP);
     for (int x2=minx; x2<maxx; x2++) {
       int xc = x2 & (cx - 1);
@@ -757,7 +944,7 @@ void PTerrain::drawSplat(float x, float y, float scale, float angle)
     glEnd();
   }
   glPopMatrix();
-  
+
   glMatrixMode(GL_MODELVIEW);
 }
 
diff --git a/src/PEngine/texture.cpp b/src/PEngine/texture.cpp
index 99e22a7..6a34071 100644
--- a/src/PEngine/texture.cpp
+++ b/src/PEngine/texture.cpp
@@ -9,7 +9,7 @@
 
 #include "main.h"
 
-#include <SDL/SDL_image.h>
+#include <SDL2/SDL_image.h>
 
 
 
diff --git a/src/PEngine/util.cpp b/src/PEngine/util.cpp
index 876f287..032fab2 100644
--- a/src/PEngine/util.cpp
+++ b/src/PEngine/util.cpp
@@ -132,20 +132,20 @@ char* PUtil::fgets2(char *s, int size, PHYSFS_file *pfile)
 {
   int i;
   for (i = 0; i < size-1; i++) {
-    
+
     // check for EOF
     if (PHYSFS_eof(pfile)) return nullptr;
-    
+
     int ret = PHYSFS_read(pfile, s + i, sizeof (char), 1);
-    
+
     if (s[i] == '\n') break;
-    
+
     if (ret == -1) return nullptr; // major error
     if (ret == 0) { i--; break; } // er, must be end of file anyway
   }
-  
+
   s[i+1] = '\0';
-  
+
   return s;
 }
 
@@ -161,9 +161,9 @@ std::string PUtil::extractPathFromFilename(const std::string &filename)
 std::string PUtil::assemblePath(const std::string &relativefile, const std::string &parentfile)
 {
   // This function is required because PhysFS doesn't allow ".."s in path names
-  
+
   std::string totalpath;
-  
+
   if (relativefile[0] == '/') {
     // relativefile is an absolute path
     totalpath = relativefile.substr(1);
@@ -171,86 +171,67 @@ std::string PUtil::assemblePath(const std::string &relativefile, const std::stri
     // relativefile is indeed relative, so concat with parent path
     totalpath = extractPathFromFilename(parentfile) + relativefile;
   }
-  
+
   std::string totalpath_hold = totalpath;
-  
+
   std::string::size_type found;
-  
+
   while (true) {
-    
+
     // attempt to find a ".." to crunch back
-    
+
     found = totalpath.find("../");
-    
+
     if (found == std::string::npos) break; // no more ".."s
-    
+
     if (found < 2) {
       // ".." is too close to start of path
       PUtil::outLog() << "Error: above local root: \"" << totalpath_hold << "\"" << std::endl;
       return std::string();
     }
-    
+
     std::string::size_type crunch = totalpath.substr(0, found-1).find_last_of('/');
-    
+
     if (crunch == std::string::npos) {
       // Crunch back to root (special case)
       crunch = 0;
       found += 1;
     }
-    
+
     // remove the "/dir/.." chunk
-    
+
     totalpath.erase(crunch, found + 2 - crunch);
   }
-  
+
   return totalpath;
 }
 
 
-TiXmlElement *PUtil::loadRootElement(TiXmlDocument &doc, const char *rootName)
+XMLElement *PUtil::loadRootElement(XMLDocument &doc, const std::string &filename, const char *rootName)
 {
-  // Old direct file code
-  /*
-  if (!doc.LoadFile()) {
-    PUtil::outLog() << "Load failed: TinyXML couldn't load file, possibly file not found or invalid XML" << std::endl;
-    PUtil::outLog() << "TinyXML: " << xmlfile.ErrorDesc() << std::endl;
-    return nullptr;
-  }
-  */
-  
-  // New PhysFS handler
-  
-  PHYSFS_file *pfile = PHYSFS_openRead(doc.Value());
+  PHYSFS_file *pfile = PHYSFS_openRead(filename.c_str());
   if (pfile == nullptr) {
     PUtil::outLog() << "Load failed: PhysFS: " << PHYSFS_getLastError() << std::endl;
     return nullptr;
   }
-  
+
   int filesize = PHYSFS_fileLength(pfile);
-  
+
   char *xmlbuffer = new char[filesize + 1];
-  
+
   PHYSFS_read(pfile, xmlbuffer, sizeof (char), filesize);
   PHYSFS_close(pfile);
-  
+
   xmlbuffer[filesize] = '\0';
-  
+
   doc.Parse(xmlbuffer);
-  
+
   delete [] xmlbuffer;
-  
-  TiXmlNode *rootnode;
-  rootnode = doc.FirstChild(rootName);
-  if (!rootnode) {
-    PUtil::outLog() << "Couldn't process <" << rootName << "> element, possibly invalid file type" << std::endl;
-    PUtil::outLog() << "TinyXML: " << doc.ErrorDesc() << std::endl;
-    return nullptr;
-  }
 
-  TiXmlElement *rootelem = rootnode->ToElement();
+  XMLElement *rootelem = doc.FirstChildElement(rootName);
   if (!rootelem) {
     PUtil::outLog() << "Load failed: TinyXML error" << std::endl;
-    PUtil::outLog() << "TinyXML: " << doc.ErrorDesc() << std::endl;
+    PUtil::outLog() << "TinyXML: " << doc.GetErrorStr1() << ", " << doc.GetErrorStr2() << std::endl;
     return nullptr;
   }
 
@@ -261,24 +242,24 @@ TiXmlElement *PUtil::loadRootElement(TiXmlDocument &doc, const char *rootName)
 bool PUtil::copyFile(const std::string &fileFrom, const std::string &fileTo)
 {
   PUtil::outLog() << "Copying \"" << fileFrom << "\" to \"" << fileTo << "\"" << std::endl;
-  
+
   // PhysFS doesn't implement copy, so do it the hard way
-  
+
   PHYSFS_file *pfile_from, *pfile_to;
-  
+
   // Open source file
-  
+
   pfile_from = PHYSFS_openRead(fileFrom.c_str());
-  
+
   if (!pfile_from) {
     PUtil::outLog() << "Copy failed: PhysFS: " << PHYSFS_getLastError() << std::endl;
     return false;
   }
-  
+
   // Make dest directory
-  
+
   std::string filepath = extractPathFromFilename(fileTo);
-  
+
   if (!PHYSFS_mkdir(filepath.c_str())) {
     const char *errstr = PHYSFS_getLastError();
     if (strcmp(errstr, "File exists")) {
@@ -286,95 +267,95 @@ bool PUtil::copyFile(const std::string &fileFrom, const std::string &fileTo)
       PUtil::outLog() << "PhysFS: " << errstr << std::endl;
     }
   }
-  
+
   // Open dest file
-  
+
   pfile_to = PHYSFS_openWrite(fileTo.c_str());
-  
+
   if (!pfile_to) {
     PHYSFS_close(pfile_from);
     PUtil::outLog() << "Copy failed: PhysFS: " << PHYSFS_getLastError() << std::endl;
     return false;
   }
-  
+
   // Copy over the data in chunks
-  
+
   const int blocksize = 4096;
   char block[blocksize];
 
-  int readcount;  
+  int readcount;
   do {
-    
+
     readcount = PHYSFS_read(pfile_from, block, sizeof (char), blocksize);
-    
+
     PHYSFS_write(pfile_to, block, sizeof (char), readcount);
-    
+
   } while (readcount == blocksize);
-  
+
   // Close up shop
-  
+
   PHYSFS_close(pfile_from);
   PHYSFS_close(pfile_to);
-  
+
   return true;
 }
 
 std::list<std::string> PUtil::findFiles(const std::string &basedir, const std::string &extension)
 {
   std::list<std::string> results;
-  
+
   char **filelist = PHYSFS_enumerateFiles(basedir.c_str());
-  
+
   for (char **i = filelist; *i; i++) {
-    
+
     std::string thisfile = basedir + '/' + *i;
-    
+
     if (PHYSFS_isDirectory(thisfile.c_str())) {
-      
+
       // Recurse into subdirectory
       std::list<std::string> moreresults = findFiles(thisfile, extension);
       results.insert(results.end(), moreresults.begin(), moreresults.end());
-      
+
     } else {
-      
+
       // Check to see if file has correct extension
       if (thisfile.length() >= extension.length() &&
         thisfile.substr(thisfile.length() - extension.length()) == extension)
         results.push_back(thisfile);
     }
   }
-  
+
   PHYSFS_freeList(filelist);
-  
+
   return results;
 }
 
 std::string PUtil::formatInt(int value, int width)
 {
   std::string text(width, '0');
-  
+
   for (std::string::reverse_iterator c = text.rbegin(); c != text.rend(); c++) {
-    
+
     *c = '0' + (value % 10);
-    
+
     value /= 10;
   }
-  
+
   return text;
 }
 
 std::string PUtil::formatInt(int value)
 {
   std::string text;
-  
+
   bool neg = false;
-  
+
   if (value < 0) { neg = true; value = -value; }
   else if (value == 0) return std::string ("0");
-  
+
   for ( ; value; value /= 10)
     text = ((char)('0' + (value % 10))) + text;
-  
+
   return (neg) ? ('-' + text) : (text);
 }
 
@@ -385,7 +366,7 @@ std::string PUtil::formatTime(float seconds)
   int time_secs = (int)(seconds);
   seconds -= time_secs;
   int time_centis = (int)(seconds * 100.0f);
-  
+
   return
     formatInt(time_mins, 2) + ':' +
     formatInt(time_secs, 2) + '.' +
@@ -402,27 +383,30 @@ std::string PUtil::formatTimeShort(float seconds)
     const int time_mins = static_cast<int> (seconds / 60.0f);
     seconds -= time_mins * 60;
     const int time_secs = static_cast<int> (seconds);
-    
+
     return formatInt(time_mins) + ':' + formatInt(time_secs, 2);
 }
 
 // These are implemented over in physfs_rw.cpp
 
-int physfs_seek(SDL_RWops *context, int offset, int whence);
-int physfs_read(SDL_RWops *context, void *ptr, int size, int maxnum);
-int physfs_write(SDL_RWops *context, const void *ptr, int size, int num);
+Sint64 physfs_size(SDL_RWops *context);
+Sint64 physfs_seek(SDL_RWops *context, Sint64 offset, int whence);
+size_t physfs_read(SDL_RWops *context, void *ptr, size_t size, size_t maxnum);
+size_t physfs_write(SDL_RWops *context, const void *ptr, size_t size, size_t num);
 int physfs_close(SDL_RWops *context);
 
 SDL_RWops *PUtil::allocPhysFSops(PHYSFS_file *pfile)
 {
   SDL_RWops *rwops = SDL_AllocRW();
-  
+
+  rwops->size = physfs_size;
   rwops->seek = physfs_seek;
   rwops->read = physfs_read;
   rwops->write = physfs_write;
   rwops->close = physfs_close;
+  rwops->type = SDL_RWOPS_UNKNOWN;
   rwops->hidden.unknown.data1 = (void *) pfile;
-  
+
   return rwops;
 }
 
diff --git a/src/PSim/sim.cpp b/src/PSim/sim.cpp
index 7f0e252..df39bb7 100644
--- a/src/PSim/sim.cpp
+++ b/src/PSim/sim.cpp
@@ -45,53 +45,53 @@ PRigidBody *PSim::createRigidBody()
   return newbody;
 }
 
-PVehicle *PSim::createVehicle(TiXmlElement *element, const std::string &filepath, PSSModel &ssModel)
+PVehicle *PSim::createVehicle(XMLElement *element, const std::string &filepath, PSSModel &ssModel)
 {
   const char *val;
-  
+
   const char *type = element->Attribute("type");
   if (!type) {
     PUtil::outLog() << "Vehicle has no type\n";
     return nullptr;
   }
-  
+
   vec3f pos = vec3f::zero();
-  
+
   val = element->Attribute("pos");
   if (val) sscanf(val, "%f , %f , %f", &pos.x, &pos.y, &pos.z);
-  
+
   quatf ori = quatf::identity();
-  
+
   val = element->Attribute("ori");
   if (val) sscanf(val, "%f , %f , %f , %f", &ori.w, &ori.x, &ori.y, &ori.z);
-  
+
   return createVehicle(type, pos, ori, filepath, ssModel);
 }
 
 PVehicle *PSim::createVehicle(const std::string &type, const vec3f &pos, const quatf &ori, const std::string &filepath, PSSModel &ssModel)
 {
   PVehicleType *vtype = loadVehicleType(PUtil::assemblePath(type, filepath), ssModel);
-  
+
   return createVehicle(vtype, pos, ori, ssModel);
 }
 
 PVehicle *PSim::createVehicle(PVehicleType *type, const vec3f &pos, const quatf &ori, PSSModel &ssModel)
 {
   PSSModel *unused = &ssModel; unused = unused;
-  
+
   if (!type) return nullptr;
-  
+
   PVehicle *newvehicle = new PVehicle(*this, type);
-  
+
   vec3f vpos = pos;
   if (terrain) vpos.z += terrain->getHeight(vpos.x, vpos.y);
   newvehicle->getBody().setPosition(vpos);
-  
+
   newvehicle->getBody().setOrientation(ori);
   newvehicle->getBody().updateMatrices();
-  
+
   newvehicle->updateParts();
-  
+
   vehicle.push_back(newvehicle);
   return newvehicle;
 }
diff --git a/src/PSim/vehicle.cpp b/src/PSim/vehicle.cpp
index 28a79dc..f2908dc 100644
--- a/src/PSim/vehicle.cpp
+++ b/src/PSim/vehicle.cpp
@@ -151,8 +151,8 @@ bool PVehicleType::load(const std::string &filename, PSSModel &ssModel)
   
   //
   
-  TiXmlDocument xmlfile(filename.c_str());
-  TiXmlElement *rootelem = PUtil::loadRootElement(xmlfile, "vehicle");
+  XMLDocument xmlfile;
+  XMLElement *rootelem = PUtil::loadRootElement(xmlfile, filename, "vehicle");
   if (!rootelem) {
     PUtil::outLog() << "Load failed: TinyXML error\n";
     return false;
@@ -192,7 +192,7 @@ bool PVehicleType::load(const std::string &filename, PSSModel &ssModel)
     return false;
   }
 
-  for (TiXmlElement *walk = rootelem->FirstChildElement();
+  for (XMLElement *walk = rootelem->FirstChildElement();
     walk; walk = walk->NextSiblingElement()) {
 
     if (false) {
@@ -257,7 +257,7 @@ bool PVehicleType::load(const std::string &filename, PSSModel &ssModel)
 
     } else if (!strcmp(walk->Value(), "drivesystem")) {
       
-      for (TiXmlElement *walk2 = walk->FirstChildElement();
+      for (XMLElement *walk2 = walk->FirstChildElement();
         walk2; walk2 = walk2->NextSiblingElement()) {
         if (!strcmp(walk2->Value(), "engine")) {
           
@@ -266,7 +266,7 @@ bool PVehicleType::load(const std::string &filename, PSSModel &ssModel)
           val = walk2->Attribute("powerscale");
           if (val) powerscale = atof(val);
           
-          for (TiXmlElement *walk3 = walk2->FirstChildElement();
+          for (XMLElement *walk3 = walk2->FirstChildElement();
             walk3; walk3 = walk3->NextSiblingElement()) {
             
             if (!strcmp(walk3->Value(), "powerpoint")) {
@@ -293,7 +293,7 @@ bool PVehicleType::load(const std::string &filename, PSSModel &ssModel)
           
         } else if (!strcmp(walk2->Value(), "gearbox")) {
           
-          for (TiXmlElement *walk3 = walk2->FirstChildElement();
+          for (XMLElement *walk3 = walk2->FirstChildElement();
             walk3; walk3 = walk3->NextSiblingElement()) {
             
             if (!strcmp(walk3->Value(), "gear")) {
@@ -358,7 +358,7 @@ bool PVehicleType::load(const std::string &filename, PSSModel &ssModel)
       val = walk->Attribute("model");
       if (val) vtp->model = ssModel.loadModel(PUtil::assemblePath(val, filename));
 
-      for (TiXmlElement *walk2 = walk->FirstChildElement();
+      for (XMLElement *walk2 = walk->FirstChildElement();
         walk2; walk2 = walk2->NextSiblingElement()) {
         if (!strcmp(walk2->Value(), "clip")) {
           vehicle_clip_s vc;
diff --git a/src/Trigger/game.cpp b/src/Trigger/game.cpp
index 19b6d3e..e195a8e 100644
--- a/src/Trigger/game.cpp
+++ b/src/Trigger/game.cpp
@@ -109,8 +109,8 @@ bool TriggerGame::loadLevel(const std::string &filename)
   
   cdcheckpt_ordered = false;
   
-  TiXmlDocument xmlfile(filename.c_str());
-  TiXmlElement *rootelem = PUtil::loadRootElement(xmlfile, "level");
+  XMLDocument xmlfile;
+  XMLElement *rootelem = PUtil::loadRootElement(xmlfile, filename, "level");
   if (!rootelem) return false;
   
   const char *val;
@@ -118,7 +118,7 @@ bool TriggerGame::loadLevel(const std::string &filename)
   val = rootelem->Attribute("comment");
   if (val) comment = val;
   
-  for (TiXmlElement *walk = rootelem->FirstChildElement();
+  for (XMLElement *walk = rootelem->FirstChildElement();
     walk; walk = walk->NextSiblingElement()) {
     
     if (!strcmp(walk->Value(), "terrain")) {
@@ -193,7 +193,7 @@ bool TriggerGame::loadLevel(const std::string &filename)
                 number_of_laps = 1;
         }
       
-      for (TiXmlElement *walk2 = walk->FirstChildElement();
+      for (XMLElement *walk2 = walk->FirstChildElement();
         walk2; walk2 = walk2->NextSiblingElement()) {
         
         if (!strcmp(walk2->Value(), "checkpoint")) {
diff --git a/src/Trigger/main.cpp b/src/Trigger/main.cpp
index a7a1cc1..b495e07 100644
--- a/src/Trigger/main.cpp
+++ b/src/Trigger/main.cpp
@@ -8,37 +8,39 @@
 
 #include "main.h"
 
-#include <SDL/SDL_thread.h>
+#include <SDL2/SDL_main.h>
+#include <SDL2/SDL_thread.h>
 
 #include <cctype>
 #include <regex>
 
 GLfloat MainApp::cfg_anisotropy = 1.0f;
 bool MainApp::cfg_foliage = true;
+bool MainApp::cfg_roadsigns = true;
 bool MainApp::cfg_weather = true;
 
 void MainApp::config()
 {
-  PUtil::setDebugLevel(DEBUGLEVEL_DEVELOPER);
-  
-  loadConfig();
-  setScreenMode(cfg_video_cx, cfg_video_cy, cfg_video_fullscreen);
-  calcScreenRatios();
-  
-  if (cfg_datadirs.empty())
-    throw MakePException("Data directory paths are empty: check your trigger-rally.config file.");
+    PUtil::setDebugLevel(DEBUGLEVEL_DEVELOPER);
 
-  for (const std::string &datadir: cfg_datadirs)
-    if (PHYSFS_addToSearchPath(datadir.c_str(), 1) == 0)
-    {
-      PUtil::outLog() << "Failed to add PhysFS search directory \"" << datadir << "\"" << std::endl
-          << "PhysFS: " << PHYSFS_getLastError() << std::endl;
-    }
-    else
-    {
-        PUtil::outLog() << "Main game data directory datadir=\"" << datadir << "\"" << std::endl;
-        break;
-    }
+    loadConfig();
+    setScreenMode(cfg_video_cx, cfg_video_cy, cfg_video_fullscreen);
+    calcScreenRatios();
+
+    if (cfg_datadirs.empty())
+        throw MakePException("Data directory paths are empty: check your trigger-rally.config file.");
+
+    for (const std::string &datadir: cfg_datadirs)
+        if (PHYSFS_addToSearchPath(datadir.c_str(), 1) == 0)
+        {
+            PUtil::outLog() << "Failed to add PhysFS search directory \"" << datadir << "\"" << std::endl
+                << "PhysFS: " << PHYSFS_getLastError() << std::endl;
+        }
+        else
+        {
+            PUtil::outLog() << "Main game data directory datadir=\"" << datadir << "\"" << std::endl;
+            break;
+        }
 
     if (cfg_copydefplayers)
         copyDefaultPlayers();
@@ -89,7 +91,7 @@ void MainApp::load()
       break;
       
     case UserControl::TypeKey:
-      if (ctrl.map[i].key.sym <= 0 || ctrl.map[i].key.sym >= SDLK_LAST)
+      if (ctrl.map[i].key.sym <= 0 /* || ctrl.map[i].key.sym >= SDLK_LAST */) // `SDLK_LAST` unavailable in SDL2
         ctrl.map[i].type = UserControl::TypeUnassigned;
       break;
       
@@ -111,20 +113,20 @@ namespace
 
 ///
 /// @brief X-macro defining supported SDL keymaps.
-/// @see http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlkey.html
+/// @see http://wiki.libsdl.org/SDL_Keycode
 ///
 #define STRING_TO_SDL_KEYMAP    \
+    X(SDLK_UNKNOWN)             \
     X(SDLK_BACKSPACE)           \
     X(SDLK_TAB)                 \
-    X(SDLK_CLEAR)               \
     X(SDLK_RETURN)              \
-    X(SDLK_PAUSE)               \
     X(SDLK_ESCAPE)              \
     X(SDLK_SPACE)               \
     X(SDLK_EXCLAIM)             \
     X(SDLK_QUOTEDBL)            \
     X(SDLK_HASH)                \
     X(SDLK_DOLLAR)              \
+    X(SDLK_PERCENT)             \
     X(SDLK_AMPERSAND)           \
     X(SDLK_QUOTE)               \
     X(SDLK_LEFTPAREN)           \
@@ -185,32 +187,7 @@ namespace
     X(SDLK_y)                   \
     X(SDLK_z)                   \
     X(SDLK_DELETE)              \
-    X(SDLK_KP0)                 \
-    X(SDLK_KP1)                 \
-    X(SDLK_KP2)                 \
-    X(SDLK_KP3)                 \
-    X(SDLK_KP4)                 \
-    X(SDLK_KP5)                 \
-    X(SDLK_KP6)                 \
-    X(SDLK_KP7)                 \
-    X(SDLK_KP8)                 \
-    X(SDLK_KP9)                 \
-    X(SDLK_KP_PERIOD)           \
-    X(SDLK_KP_DIVIDE)           \
-    X(SDLK_KP_MULTIPLY)         \
-    X(SDLK_KP_MINUS)            \
-    X(SDLK_KP_PLUS)             \
-    X(SDLK_KP_ENTER)            \
-    X(SDLK_KP_EQUALS)           \
-    X(SDLK_UP)                  \
-    X(SDLK_DOWN)                \
-    X(SDLK_RIGHT)               \
-    X(SDLK_LEFT)                \
-    X(SDLK_INSERT)              \
-    X(SDLK_HOME)                \
-    X(SDLK_END)                 \
-    X(SDLK_PAGEUP)              \
-    X(SDLK_PAGEDOWN)            \
+    X(SDLK_CAPSLOCK)            \
     X(SDLK_F1)                  \
     X(SDLK_F2)                  \
     X(SDLK_F3)                  \
@@ -223,37 +200,165 @@ namespace
     X(SDLK_F10)                 \
     X(SDLK_F11)                 \
     X(SDLK_F12)                 \
+    X(SDLK_PRINTSCREEN)         \
+    X(SDLK_SCROLLLOCK)          \
+    X(SDLK_PAUSE)               \
+    X(SDLK_INSERT)              \
+    X(SDLK_HOME)                \
+    X(SDLK_PAGEUP)              \
+    X(SDLK_END)                 \
+    X(SDLK_PAGEDOWN)            \
+    X(SDLK_RIGHT)               \
+    X(SDLK_LEFT)                \
+    X(SDLK_DOWN)                \
+    X(SDLK_UP)                  \
+    X(SDLK_NUMLOCKCLEAR)        \
+    X(SDLK_KP_DIVIDE)           \
+    X(SDLK_KP_MULTIPLY)         \
+    X(SDLK_KP_MINUS)            \
+    X(SDLK_KP_PLUS)             \
+    X(SDLK_KP_ENTER)            \
+    X(SDLK_KP_1)                \
+    X(SDLK_KP_2)                \
+    X(SDLK_KP_3)                \
+    X(SDLK_KP_4)                \
+    X(SDLK_KP_5)                \
+    X(SDLK_KP_6)                \
+    X(SDLK_KP_7)                \
+    X(SDLK_KP_8)                \
+    X(SDLK_KP_9)                \
+    X(SDLK_KP_0)                \
+    X(SDLK_KP_PERIOD)           \
+    X(SDLK_APPLICATION)         \
+    X(SDLK_POWER)               \
+    X(SDLK_KP_EQUALS)           \
     X(SDLK_F13)                 \
     X(SDLK_F14)                 \
     X(SDLK_F15)                 \
-    X(SDLK_NUMLOCK)             \
-    X(SDLK_CAPSLOCK)            \
-    X(SDLK_SCROLLOCK)           \
-    X(SDLK_RSHIFT)              \
+    X(SDLK_F16)                 \
+    X(SDLK_F17)                 \
+    X(SDLK_F18)                 \
+    X(SDLK_F19)                 \
+    X(SDLK_F20)                 \
+    X(SDLK_F21)                 \
+    X(SDLK_F22)                 \
+    X(SDLK_F23)                 \
+    X(SDLK_F24)                 \
+    X(SDLK_EXECUTE)             \
+    X(SDLK_HELP)                \
+    X(SDLK_MENU)                \
+    X(SDLK_SELECT)              \
+    X(SDLK_STOP)                \
+    X(SDLK_AGAIN)               \
+    X(SDLK_UNDO)                \
+    X(SDLK_CUT)                 \
+    X(SDLK_COPY)                \
+    X(SDLK_PASTE)               \
+    X(SDLK_FIND)                \
+    X(SDLK_MUTE)                \
+    X(SDLK_VOLUMEUP)            \
+    X(SDLK_VOLUMEDOWN)          \
+    X(SDLK_KP_COMMA)            \
+    X(SDLK_KP_EQUALSAS400)      \
+    X(SDLK_ALTERASE)            \
+    X(SDLK_SYSREQ)              \
+    X(SDLK_CANCEL)              \
+    X(SDLK_CLEAR)               \
+    X(SDLK_PRIOR)               \
+    X(SDLK_RETURN2)             \
+    X(SDLK_SEPARATOR)           \
+    X(SDLK_OUT)                 \
+    X(SDLK_OPER)                \
+    X(SDLK_CLEARAGAIN)          \
+    X(SDLK_CRSEL)               \
+    X(SDLK_EXSEL)               \
+    X(SDLK_KP_00)               \
+    X(SDLK_KP_000)              \
+    X(SDLK_THOUSANDSSEPARATOR)  \
+    X(SDLK_DECIMALSEPARATOR)    \
+    X(SDLK_CURRENCYUNIT)        \
+    X(SDLK_CURRENCYSUBUNIT)     \
+    X(SDLK_KP_LEFTPAREN)        \
+    X(SDLK_KP_RIGHTPAREN)       \
+    X(SDLK_KP_LEFTBRACE)        \
+    X(SDLK_KP_RIGHTBRACE)       \
+    X(SDLK_KP_TAB)              \
+    X(SDLK_KP_BACKSPACE)        \
+    X(SDLK_KP_A)                \
+    X(SDLK_KP_B)                \
+    X(SDLK_KP_C)                \
+    X(SDLK_KP_D)                \
+    X(SDLK_KP_E)                \
+    X(SDLK_KP_F)                \
+    X(SDLK_KP_XOR)              \
+    X(SDLK_KP_POWER)            \
+    X(SDLK_KP_PERCENT)          \
+    X(SDLK_KP_LESS)             \
+    X(SDLK_KP_GREATER)          \
+    X(SDLK_KP_AMPERSAND)        \
+    X(SDLK_KP_DBLAMPERSAND)     \
+    X(SDLK_KP_VERTICALBAR)      \
+    X(SDLK_KP_DBLVERTICALBAR)   \
+    X(SDLK_KP_COLON)            \
+    X(SDLK_KP_HASH)             \
+    X(SDLK_KP_SPACE)            \
+    X(SDLK_KP_AT)               \
+    X(SDLK_KP_EXCLAM)           \
+    X(SDLK_KP_MEMSTORE)         \
+    X(SDLK_KP_MEMRECALL)        \
+    X(SDLK_KP_MEMCLEAR)         \
+    X(SDLK_KP_MEMADD)           \
+    X(SDLK_KP_MEMSUBTRACT)      \
+    X(SDLK_KP_MEMMULTIPLY)      \
+    X(SDLK_KP_MEMDIVIDE)        \
+    X(SDLK_KP_PLUSMINUS)        \
+    X(SDLK_KP_CLEAR)            \
+    X(SDLK_KP_CLEARENTRY)       \
+    X(SDLK_KP_BINARY)           \
+    X(SDLK_KP_OCTAL)            \
+    X(SDLK_KP_DECIMAL)          \
+    X(SDLK_KP_HEXADECIMAL)      \
+    X(SDLK_LCTRL)               \
     X(SDLK_LSHIFT)              \
+    X(SDLK_LALT)                \
+    X(SDLK_LGUI)                \
     X(SDLK_RCTRL)               \
-    X(SDLK_LCTRL)               \
+    X(SDLK_RSHIFT)              \
     X(SDLK_RALT)                \
-    X(SDLK_LALT)                \
-    X(SDLK_RMETA)               \
-    X(SDLK_LMETA)               \
-    X(SDLK_LSUPER)              \
-    X(SDLK_RSUPER)              \
+    X(SDLK_RGUI)                \
     X(SDLK_MODE)                \
-    X(SDLK_HELP)                \
-    X(SDLK_PRINT)               \
-    X(SDLK_SYSREQ)              \
-    X(SDLK_BREAK)               \
-    X(SDLK_MENU)                \
-    X(SDLK_POWER)               \
-    X(SDLK_EURO)
+    X(SDLK_AUDIONEXT)           \
+    X(SDLK_AUDIOPREV)           \
+    X(SDLK_AUDIOSTOP)           \
+    X(SDLK_AUDIOPLAY)           \
+    X(SDLK_AUDIOMUTE)           \
+    X(SDLK_MEDIASELECT)         \
+    X(SDLK_WWW)                 \
+    X(SDLK_MAIL)                \
+    X(SDLK_CALCULATOR)          \
+    X(SDLK_COMPUTER)            \
+    X(SDLK_AC_SEARCH)           \
+    X(SDLK_AC_HOME)             \
+    X(SDLK_AC_BACK)             \
+    X(SDLK_AC_FORWARD)          \
+    X(SDLK_AC_STOP)             \
+    X(SDLK_AC_REFRESH)          \
+    X(SDLK_AC_BOOKMARKS)        \
+    X(SDLK_BRIGHTNESSDOWN)      \
+    X(SDLK_BRIGHTNESSUP)        \
+    X(SDLK_DISPLAYSWITCH)       \
+    X(SDLK_KBDILLUMTOGGLE)      \
+    X(SDLK_KBDILLUMDOWN)        \
+    X(SDLK_KBDILLUMUP)          \
+    X(SDLK_EJECT)               \
+    X(SDLK_SLEEP)
 
 ///
 /// @brief Converts the string to a SDL keycode.
 /// @param [in] s   The string to be converted.
 /// @returns The keycode.
 ///
-SDLKey getSdlKeySym(const std::string &s)
+SDL_Keycode getSdlKeySym(const std::string &s)
 {
 #define X(SdlKey)   if (s == #SdlKey) return SdlKey;
     STRING_TO_SDL_KEYMAP
@@ -323,6 +428,7 @@ void MainApp::loadConfig()
   cfg_volume_codriver = 1.0f;
   cfg_anisotropy = 1.0f;
   cfg_foliage = true;
+  cfg_roadsigns = true;
   cfg_weather = true;
   cfg_speed_unit = mph;
   cfg_speed_style = analogue;
@@ -361,7 +467,18 @@ void MainApp::loadConfig()
   std::string cfgfilename = "trigger-rally-" PACKAGE_VERSION ".config";
   
   if (!PHYSFS_exists(cfgfilename.c_str())) {
-    
+#ifdef UNIX
+    const std::vector<std::string> cfghidingplaces {
+        "/usr/share/games/trigger-rally/"
+    };
+
+    for (const std::string &cfgpath: cfghidingplaces)
+        if (PHYSFS_addToSearchPath(cfgpath.c_str(), 1) == 0)
+        {
+            PUtil::outLog() << "Failed to add PhysFS search directory \"" <<
+                cfgpath << "\"\nPhysFS: " << PHYSFS_getLastError() << std::endl;
+        }
+#endif
     PUtil::outLog() << "No user config file, copying over defaults" << std::endl;
     
     std::string cfgdefaults = "trigger-rally.config.defs";
@@ -376,19 +493,19 @@ void MainApp::loadConfig()
   
   // Load actual settings from file
   
-  TiXmlDocument xmlfile(cfgfilename.c_str());
+  XMLDocument xmlfile;
   
-  TiXmlElement *rootelem = PUtil::loadRootElement(xmlfile, "config");
+  XMLElement *rootelem = PUtil::loadRootElement(xmlfile, cfgfilename, "config");
   if (!rootelem) {
     PUtil::outLog() << "Error: Couldn't load configuration file" << std::endl;
-    PUtil::outLog() << "TinyXML: " << xmlfile.ErrorDesc() << std::endl;
+    PUtil::outLog() << "TinyXML: " << xmlfile.GetErrorStr1() << ", " << xmlfile.GetErrorStr2() << std::endl;
     PUtil::outLog() << "Your data paths are probably not set up correctly" << std::endl;
     throw MakePException ("Boink");
   }
   
   const char *val;
   
-  for (TiXmlElement *walk = rootelem->FirstChildElement();
+  for (XMLElement *walk = rootelem->FirstChildElement();
     walk; walk = walk->NextSiblingElement()) {
 
     if (strcmp(walk->Value(), "player") == 0)
@@ -548,6 +665,16 @@ void MainApp::loadConfig()
                 cfg_foliage = true;
         }
         
+        val = walk->Attribute("roadsigns");
+        
+        if (val != nullptr)
+        {
+            if (strcmp(val, "no") == 0)
+                cfg_roadsigns = false;
+            else // yes
+                cfg_roadsigns = true;
+        }
+        
         val = walk->Attribute("weather");
         
         if (val)
@@ -584,7 +711,7 @@ void MainApp::loadConfig()
     else
     if (!strcmp(walk->Value(), "datadirectory"))
     {
-        for (TiXmlElement *walk2 = walk->FirstChildElement(); walk2; walk2 = walk2->NextSiblingElement())
+        for (XMLElement *walk2 = walk->FirstChildElement(); walk2; walk2 = walk2->NextSiblingElement())
             if (!strcmp(walk2->Value(), "data"))
                 cfg_datadirs.push_back(walk2->Attribute("path"));
     }
@@ -635,8 +762,8 @@ void MainApp::loadConfig()
 
       val = walk->Attribute("codriversigns");
 
-      if (val != nullptr)
-        cfg_codriversigns = val;
+        if (val != nullptr)
+            cfg_codriversigns = val;
 
         val = walk->Attribute("codriversignslife");
 
@@ -660,7 +787,7 @@ void MainApp::loadConfig()
 
     } else if (!strcmp(walk->Value(), "controls")) {
       
-      for (TiXmlElement *walk2 = walk->FirstChildElement();
+      for (XMLElement *walk2 = walk->FirstChildElement();
         walk2; walk2 = walk2->NextSiblingElement()) {
         
         if (!strcmp(walk2->Value(), "keyboard")) {
@@ -669,7 +796,7 @@ void MainApp::loadConfig()
           if (val && !strcmp(val, "no"))
             continue;
           
-          for (TiXmlElement *walk3 = walk2->FirstChildElement();
+          for (XMLElement *walk3 = walk2->FirstChildElement();
             walk3; walk3 = walk3->NextSiblingElement()) {
             
             if (!strcmp(walk3->Value(), "key")) {
@@ -713,7 +840,7 @@ void MainApp::loadConfig()
           if (val && !strcmp(val, "no"))
             continue;
           
-          for (TiXmlElement *walk3 = walk2->FirstChildElement();
+          for (XMLElement *walk3 = walk2->FirstChildElement();
             walk3; walk3 = walk3->NextSiblingElement()) {
             
             if (!strcmp(walk3->Value(), "button")) {
@@ -807,8 +934,8 @@ bool MainApp::loadLevel(TriggerLevel &tl)
   tl.tex_minimap = nullptr;
   tl.tex_screenshot = nullptr;
   
-  TiXmlDocument xmlfile(tl.filename.c_str());
-  TiXmlElement *rootelem = PUtil::loadRootElement(xmlfile, "level");
+  XMLDocument xmlfile;
+  XMLElement *rootelem = PUtil::loadRootElement(xmlfile, tl.filename, "level");
   if (!rootelem) {
     PUtil::outLog() << "Couldn't read level \"" << tl.filename << "\"" << std::endl;
     return false;
@@ -839,7 +966,7 @@ bool MainApp::loadLevel(TriggerLevel &tl)
   if (val != nullptr)
     tl.tex_minimap = getSSTexture().loadTexture(PUtil::assemblePath(val, tl.filename));
 
-  for (TiXmlElement *walk = rootelem->FirstChildElement();
+  for (XMLElement *walk = rootelem->FirstChildElement();
     walk; walk = walk->NextSiblingElement()) {
 
     if (!strcmp(walk->Value(), "race")) {
@@ -889,8 +1016,8 @@ bool MainApp::loadLevelsAndEvents()
     
     te.filename = *i;
     
-    TiXmlDocument xmlfile(i->c_str());
-    TiXmlElement *rootelem = PUtil::loadRootElement(xmlfile, "event");
+    XMLDocument xmlfile;
+    XMLElement *rootelem = PUtil::loadRootElement(xmlfile, *i, "event");
     if (!rootelem) {
       PUtil::outLog() << "Couldn't read event \"" << *i << "\"" << std::endl;
       continue;
@@ -914,7 +1041,7 @@ bool MainApp::loadLevelsAndEvents()
     
     float evtotaltime = 0.0f;
     
-    for (TiXmlElement *walk = rootelem->FirstChildElement();
+    for (XMLElement *walk = rootelem->FirstChildElement();
       walk; walk = walk->NextSiblingElement()) {
       
       if (strcmp(walk->Value(), "unlocks") == 0)
@@ -1423,7 +1550,7 @@ void MainApp::tickStateGame(float delta)
       break;
       
     case UserControl::TypeKey:
-      ctrl.map[a].value = keyDown(ctrl.map[a].key.sym) ? 1.0f : 0.0f;
+      ctrl.map[a].value = keyDown(SDL_GetScancodeFromKey(ctrl.map[a].key.sym)) ? 1.0f : 0.0f;
       break;
       
     case UserControl::TypeJoyButton:
@@ -2113,39 +2240,7 @@ void MainApp::joyButtonEvent(int which, int button, bool down)
   }
 }
 
-#ifndef WIN32
-
 int main(int argc, char *argv[])
 {
-  MainApp *game = new MainApp("Trigger Rally", ".trigger-rally");
-
-  int ret = game->run(argc, argv);
-
-  delete game;
-
-  return ret;
-}
-
-#else
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
-{
-    UNREFERENCED_PARAMETER(hInstance);
-    UNREFERENCED_PARAMETER(hPrevInstance);
-    UNREFERENCED_PARAMETER(lpCmdLine);
-    UNREFERENCED_PARAMETER(nCmdShow);
-
-  MainApp *game = new MainApp("Trigger Rally", ".trigger-rally");
-
-  int ret = game->run(0, nullptr);
-
-  delete game;
-
-  return ret;
+    return MainApp("Trigger Rally", ".trigger-rally").run(argc, argv);
 }
-
-#endif
-
diff --git a/src/Trigger/menu.cpp b/src/Trigger/menu.cpp
index d2007ca..723e706 100644
--- a/src/Trigger/menu.cpp
+++ b/src/Trigger/menu.cpp
@@ -1285,8 +1285,8 @@ bool Gui::loadColors(const std::string &filename)
     if (PUtil::isDebugLevel(DEBUGLEVEL_TEST))
         PUtil::outLog() << "Loading GUI colors from \"" << filename << "\"\n";
 
-    TiXmlDocument xmlfile(filename.c_str());
-    TiXmlElement *rootelem = PUtil::loadRootElement(xmlfile, "menucolors");
+    XMLDocument xmlfile;
+    XMLElement *rootelem = PUtil::loadRootElement(xmlfile, filename, "menucolors");
 
     if (rootelem == nullptr)
         return false;
diff --git a/src/Trigger/render.cpp b/src/Trigger/render.cpp
index 09ce7a3..8827a61 100644
--- a/src/Trigger/render.cpp
+++ b/src/Trigger/render.cpp
@@ -670,14 +670,14 @@ glMatrixMode(GL_PROJECTION);
     glTranslatef(100.0f, 230.0f, 0.0f);
     glScalef(30.0f, 30.0f, 1.0f);
     glColor4f(gwc.header.x, gwc.header.y, gwc.header.z, gwc.header.w);
-    getSSRender().drawText(vtype->proper_name, PTEXT_HZA_LEFT | PTEXT_VTA_CENTER);
+    getSSRender().drawText(vtype->proper_name.substr(0, 9), PTEXT_HZA_LEFT | PTEXT_VTA_CENTER);
     glPopMatrix(); // 1
 
     glPushMatrix(); // 1
     glTranslatef(100.0f, 200.0f, 0.0f);
     glScalef(20.0f, 20.0f, 1.0f);
     glColor4f(gwc.strong.x, gwc.strong.y, gwc.strong.z, gwc.strong.w);
-    getSSRender().drawText(vtype->proper_class, PTEXT_HZA_LEFT | PTEXT_VTA_CENTER);
+    getSSRender().drawText(vtype->proper_class.substr(0, 8), PTEXT_HZA_LEFT | PTEXT_VTA_CENTER);
     glPopMatrix(); // 1
 
     glPushMatrix(); // 1
diff --git a/src/TriggerRally.dev b/src/TriggerRally.dev
deleted file mode 100644
index f62d8d7..0000000
--- a/src/TriggerRally.dev
+++ /dev/null
@@ -1,442 +0,0 @@
-[Project]
-FileName=TriggerRally.dev
-Name=TriggerRally
-Type=1
-Ver=2
-ObjFiles=
-Includes=.\include;..\..\libraries-win32\include;"C:\Program Files\FMOD SoundSystem\FMOD Studio API Windows\api\lowlevel\inc"
-Libs=..\..\libraries-win32\lib;"C:\Program Files\FMOD SoundSystem\FMOD Studio API Windows\api\lowlevel\lib"
-PrivateResource=
-ResourceIncludes=
-MakeIncludes=TriggerRally.mak
-Compiler=
-CppCompiler=-DNDEBUG_@@_-DWIN32_@@_-DPACKAGE_VERSION=\"0.6.4\"_@@_
-Linker=-lopengl32_@@_-lglu32_@@_-lglew32_@@_-lSDL_@@_-lSDL_image_@@_-lphysfs_@@_-lfmod_@@_
-IsCpp=1
-Icon=
-ExeOutput=..\bin
-ObjectOutput=
-LogOutput=
-LogOutputEnabled=0
-OverrideOutput=1
-OverrideOutputName=trigger-rally.exe
-HostApplication=
-UseCustomMakefile=0
-CustomMakefile=
-CommandLine=
-Folders=Headers,PEngine,PSim,TinyXML,Trigger
-IncludeVersionInfo=0
-SupportXPThemes=0
-CompilerSet=3
-CompilerSettings=000dg0a1c0111000000001000
-UnitCount=39
-
-[VersionInfo]
-Major=1
-Minor=0
-Release=0
-Build=0
-LanguageID=1033
-CharsetID=1252
-CompanyName=
-FileVersion=1.0.0.0
-FileDescription=Developed using the Dev-C++ IDE
-InternalName=
-LegalCopyright=
-LegalTrademarks=
-OriginalFilename=
-ProductName=
-ProductVersion=1.0.0.0
-AutoIncBuildNr=0
-SyncProduct=1
-
-[Unit1]
-FileName=include\app.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit2]
-FileName=include\audio.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit3]
-FileName=include\exception.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit4]
-FileName=include\main.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit5]
-FileName=include\menu.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit6]
-FileName=include\pengine.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit7]
-FileName=include\psim.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit8]
-FileName=include\render.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit9]
-FileName=include\subsys.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit10]
-FileName=include\tinystr.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit11]
-FileName=include\tinyxml.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit12]
-FileName=include\vbuffer.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit13]
-FileName=include\vehicle.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit14]
-FileName=include\vmath.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit15]
-FileName=PEngine\app.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit16]
-FileName=PEngine\audio.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit17]
-FileName=PEngine\fxman.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit18]
-FileName=PEngine\model.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit19]
-FileName=PEngine\physfs_rw.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit20]
-FileName=PEngine\render.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit21]
-FileName=PEngine\terrain.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit22]
-FileName=PEngine\texture.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit23]
-FileName=PEngine\util.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit24]
-FileName=PEngine\vbuffer.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit25]
-FileName=PEngine\vmath.cpp
-CompileCpp=1
-Folder=PEngine
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit26]
-FileName=PSim\rigidbody.cpp
-CompileCpp=1
-Folder=PSim
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit27]
-FileName=PSim\sim.cpp
-CompileCpp=1
-Folder=PSim
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit28]
-FileName=PSim\vehicle.cpp
-CompileCpp=1
-Folder=PSim
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit29]
-FileName=TinyXML\tinystr.cpp
-CompileCpp=1
-Folder=TinyXML
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit30]
-FileName=TinyXML\tinyxml.cpp
-CompileCpp=1
-Folder=TinyXML
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit31]
-FileName=TinyXML\tinyxmlerror.cpp
-CompileCpp=1
-Folder=TinyXML
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit32]
-FileName=TinyXML\tinyxmlparser.cpp
-CompileCpp=1
-Folder=TinyXML
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit33]
-FileName=Trigger\game.cpp
-CompileCpp=1
-Folder=Trigger
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit34]
-FileName=Trigger\main.cpp
-CompileCpp=1
-Folder=Trigger
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit35]
-FileName=Trigger\menu.cpp
-CompileCpp=1
-Folder=Trigger
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit36]
-FileName=Trigger\render.cpp
-CompileCpp=1
-Folder=Trigger
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit37]
-FileName=include\terrainmap.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit38]
-FileName=include\codriver.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
-[Unit39]
-FileName=include\hiscore1.h
-CompileCpp=1
-Folder=Headers
-Compile=1
-Link=1
-Priority=1000
-OverrideBuildCmd=0
-BuildCmd=
-
diff --git a/src/TriggerRally.mak b/src/TriggerRally.mak
deleted file mode 100644
index f7c9758..0000000
--- a/src/TriggerRally.mak
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# Addon makefile for the "TriggerRally.dev" Dev-C++ project;
-# its purpose is to conveniently copy .DLL files to the binary folder.
-#
-# Written for Trigger Rally 0.6.3
-# Updated for Trigger Rally 0.6.4, 2016-04-22
-#
-
-LIBWINPATH      = ..\..\libraries-win32\bin
-LIBFMODPATH     = C:\Program Files\FMOD SoundSystem\FMOD Studio API Windows\api\lowlevel\lib
-TR_BINDIR       = ..\bin
-TR_DLLFILES     =                   \
-    $(TR_BINDIR)\fmod.dll           \
-    $(TR_BINDIR)\glew32.dll         \
-    $(TR_BINDIR)\libjpeg-9.dll      \
-    $(TR_BINDIR)\libphysfs.dll      \
-    $(TR_BINDIR)\libpng16-16.dll    \
-    $(TR_BINDIR)\SDL.dll            \
-    $(TR_BINDIR)\SDL_image.dll      \
-    $(TR_BINDIR)\zlib1.dll
-
-all-after: $(TR_DLLFILES)
-
-$(TR_BINDIR)\fmod.dll:
-	@xcopy "$(LIBFMODPATH)\fmod.dll" "$(TR_BINDIR)"
-
-$(TR_BINDIR)\glew32.dll:
-	@xcopy "$(LIBWINPATH)\glew32.dll" "$(TR_BINDIR)"
-
-$(TR_BINDIR)\libjpeg-9.dll:
-	@xcopy "$(LIBWINPATH)\libjpeg-9.dll" "$(TR_BINDIR)"
-
-$(TR_BINDIR)\libphysfs.dll:
-	@xcopy "$(LIBWINPATH)\libphysfs.dll" "$(TR_BINDIR)"
-
-$(TR_BINDIR)\libpng16-16.dll:
-	@xcopy "$(LIBWINPATH)\libpng16-16.dll" "$(TR_BINDIR)"
-
-$(TR_BINDIR)\SDL.dll:
-	@xcopy "$(LIBWINPATH)\SDL.dll" "$(TR_BINDIR)"
-
-$(TR_BINDIR)\SDL_image.dll:
-	@xcopy "$(LIBWINPATH)\SDL_image.dll" "$(TR_BINDIR)"
-
-$(TR_BINDIR)\zlib1.dll:
-	@xcopy "$(LIBWINPATH)\zlib1.dll" "$(TR_BINDIR)"
diff --git a/src/_clean.cmd b/src/_clean.cmd
deleted file mode 100644
index 3273391..0000000
--- a/src/_clean.cmd
+++ /dev/null
@@ -1,4 +0,0 @@
- at echo off
-del Makefile.win
-del TriggerRally.layout
-for /R %%f in (*.o) do del %%f
diff --git a/src/include/app.h b/src/include/app.h
index ba2a32a..bc46d55 100644
--- a/src/include/app.h
+++ b/src/include/app.h
@@ -43,7 +43,8 @@ class PApp
 
         std::string appname, apptitle;
 
-        SDL_Surface *screen;
+        SDL_Window *screen; // TODO: rename this to "window" maybe
+        SDL_GLContext context;
         
         /// Attempts to set fullscreen at native resolution.
         bool autoVideo = false;
@@ -62,7 +63,7 @@ class PApp
         StereoMode stereo;
         float stereoEyeTranslation;
 
-        uint8* sdl_keymap;
+        const uint8* sdl_keymap;
         int sdl_numkeys;
         uint8 sdl_mousemap;
         std::vector<joystick_s> sdl_joy;
@@ -90,7 +91,12 @@ class PApp
             best_times("/players")
         {
             //PUtil::outLog() << "Initialising SDL" << std::endl;
-            SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE);
+            const int si = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_JOYSTICK);
+
+            if (si < 0)
+            {
+                PUtil::outLog() << "Failed to initialize SDL: " << SDL_GetError() << std::endl;
+            }
 
             cx = cy = 0;
             bpp = 0;
@@ -218,15 +224,26 @@ class PApp
             // use automatic video mode
             if (autoVideo)
             {
-                const SDL_VideoInfo *info = SDL_GetVideoInfo();
-
-                cx      = info->current_w;
-                cy      = info->current_h;
-                bpp     = info->vfmt->BitsPerPixel;
-                fullscr = true;
-                noframe = hideFrame;
+                SDL_DisplayMode dm;
+
+                if (SDL_GetCurrentDisplayMode(0, &dm) == 0)
+                {
+                    cx = dm.w;
+                    cy = dm.h;
+                    fullscr = fullScreen;
+                    noframe = hideFrame;
+                    PUtil::outLog() << "Automatic video mode resolution: " << cx << 'x' << cy << std::endl;
+                }
+                else
+                {
+                    PUtil::outLog() << "SDL error, SDL_GetCurrentDisplayMode(): " << SDL_GetError() << std::endl;
+                    autoVideo = false;
+                }
             }
-            else
+
+            // not written as an `else` branch because `autoVideo` may have
+            // been updated in the case that automatic video mode failed
+            if (!autoVideo)
             {
                 cx = w;
                 cy = h;
diff --git a/src/include/codriver.h b/src/include/codriver.h
index 66205f8..a7a0965 100644
--- a/src/include/codriver.h
+++ b/src/include/codriver.h
@@ -166,6 +166,8 @@ private:
 
 ///
 /// @brief Gives a voice to the codriver.
+/// @todo Re-code without making use of C++11 threads to be in line with
+///  the rest of the game logic, which is implemented with `tick()` functions.
 ///
 class PCodriverVoice
 {
@@ -231,7 +233,8 @@ private:
             voice.setGain(volume);
             voice.play();
 
-            while (voice.isPlaying());
+            while (voice.isPlaying())
+                std::this_thread::sleep_for(std::chrono::milliseconds(50));
         }
     }
 
diff --git a/src/include/hiscore1.h b/src/include/hiscore1.h
index 242c39f..29ec025 100644
--- a/src/include/hiscore1.h
+++ b/src/include/hiscore1.h
@@ -41,6 +41,17 @@
         return InputStream;                                         \
 } else (void)0
 
+
+// TODO: remove duplicate code
+#define GETLINE_SKIP_EMPTY_LINES_B(InputStream, String) if (true) { \
+    while (std::getline(InputStream, String)) {                     \
+        if (!String.empty())                                        \
+            break;                                                  \
+    }                                                               \
+    if (String.empty())                                             \
+        return static_cast<bool> (InputStream);                     \
+} else (void)0
+
 ///
 /// @brief Basic structure to load and save race results.
 ///
@@ -724,19 +735,19 @@ private:
         std::istringstream sspdata(decrypt(pdata));
 #undef decrypt
 
-        GETLINE_SKIP_EMPTY_LINES(sspdata, ts);
+        GETLINE_SKIP_EMPTY_LINES_B(sspdata, ts);
         nu = std::stoul(ts);
 
         while (nu-- != 0)
         {
-            GETLINE_SKIP_EMPTY_LINES(sspdata, ts);
+            GETLINE_SKIP_EMPTY_LINES_B(sspdata, ts);
             allunlocks[pname].insert(ts);
         }
 
         while (sspdata >> rd)
             alltimes.insert({rd.mapname, rd});
 
-        return sspdata;
+        return static_cast<bool> (sspdata);
     }
 
     ///
@@ -803,4 +814,4 @@ private:
 };
 
 #undef GETLINE_SKIP_EMPTY_LINES
-
+#undef GETLINE_SKIP_EMPTY_LINES_B
diff --git a/src/include/main.h b/src/include/main.h
index 9e7ab94..7e51528 100644
--- a/src/include/main.h
+++ b/src/include/main.h
@@ -21,7 +21,7 @@ class MainApp;
 
 struct CheckPoint {
   vec3f pt;
-  
+
   CheckPoint(const vec3f &_pt) : pt(_pt) { }
 };
 
@@ -43,7 +43,7 @@ struct CodriverCP
 /*
 struct AIDriver {
   int vehic;
-  
+
   AIDriver(int v) : vehic(v) { }
 };
 */
@@ -68,17 +68,17 @@ public:
 
 private:
   MainApp *app;
-  
+
   PSim *sim;
-  
+
   int randomseed;
-  
+
   std::vector<PVehicle *> vehicle;
-  
+
   //std::vector<AIDriver> aid;
-  
+
   PTerrain *terrain;
-  
+
   std::vector<CheckPoint> checkpt;
   std::vector<CodriverCP> codrivercheckpt;
   int number_of_laps = 1;
@@ -105,14 +105,14 @@ public:
 private:
 
   int gamestate;
-  
+
   float coursetime;
   float othertime; // used pre and post race
   float cptime; // checkpoint time
   float targettime; // the time needed to win
-  
+
   std::string comment; // level comment string
-  
+
   vec3f start_pos;
   quatf start_ori;
 
@@ -152,36 +152,36 @@ private:
 
 public:
   std::vector<PVehicleType *> vehiclechoices;
-  
+
 public:
   TriggerGame(MainApp *parent);
   ~TriggerGame();
-  
+
   void resetAtCheckpoint(PVehicle *veh)
   {
       veh->doReset2(lastCkptPos, lastCkptOri);
   }
-  
+
   void renderCodriverSigns()
   {
       cdsigns.render(coursetime);
   }
-  
+
   bool loadVehicles();
-  
+
   bool loadLevel(const std::string &filename);
-  
+
   void chooseVehicle(PVehicleType *type);
-  
+
   void tick(float delta);
-  
+
   bool isFinished() { return (gamestate == GS_FINISHED) && (othertime <= 0.0f); }
-  
+
   bool isRacing() const
   {
       return gamestate == GS_RACING;
   }
-  
+
   int getFinishState() {
     if (gamestate != GS_FINISHED) return GF_NOT_FINISHED;
     if (coursetime + offroadtime_total * offroadtime_penalty_multiplier <= targettime) return GF_PASS;
@@ -205,9 +205,9 @@ public:
 
 struct TriggerLevel {
   std::string filename, name, description, comment, author, targettime, targettimeshort;
-  
+
   float targettimefloat;
-  
+
   PTexture *tex_minimap = nullptr;
   PTexture *tex_screenshot = nullptr;
 };
@@ -217,11 +217,11 @@ struct TriggerEvent {
 
   bool locked = false;
   UnlockData unlocks; ///< @see `HiScore1`
-  
+
   // Note that levels are not linked to... they are
   // stored in the event because an event may have
   // "hidden" levels not otherwise available
-  
+
   std::vector<TriggerLevel> levels;
 };
 
@@ -229,9 +229,9 @@ struct TriggerEvent {
 class DirtParticleSystem : public PParticleSystem {
 public:
   void tick(float delta) {
-    
+
     PParticleSystem::tick(delta);
-    
+
     for (unsigned int i=0; i<part.size(); i++) {
       PULLTOWARD(part[i].linvel, vec3f::zero(), delta * 25.0f);
     }
@@ -261,7 +261,7 @@ struct UserControl {
   } type;
   union {
     struct {
-      SDLKey sym;
+      SDL_Keycode sym;
     } key;
     struct {
       int button;
@@ -273,7 +273,7 @@ struct UserControl {
       float maxrange;
     } joyaxis; // more like half-axis, really
   };
-  
+
   float value; // from 0.0 to 1.0 depending on activation level
 };
 
@@ -299,17 +299,18 @@ public:
   // TODO: these shouldn't be static+public, but the simplicity is worth it for now
   static GLfloat    cfg_anisotropy;     ///< Anisotropic filter quality.
   static bool       cfg_foliage;        ///< Foliage on/off flag.
+  static bool       cfg_roadsigns;      ///< Road signs on/off flag.
   static bool       cfg_weather;        ///< Weather on/off flag.
 
 private:
-  
+
   int appstate;
-  
+
   // TODO: use `aspect` instead of these?
   // TODO: type should be GLdouble instead of double
   double hratio; ///< Horizontal ratio.
   double vratio; ///< Vertical ratio.
-  
+
   UnlockData player_unlocks; ///< Unlocks for current player, see `HiScore1`.
 
 public:
@@ -333,8 +334,8 @@ public:
     ///
     bool isVehicleLocked(const std::string &vefi) const
     {
-        TiXmlDocument xmlfile(vefi.c_str());
-        TiXmlElement *rootelem = PUtil::loadRootElement(xmlfile, "vehicle");
+        XMLDocument xmlfile;
+        XMLElement *rootelem = PUtil::loadRootElement(xmlfile, vefi, "vehicle");
 
         if (rootelem == nullptr)
         {
@@ -353,27 +354,27 @@ public:
 private:
 
   // Config settings
-  
+
   std::string cfg_playername;
   bool cfg_copydefplayers;
-  
+
   int cfg_video_cx, cfg_video_cy;
   bool cfg_video_fullscreen;
-  
+
   float cfg_drivingassist;
   bool cfg_enable_sound;
   bool cfg_enable_codriversigns;
-  
+
   long int cfg_skip_saves;
-  
+
   /// Basic volume control.
   float cfg_volume_engine       = 0.33f;    ///< Engine.
   float cfg_volume_sfx          = 1.00f;    ///< Sound effects (wind, gear change, crash, skid, etc.)
   float cfg_volume_codriver     = 1.00f;    ///< Codriver voice.
-  
+
   /// Search paths for the data directory, as read from the configuration.
   std::list<std::string> cfg_datadirs;
-  
+
   /// Name of the codriver whose words to load.
   /// Must be a valid directory in /data/sounds/codriver/.
   std::string cfg_codrivername;
@@ -392,7 +393,7 @@ private:
   float hud_speedo_mps_speed_mult;
 
     SnowFlakeType cfg_snowflaketype = SnowFlakeType::point;
-    
+
     bool cfg_dirteffect = true;
 
   enum Action {
@@ -417,35 +418,35 @@ private:
     std::string action_name[ActionCount];
     UserControl map[ActionCount];
   } ctrl;
-  
+
   //
-  
+
   float splashtimeout;
-  
+
   //
-  
+
   std::vector<TriggerLevel> levels;
   std::vector<TriggerEvent> events;
-  
+
   // for level screen
   Gui gui;
 public:
   LevelState lss;
 private:
   //
-  
+
   HISCORE1_SORT hs_sort_method = HISCORE1_SORT::BY_TOTALTIME_ASC;
   RaceData race_data;
   std::vector<TimeEntry> current_times;
 
   TriggerGame *game;
-  
+
   PVehicleType *vt_tank;
-  
+
   PTexture *tex_fontDsmNormal,
            *tex_fontDsmOutlined,
            *tex_fontDsmShadowed;
-  
+
   PTexture *tex_detail,
            *tex_sky[1],
            *tex_water,
@@ -464,107 +465,107 @@ private:
            *tex_race_no_minimap,
            *tex_button_next,
            *tex_button_prev;
-  
+
   std::unordered_map<std::string, PTexture *> tex_codriversigns;
   std::unordered_map<std::string, PAudioSample *> aud_codriverwords;
 
   DirtParticleSystem *psys_dirt;
-  
+
   PAudioSample *aud_engine,
                *aud_wind,
                *aud_gearchange,
                *aud_gravel,
                *aud_crash1;
-  
+
   PAudioInstance *audinst_engine, *audinst_wind, *audinst_gravel;
   std::vector<PAudioInstance *> audinst;
-  
+
   float cloudscroll;
-  
+
   vec3f campos, campos_prev;
   quatf camori;
-  
+
   vec3f camvel;
-  
+
   float nextcpangle;
-  
+
   float cprotate;
-  
+
   int cameraview;
   float camera_angle;
   float camera_user_angle;
-  
+
   bool renderowncar; // this is determined from cameraview
-  
+
   bool showmap;
 
     bool pauserace;
 
   bool showui;
-  
+
   bool showcheckpoint;
 
   float crashnoise_timeout;
-  
+
   std::vector<RainDrop> rain;
   std::vector<SnowFlake> snowfall;
 
   //
-  
+
   int loadscreencount;
-  
+
   float choose_spin;
-  
+
   int choose_type;
-  
+
 protected:
   void renderWater();
   void renderSky(const mat44f &cammat);
-  
+
   bool startGame(const std::string &filename);
   void toggleSounds(bool to);
   void startGame2();
   void endGame(int gamestate);
-  
+
   void quitGame() {
     endGame(GF_NOT_FINISHED);
     splashtimeout = 0.0f;
     appstate = AS_END_SCREEN;
   }
-  
+
   void levelScreenAction(int action, int index);
   void handleLevelScreenKey(const SDL_KeyboardEvent &ke);
   void finishRace(int gamestate, float coursetime);
-  
+
 public:
   MainApp(const std::string &title, const std::string &name):
     PApp(title, name)
   {
   }
   //MainApp::~MainApp(); // should not have destructor, use unload
-  
+
   float getCodriverVolume() const
   {
       return cfg_volume_codriver;
   }
-  
+
   void config();
   void load();
   void unload();
-  
+
   void copyDefaultPlayers() const;
   void loadConfig();
   bool loadAll();
   bool loadLevelsAndEvents();
   bool loadLevel(TriggerLevel &tl);
-  
+
   void calcScreenRatios();
-  
+
   void tick(float delta);
-  
+
   void resize();
   void render(float eyetranslation);
-  
+
   void renderStateLoading(float eyetranslation);
   void renderStateEnd(float eyetranslation);
   void tickStateLevel(float delta);
@@ -573,7 +574,7 @@ public:
   void renderStateChoose(float eyetranslation);
   void tickStateGame(float delta);
   void renderStateGame(float eyetranslation);
-  
+
   void keyEvent(const SDL_KeyboardEvent &ke);
   void mouseMoveEvent(int dx, int dy);
   void cursorMoveEvent(int posx, int posy);
@@ -584,12 +585,12 @@ public:
     {
         return aud_codriverwords;
     }
-    
+
     std::unordered_map<std::string, PTexture *> getCodriverSigns() const
     {
         return tex_codriversigns;
     }
-    
+
     PCodriverUserConfig getCodriverUserConfig() const
     {
         return cfg_codriveruserconfig;
diff --git a/src/include/pengine.h b/src/include/pengine.h
index b360e59..d96812a 100644
--- a/src/include/pengine.h
+++ b/src/include/pengine.h
@@ -21,7 +21,7 @@
 // "SDL.h" is supposed to be the portable way, but it
 // doesn't seem to work in some circumstances...
 //#include "SDL.h"
-#include <SDL/SDL.h>
+#include <SDL2/SDL.h>
 
 
 // The PhysicsFS game file system
@@ -31,8 +31,8 @@
 #include <GL/glext.h>
 
 // TinyXML is built into the pengine library
-#include "tinyxml.h"
-
+#include "tinyxml2.h"
+using namespace tinyxml2;
 
 // Some maths utils and macros
 #include "vmath.h"
@@ -90,9 +90,9 @@ struct dirtinfo
     float startsize = 0.1f;
     float endsize   = 0.5f;
     float decay     = 6.0f;
-    
+
     dirtinfo() = default;
-    
+
     dirtinfo(float startsize, float endsize, float decay):
         startsize(startsize),
         endsize(endsize),
@@ -109,9 +109,9 @@ struct rgbcolor
     uint8_t r=0;    ///< Red.
     uint8_t g=0;    ///< Green.
     uint8_t b=0;    ///< Blue.
-    
+
     rgbcolor() = default;
-    
+
     rgbcolor(uint8_t r, uint8_t g, uint8_t b):
         r(r),
         g(g),
@@ -128,7 +128,7 @@ struct rgbcolor
 
         return false;
     }
-    
+
     bool operator != (const rgbcolor &rhs) const
     {
         if (r != rhs.r ||
@@ -145,17 +145,17 @@ struct rgbcolor
 class PUtil {
 private:
   PUtil() { } // cannot be constructed
-  
+
   static int deblev;
-  
+
 public:
   // Output streams
   static std::ostream &outLog() { return std::cout; }
-  
+
   // Debug level
   static bool isDebugLevel(int debugLevel) { return deblev >= debugLevel; }
   static void setDebugLevel(int debugLevel) { deblev = debugLevel; }
- 
+
   // TODO: these two functions are probably misplaced here
   static TerrainType decideRoadSurface(const rgbcolor &c);
   static float decideFrictionCoef(TerrainType tt);
@@ -163,43 +163,43 @@ public:
   static const char * getTerrainInfo(TerrainType tt);
   static dirtinfo getDirtInfo(TerrainType tt);
   static rgbcolor getTerrainColor(TerrainType tt);
- 
+
   /*! Get token and value from a string line. The token is the string
    * before first space. The value, is the remaining string
    * \return true if could extract token and value */
   static bool getToken(std::string line, std::string& tok, std::string& value);
 
   static char* fgets2(char *s, int size, PHYSFS_file *pfile);
-  
+
   // Given "data/blah/pic.jpg" will return "data/blah/"
   static std::string extractPathFromFilename(const std::string &filename);
-  
+
   static std::string assemblePath(const std::string &relativefile, const std::string &parentfile);
-  
+
   // Load XML file and return the root element of given name (failure: null)
-  static TiXmlElement *loadRootElement(TiXmlDocument &doc, const char *rootName);
-  
+  static XMLElement *loadRootElement(XMLDocument &doc, const std::string &filename, const char *rootName);
+
   static bool copyFile(const std::string &fileFrom, const std::string &fileTo);
   static std::list<std::string> findFiles(const std::string &basedir, const std::string &extension);
-  
+
   static std::string formatInt(int value, int width);
   static std::string formatInt(int value);
-  
+
   static std::string formatTime(float seconds);
   static std::string formatTimeShort(float seconds);
-  
+
   // RWops created must be freed by using SDL freesrc on load
   static SDL_RWops *allocPhysFSops(PHYSFS_file *pfile);
 };
 
 
 
-#include <hiscore1.h>
+#include "hiscore1.h"
 #include "app.h"
 #include "subsys.h"
 #include "audio.h"
 #include "render.h"
-#include <codriver.h>
+#include "codriver.h"
 
 
 #endif // PENGINE_H_INCLUDED
diff --git a/src/include/psim.h b/src/include/psim.h
index a3d1c19..c626a1e 100644
--- a/src/include/psim.h
+++ b/src/include/psim.h
@@ -131,45 +131,45 @@ public:
 
 class PSim {
 private:
-  
+
   PTerrain *terrain;
-  
+
   PResourceList<PVehicleType> vtypelist;
-  
+
   std::vector<PRigidBody *> body;
-  
+
   std::vector<PVehicle *> vehicle;
-  
+
   vec3f gravity;
-  
+
 public:
   PSim();
   ~PSim();
-  
+
 public:
   void setTerrain(PTerrain *_terrain) { terrain = _terrain; }
-  
+
   void setGravity(const vec3f &_gravity) { gravity = _gravity; }
-  
+
   PVehicleType *loadVehicleType(const std::string &filename, PSSModel &ssModel);
-  
+
   PRigidBody *createRigidBody();
-  
-  PVehicle *createVehicle(TiXmlElement *element, const std::string &filepath, PSSModel &ssModel);
+
+  PVehicle *createVehicle(XMLElement *element, const std::string &filepath, PSSModel &ssModel);
   PVehicle *createVehicle(const std::string &type, const vec3f &pos, const quatf &ori, const std::string &filepath, PSSModel &ssModel);
   PVehicle *createVehicle(PVehicleType *type, const vec3f &pos, const quatf &ori, PSSModel &ssModel);
-  
+
   // Remove all bodies and vehicles
   void clear();
-  
+
   // Step the simulation delta seconds
   void tick(float delta);
-  
-  
+
+
   PTerrain *getTerrain() { return terrain; }
-  
+
 public:
-  
+
   friend class PRigidBody;
   friend class PVehicle;
 };
diff --git a/src/include/render.h b/src/include/render.h
index aa956c6..6f10c29 100644
--- a/src/include/render.h
+++ b/src/include/render.h
@@ -92,13 +92,13 @@ private:
 public:
   PSSRender(PApp &parentApp);
   ~PSSRender();
-  
+
   void tick(float delta, const vec3f &eyepos, const mat44f &eyeori, const vec3f &eyevel);
-  
+
   void render(PParticleSystem *psys);
-  
+
   void drawModel(PModel &model, PSSEffect &ssEffect, PSSTexture &ssTexture);
-  
+
   void drawText(const std::string &text, uint32 flags);
   vec2f getTextDims(const std::string &text);
 };
@@ -127,11 +127,11 @@ public:
   PImage (const std::string &filename) : data (nullptr) { load (filename); }
   PImage (int _cx, int _cy, int _cc) : data (nullptr) { load (_cx, _cy, _cc); }
   ~PImage ();
-  
+
   void load (const std::string &filename);
   void load (int _cx, int _cy, int _cc);
   void unload ();
-  
+
   void expandChannels();
 
   int getcx() const { return cx; }
@@ -148,12 +148,12 @@ public:
   {
       return data[i];
   }
-  
+
   uint8 getByte(int i) const
   {
       return data[i];
   }
-  
+
   void swap (PImage &other) throw ()
   {
     { uint8 *tmp = data; data = other.data; other.data = tmp; }
@@ -168,13 +168,13 @@ class PTexture : public PResource {
 private:
   GLuint texid;
   GLenum textarget;
-  
+
 public:
   PTexture () : texid (0) { }
   PTexture (const std::string &filename, bool genMipmaps, bool clamp) : texid (0) { load (filename, genMipmaps, clamp); }
   PTexture (PImage &img, bool genMipmaps, bool clamp) : texid (0) { load (img, genMipmaps, clamp); }
   ~PTexture() { unload (); }
-  
+
   void load (const std::string &filename, bool genMipmaps, bool clamp);
   void load(PImage &img, bool genMipmaps = true, bool clamp = false);
   void loadPiece(PImage &img, int offx, int offy, int sizex, int sizey, bool genMipmaps = true, bool clamp = false);
@@ -354,7 +354,7 @@ public:
 
 public:
   PModel (const std::string &filename, float globalScale = 1.0);
-  
+
 private:
   void loadASE (const std::string &filename, float globalScale);
   void loadOBJ (const std::string &filename, float globalScale);
@@ -370,7 +370,7 @@ struct PTerrainFoliageBand {
   float scalemin;
   float scalemax;
   */
-  
+
   PTexture *sprite_tex;
   int sprite_count;
 };
@@ -383,25 +383,33 @@ struct PTerrainFoliage {
 
 struct PTerrainFoliageSet {
   std::vector<PTerrainFoliage> inst;
-  
+
   PVBuffer buff[2];
   int numvert, numelem;
 };
 
+struct PRoadSignSet {
+    std::vector<PTerrainFoliage> inst;
+    PVBuffer buff[2];
+    int numvert;
+    int numelem;
+};
+
 struct PTerrainTile {
   int posx, posy;
   int lru_counter;
-  
+
   PVBuffer vert;
   int numverts;
-  
+
   PTexture tex;
-  
+
   vec3f mins,maxs; // AABB
-  
+
   //
-  
+
   std::vector<PTerrainFoliageSet> foliage;
+  std::vector<PRoadSignSet> roadsignset;
 };
 
 ///
@@ -504,54 +512,90 @@ private:
     int by;
 };
 
+struct road_sign
+{
+public:
+
+//
+// being smart here makes the rest of the code more complicated;
+// so let's be stupid for now...
+//
+#if 0
+    struct road_sign_location
+    {
+    public:
+
+        float x;
+        float y;
+        float deg;
+
+        road_sign_location(float x=0, float y=0, float deg=0):
+            x(x), y(y), deg(deg)
+        {
+        }
+    };
+
+    std::vector<road_sign_location> location;
+#endif
+
+    PTexture   *sprite  = nullptr;
+//  PTexture   *front   = nullptr;
+//  PTexture   *back    = nullptr;
+    float       scale   = 1.0f;
+    float       x       = 0.0f;
+    float       y       = 0.0f;
+    float       deg     = 0.0f;
+};
+
 class PTerrain // TODO: make this RAII conformant
 {
 protected:
   bool loaded;
-  
+
   int tilesize, tilecount, totsize, totmask, totsizesq;
-  
+
   float scale_hz, scale_vt, scale_hz_inv, scale_vt_inv, scale_tile_inv;
-  
+
   int cmaptotsize, cmaptilesize, cmaptotmask;
-  
+
   //std::vector<uint8> hmap;
   std::vector<float> hmap;
-  
+
   PImage cmap;
   PImage tmap; ///< Terrain map.
   RoadMap rmap; ///< Road map.
-  
+
   std::vector<float> fmap;
   std::vector<PTerrainFoliageBand> foliageband;
-  
+  std::vector<road_sign> roadsigns;
+
   std::list<PTerrainTile> tile;
-  
+
   // tiles share index buffers
   PVBuffer ind;
   int numinds;
-  
+
   PTexture *tex_hud_map;
-  
+
 protected:
-  
+
   PTerrainTile *getTile(int x, int y);
-  
+
   float getInterp(float x, float y, float *data) {
     x *= scale_hz_inv;
     int xi = (int)x;
     if (x < 0.0) xi--;
     x -= (float)xi;
     int xiw = xi & totmask, xiw2 = (xiw+1) & totmask;
-    
+
     y *= scale_hz_inv;
     int yi = (int)y;
     if (y < 0.0) yi--;
     y -= (float)yi;
     int yiw = yi & totmask, yiw2 = (yiw+1) & totmask;
-    
+
     const int cx = totsize;
-    
+
     float xv1,xv2;
     if (y > 0.0) {
       if (y < 1.0) {
@@ -572,21 +616,21 @@ protected:
   }
 
 public:
-  PTerrain(TiXmlElement *element, const std::string &filepath, PSSTexture &ssTexture);
+  PTerrain(XMLElement *element, const std::string &filepath, PSSTexture &ssTexture);
   ~PTerrain();
-  
+
   void unload();
-  
+
   void render(const vec3f &campos, const mat44f &camorim);
-  
+
   void drawSplat(float x, float y, float scale, float angle);
-  
-  
+
+
   struct ContactInfo {
     vec3f pos;
     vec3f normal;
   };
-  
+
     ///
     /// @brief Returns whether or not the given position is on road.
     /// @param [in] pos         Position to be checked.
@@ -598,7 +642,7 @@ public:
     {
         return rmap.isOnRoad(pos.x, pos.y, getMapSize());
     }
-  
+
     ///
     /// @brief Returns the color of the pixel in the colormap that corresponds
     ///  to the given position in the terrain.
@@ -660,7 +704,7 @@ public:
         r.z = cmap.getByte((y * cmap.getcx() + x) * cmap.getcc() + 2) / 255.0f;
         return r;
     }
-  
+
     ///
     /// @brief Returns the road surface type corresponding to the given position
     ///  in the terrain.
@@ -729,16 +773,16 @@ public:
     if (x < 0.0) xi--;
     x -= (float)xi;
     int xiw = xi & totmask, xiw2 = (xi+1) & totmask;
-    
+
     float y = tci.pos.y * scale_hz_inv;
     int yi = (int)y;
     if (y < 0.0) yi--;
     y -= (float)yi;
     int yiw = yi & totmask, yiw2 = (yi+1) & totmask;
-    
+
     float *data = &hmap[0];
     const int cx = totsize;
-    
+
     float xv1,xv2;
     if (y > 0.0) {
       if (y < 1.0) {
@@ -767,17 +811,17 @@ public:
     tci.normal.z = scale_hz;
     tci.normal.normalize();
   }
-  
+
   float getHeight(float x, float y) {
     return getInterp(x, y, &hmap[0]);
   }
-  
+
   float getFoliageLevel(float x, float y) {
     return getInterp(x, y, &fmap[0]);
   }
-  
+
   PTexture *getHUDMapTexture() { return tex_hud_map; }
-  
+
   float getMapSize() const { return totsize * scale_hz; }
 };
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/trigger-rally.git



More information about the Pkg-games-commits mailing list