]> git.xonotic.org Git - xonotic/netradiant.git/commitdiff
Merge commit '839c9693774fdb0e420391f65b8066e8bd04c591' into master-merge
authorThomas Debesse <dev@illwieckz.net>
Tue, 21 Jun 2022 02:25:25 +0000 (04:25 +0200)
committerThomas Debesse <dev@illwieckz.net>
Tue, 21 Jun 2022 02:25:25 +0000 (04:25 +0200)
14 files changed:
1  2 
Makefile
libs/string/string.h
radiant/build.cpp
radiant/csg.cpp
radiant/environment.cpp
radiant/environment.h
radiant/main.cpp
radiant/mainframe.cpp
radiant/map.cpp
radiant/server.cpp
tools/quake3/q3map2/brush.c
tools/quake3/q3map2/bspfile_abstract.c
tools/quake3/q3map2/main.c
tools/quake3/q3map2/q3map2.h

diff --combined Makefile
index e66cd7f2f37985519d84678d0c6b31c80dd3e882,8e63b813c38f4833bd6890e3a71a4a6bb2d39632..2a63b9cc6ae13b24d3445fff5381b75291ddf4b8
+++ b/Makefile
@@@ -1,13 -1,3 +1,13 @@@
 +# TODO: when deleting this Makefile, don't forget to also delete conftest.cpp
 +# which is only required by this Makefile
 +
 +ifeq ($(I_KNOW_MAKEFILE_IS_DEPRECATED),)
 +    $(shell printf 'ERROR: Makefile build is deprecated, use CMake instead, see README.md\n\n' >&2)
 +    $(error I_KNOW_MAKEFILE_IS_DEPRECATED is not set)
 +else
 +    $(shell printf 'WARNING: deprecated Makefile build enforced\n\n' >&2)
 +endif
 +
  MAKEFILE_CONF      ?= Makefile.conf
  -include $(MAKEFILE_CONF)
  
@@@ -24,8 -14,6 +24,8 @@@ CXXFLAGS           ?
  CPPFLAGS           ?=
  LIBS               ?=
  RADIANT_ABOUTMSG   ?= Custom build
 +RADIANT_NAME       ?= NetRadiant
 +RADIANT_BASENAME   ?= netradiant
  
  # warning: this directory may NOT contain any files other than the ones written by this Makefile!
  # NEVER SET THIS TO A SYSTEM WIDE "bin" DIRECTORY!
@@@ -210,7 -198,7 +210,7 @@@ ifeq ($(OS),Linux
  else
  
  ifeq ($(OS),Win32)
 -      CPPFLAGS_COMMON += -DWIN32 -D_WIN32 -D_inline=inline
 +      CPPFLAGS_COMMON += -DWIN32 -D_WIN32 -D_inline=inline -DWORKAROUND_WINDOWS_FLOATING_WINDOW=1 -DWORKAROUND_WINDOWS_GTK2_GLWIDGET=1
        CFLAGS_COMMON += -mms-bitfields
        LDFLAGS_DLL = -Wl,--add-stdcall-alias
        LIBS_COMMON = -lws2_32 -luser32 -lgdi32 -lole32
@@@ -264,13 -252,16 +264,13 @@@ RADIANT_VERSION_NUMBER = $(RADIANT_MAJO
  RADIANT_VERSION = $(RADIANT_VERSION_NUMBER)n
  Q3MAP_VERSION = 2.5.17n
  
 -# Executable extension
 -RADIANT_EXECUTABLE := $(EXE)
 -
  GIT_VERSION := $(shell $(GIT) rev-parse --short HEAD $(STDERR_TO_DEVNULL))
  ifneq ($(GIT_VERSION),)
        RADIANT_VERSION := $(RADIANT_VERSION)-git-$(GIT_VERSION)
        Q3MAP_VERSION := $(Q3MAP_VERSION)-git-$(GIT_VERSION)
  endif
  
 -CPPFLAGS += -DRADIANT_VERSION="\"$(RADIANT_VERSION)\"" -DRADIANT_MAJOR_VERSION="\"$(RADIANT_MAJOR_VERSION)\"" -DRADIANT_MINOR_VERSION="\"$(RADIANT_MINOR_VERSION)\"" -DRADIANT_PATCH_VERSION="\"$(RADIANT_PATCH_VERSION)\"" -DRADIANT_ABOUTMSG="\"$(RADIANT_ABOUTMSG)\"" -DQ3MAP_VERSION="\"$(Q3MAP_VERSION)\"" -DRADIANT_EXECUTABLE="\"$(RADIANT_EXECUTABLE)\""
 +CPPFLAGS += -DRADIANT_VERSION="\"$(RADIANT_VERSION)\"" -DRADIANT_VERSION_STRING="\"$(RADIANT_VERSION_STRING)\"" -DRADIANT_ABOUTMSG="\"$(RADIANT_ABOUTMSG)\"" -DRADIANT_NAME="\"${RADIANT_NAME}\"" -DRADIANT_BASENAME="\"${RADIANT_BASENAME}\"" -DQ3MAP_VERSION="\"$(Q3MAP_VERSION)\""
  CPPFLAGS += -DGTK_TARGET=2
  
  .PHONY: all
@@@ -441,8 -432,8 +441,8 @@@ binaries-qdata3: 
  
  .PHONY: binaries-h2data
  binaries-h2data: \
 -      $(INSTALLDIR)/heretic2/h2data.$(EXE) \
 -      $(INSTALLDIR)/heretic2/h2data \
 +      $(INSTALLDIR)/h2data.$(EXE) \
 +      $(INSTALLDIR)/h2data \
  
  .PHONY: binaries-tools-quake3
  binaries-tools-quake3: \
@@@ -498,7 -489,9 +498,9 @@@ endi
  %.o: %.c $(if $(findstring $(DEPEND_ON_MAKEFILE),yes),$(wildcard Makefile*),) | dependencies-check
        $(CC) $< $(CFLAGS) $(CFLAGS_COMMON) $(CPPFLAGS_EXTRA) $(CPPFLAGS_COMMON) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@
  
- $(INSTALLDIR)/q3map2.$(EXE): LDFLAGS_EXTRA := -Wl,--large-address-aware
+ ifeq ($(OS),Win32)
+ $(INSTALLDIR)/q3map2.$(EXE): LDFLAGS_EXTRA := -Wl,--large-address-aware,--stack,4194304
+ endif
  $(INSTALLDIR)/q3map2.$(EXE): LIBS_EXTRA := $(LIBS_XML) $(LIBS_GLIB) $(LIBS_PNG) $(LIBS_JPEG) $(LIBS_WEBP) $(LIBS_ZLIB)
  $(INSTALLDIR)/q3map2.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) $(CPPFLAGS_GLIB) $(CPPFLAGS_PNG) $(CPPFLAGS_JPEG) $(CPPFLAGS_WEBP) -Itools/quake3/common -Ilibs -Iinclude
  $(INSTALLDIR)/q3map2.$(EXE): \
@@@ -1029,45 -1022,46 +1031,45 @@@ $(INSTALLDIR)/plugins/bkgrnd2d.$(DLL): 
        contrib/bkgrnd2d/dialog.o \
        contrib/bkgrnd2d/plugin.o \
  
 -$(INSTALLDIR)/heretic2/h2data.$(EXE): LIBS_EXTRA := $(LIBS_XML)
 -$(INSTALLDIR)/heretic2/h2data.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) -Itools/quake2/qdata_heretic2/common -Itools/quake2/qdata_heretic2/qcommon -Itools/quake2/qdata_heretic2 -Itools/quake2/common -Ilibs -Iinclude
 -$(INSTALLDIR)/heretic2/h2data.$(EXE): \
 -      tools/quake2/qdata_heretic2/common/bspfile.o \
 -      tools/quake2/qdata_heretic2/common/cmdlib.o \
 -      tools/quake2/qdata_heretic2/common/inout.o \
 -      tools/quake2/qdata_heretic2/common/l3dslib.o \
 -      tools/quake2/qdata_heretic2/common/lbmlib.o \
 -      tools/quake2/qdata_heretic2/common/mathlib.o \
 -      tools/quake2/qdata_heretic2/common/md4.o \
 -      tools/quake2/qdata_heretic2/common/path_init.o \
 -      tools/quake2/qdata_heretic2/common/qfiles.o \
 -      tools/quake2/qdata_heretic2/common/scriplib.o \
 -      tools/quake2/qdata_heretic2/common/threads.o \
 -      tools/quake2/qdata_heretic2/common/token.o \
 -      tools/quake2/qdata_heretic2/common/trilib.o \
 -      tools/quake2/qdata_heretic2/qcommon/reference.o \
 -      tools/quake2/qdata_heretic2/qcommon/resourcemanager.o \
 -      tools/quake2/qdata_heretic2/qcommon/skeletons.o \
 -      tools/quake2/qdata_heretic2/animcomp.o \
 -      tools/quake2/qdata_heretic2/book.o \
 -      tools/quake2/qdata_heretic2/fmodels.o \
 -      tools/quake2/qdata_heretic2/images.o \
 -      tools/quake2/qdata_heretic2/jointed.o \
 -      tools/quake2/qdata_heretic2/models.o \
 -      tools/quake2/qdata_heretic2/pics.o \
 -      tools/quake2/qdata_heretic2/qdata.o \
 -      tools/quake2/qdata_heretic2/qd_skeletons.o \
 -      tools/quake2/qdata_heretic2/sprites.o \
 -      tools/quake2/qdata_heretic2/svdcmp.o \
 -      tools/quake2/qdata_heretic2/tables.o \
 -      tools/quake2/qdata_heretic2/tmix.o \
 -      tools/quake2/qdata_heretic2/video.o \
 +$(INSTALLDIR)/h2data.$(EXE): LIBS_EXTRA := $(LIBS_XML)
 +$(INSTALLDIR)/h2data.$(EXE): CPPFLAGS_EXTRA := $(CPPFLAGS_XML) -Itools/heretic2/common -Itools/heretic2/qcommon -Itools/heretic2/h2data -Itools/quake2/common -Ilibs -Iinclude
 +$(INSTALLDIR)/h2data.$(EXE): \
 +      tools/heretic2/common/bspfile.o \
 +      tools/heretic2/common/cmdlib.o \
 +      tools/heretic2/common/inout.o \
 +      tools/heretic2/common/l3dslib.o \
 +      tools/heretic2/common/lbmlib.o \
 +      tools/heretic2/common/mathlib.o \
 +      tools/heretic2/common/md4.o \
 +      tools/heretic2/common/path_init.o \
 +      tools/heretic2/common/qfiles.o \
 +      tools/heretic2/common/scriplib.o \
 +      tools/heretic2/common/threads.o \
 +      tools/heretic2/common/token.o \
 +      tools/heretic2/common/trilib.o \
 +      tools/heretic2/qcommon/reference.o \
 +      tools/heretic2/qcommon/resourcemanager.o \
 +      tools/heretic2/qcommon/skeletons.o \
 +      tools/heretic2/h2data/animcomp.o \
 +      tools/heretic2/h2data/book.o \
 +      tools/heretic2/h2data/fmodels.o \
 +      tools/heretic2/h2data/images.o \
 +      tools/heretic2/h2data/jointed.o \
 +      tools/heretic2/h2data/models.o \
 +      tools/heretic2/h2data/pics.o \
 +      tools/heretic2/h2data/qdata.o \
 +      tools/heretic2/h2data/qd_skeletons.o \
 +      tools/heretic2/h2data/sprites.o \
 +      tools/heretic2/h2data/svdcmp.o \
 +      tools/heretic2/h2data/tables.o \
 +      tools/heretic2/h2data/tmix.o \
 +      tools/heretic2/h2data/video.o \
        libl_net.$(A) \
        $(if $(findstring $(OS),Win32),icons/h2data.o,) \
  
  .PHONY: install-data
  install-data: binaries
 -      $(MKDIR) $(INSTALLDIR)/games
 -      DOWNLOAD_GAMEPACKS="$(DOWNLOAD_GAMEPACKS)" DOWNLOADDIR="$(DOWNLOADDIR)" INSTALLDIR="$(INSTALLDIR)" GIT="$(GIT)" SVN="$(SVN)" WGET="$(WGET)" RM_R="$(RM_R)" MV="$(MV)" UNZIPPER="$(UNZIPPER)" ECHO="$(ECHO)" SH="$(SH)" CP="$(CP)" CP_R="$(CP_R)" $(SH) gamepack-manager
 +      DOWNLOAD_GAMEPACKS="$(DOWNLOAD_GAMEPACKS)" DOWNLOADDIR="$(DOWNLOADDIR)" INSTALLDIR="$(INSTALLDIR)/gamepacks" GIT="$(GIT)" SVN="$(SVN)" WGET="$(WGET)" RM_R="$(RM_R)" MV="$(MV)" UNZIPPER="$(UNZIPPER)" ECHO="$(ECHO)" CP="$(CP)" CP_R="$(CP_R)" ./gamepack-manager
        $(ECHO) $(RADIANT_MAJOR_VERSION) > $(INSTALLDIR)/RADIANT_MAJOR
        $(ECHO) $(RADIANT_MINOR_VERSION) > $(INSTALLDIR)/RADIANT_MINOR
        $(ECHO) $(RADIANT_PATCH_VERSION) > $(INSTALLDIR)/RADIANT_PATCH
diff --combined libs/string/string.h
index 5d63925d2c6ce14764bfa33ccfeb06eb5025665b,e780303b595f81c9ee8270857a1c119234825a49..e41779cd72d52e19153b551caafe76110348b6d4
@@@ -150,6 -150,11 +150,11 @@@ inline bool string_equal_suffix( const 
        return string_equal_n( s , suffix, string_length( suffix ) );
  }
  
+ inline bool string_equal_suffix_nocase( const char* string, const char* suffix){
+       const char *s = string + string_length( string ) - string_length( suffix );
+       return string_equal_nocase_n( s , suffix, string_length( suffix ) );
+ }
  /// \brief Copies \p other into \p string and returns \p string.
  /// Assumes that the space allocated for \p string is at least string_length(other) + 1.
  /// O(n)
@@@ -259,52 -264,50 +264,52 @@@ inline char* string_to_uppercase( char
  /// \brief A re-entrant string tokeniser similar to strchr.
  class StringTokeniser
  {
 -bool istoken( char c ) const {
 -      if ( strchr( m_delimiters, c ) != 0 ) {
 -              return false;
 -      }
 -      return true;
 -}
 -const char* advance(){
 -      const char* token = m_pos;
 -      bool intoken = true;
 -      while ( !string_empty( m_pos ) )
 -      {
 -              if ( !istoken( *m_pos ) ) {
 -                      *m_pos = '\0';
 -                      intoken = false;
 +private:
 +      bool istoken( char c ) const {
 +              if ( strchr( m_delimiters, c ) != 0 ) {
 +                      return false;
                }
 -              else if ( !intoken ) {
 -                      return token;
 +              return true;
 +      }
 +      const char* advance(){
 +              const char* token = m_pos;
 +              bool intoken = true;
 +              while ( !string_empty( m_pos ) )
 +              {
 +                      if ( !istoken( *m_pos ) ) {
 +                              *m_pos = '\0';
 +                              intoken = false;
 +                      }
 +                      else if ( !intoken ) {
 +                              return token;
 +                      }
 +                      ++m_pos;
                }
 -              ++m_pos;
 +              return token;
        }
 -      return token;
 -}
 -std::size_t m_length;
 -char* m_string;
 -char* m_pos;
 -const char* m_delimiters;
 +      std::size_t m_length;
 +      char* m_string;
 +      char* m_pos;
 +      const char* m_delimiters;
 +
  public:
 -StringTokeniser( const char* string, const char* delimiters = " \n\r\t\v" ) :
 -      m_length( string_length( string ) ),
 -      m_string( string_copy( string_new( m_length ), string ) ),
 -      m_pos( m_string ),
 -      m_delimiters( delimiters ){
 -      while ( !string_empty( m_pos ) && !istoken( *m_pos ) )
 -      {
 -              ++m_pos;
 +      StringTokeniser( const char* string, const char* delimiters = " \n\r\t\v" ) :
 +              m_length( string_length( string ) ),
 +              m_string( string_copy( string_new( m_length ), string ) ),
 +              m_pos( m_string ),
 +              m_delimiters( delimiters ){
 +              while ( !string_empty( m_pos ) && !istoken( *m_pos ) )
 +              {
 +                      ++m_pos;
 +              }
 +      }
 +      ~StringTokeniser(){
 +              string_release( m_string, m_length );
 +      }
 +      /// \brief Returns the next token or "" if there are no more tokens available.
 +      const char* getToken(){
 +              return advance();
        }
 -}
 -~StringTokeniser(){
 -      string_release( m_string, m_length );
 -}
 -/// \brief Returns the next token or "" if there are no more tokens available.
 -const char* getToken(){
 -      return advance();
 -}
  };
  
  /// \brief A non-mutable c-style string.
@@@ -318,41 -321,40 +323,41 @@@ template<typename Buffer
  class String : public Buffer
  {
  public:
 +      String()
 +              : Buffer(){
 +      }
 +      String( const char* string )
 +              : Buffer( string ){
 +      }
 +      String( StringRange range )
 +              : Buffer( range ){
 +      }
  
 -String()
 -      : Buffer(){
 -}
 -String( const char* string )
 -      : Buffer( string ){
 -}
 -String( StringRange range )
 -      : Buffer( range ){
 -}
 +      String(const String&) = default;
  
 -String& operator=( const String& other ){
 -      String temp( other );
 -      temp.swap( *this );
 -      return *this;
 -}
 -String& operator=( const char* string ){
 -      String temp( string );
 -      temp.swap( *this );
 -      return *this;
 -}
 -String& operator=( StringRange range ){
 -      String temp( range );
 -      temp.swap( *this );
 -      return *this;
 -}
 +      String& operator=( const String& other ){
 +              String temp( other );
 +              temp.swap( *this );
 +              return *this;
 +      }
 +      String& operator=( const char* string ){
 +              String temp( string );
 +              temp.swap( *this );
 +              return *this;
 +      }
 +      String& operator=( StringRange range ){
 +              String temp( range );
 +              temp.swap( *this );
 +              return *this;
 +      }
  
 -void swap( String& other ){
 -      Buffer::swap( other );
 -}
 +      void swap( String& other ){
 +              Buffer::swap( other );
 +      }
  
 -bool empty() const {
 -      return string_empty( Buffer::c_str() );
 -}
 +      bool empty() const {
 +              return string_empty( Buffer::c_str() );
 +      }
  };
  
  template<typename Buffer>
@@@ -400,45 -402,44 +405,45 @@@ inline void swap( String<Buffer>& self
  template<typename Allocator>
  class CopiedBuffer : private Allocator
  {
 -char* m_string;
 +      char* m_string;
  
 -char* copy_range( StringRange range ){
 -      return string_clone_range( range, static_cast<Allocator&>( *this ) );
 -}
 -char* copy( const char* other ){
 -      return string_clone( other, static_cast<Allocator&>( *this ) );
 -}
 -void destroy( char* string ){
 -      string_release( string, string_length( string ), static_cast<Allocator&>( *this ) );
 -}
 +      char* copy_range( StringRange range ){
 +              return string_clone_range( range, static_cast<Allocator&>( *this ) );
 +      }
 +      char* copy( const char* other ){
 +              return string_clone( other, static_cast<Allocator&>( *this ) );
 +      }
 +      void destroy( char* string ){
 +              string_release( string, string_length( string ), static_cast<Allocator&>( *this ) );
 +      }
  
  protected:
 -~CopiedBuffer(){
 -      destroy( m_string );
 -}
 +      ~CopiedBuffer(){
 +              destroy( m_string );
 +      }
 +
  public:
 -CopiedBuffer()
 -      : m_string( copy( "" ) ){
 -}
 -explicit CopiedBuffer( const Allocator& allocator )
 -      : Allocator( allocator ), m_string( copy( "" ) ){
 -}
 -CopiedBuffer( const CopiedBuffer& other )
 -      : Allocator( other ), m_string( copy( other.m_string ) ){
 -}
 -CopiedBuffer( const char* string, const Allocator& allocator = Allocator() )
 -      : Allocator( allocator ), m_string( copy( string ) ){
 -}
 -CopiedBuffer( StringRange range, const Allocator& allocator = Allocator() )
 -      : Allocator( allocator ), m_string( copy_range( range ) ){
 -}
 -const char* c_str() const {
 -      return m_string;
 -}
 -void swap( CopiedBuffer& other ){
 -      string_swap( m_string, other.m_string );
 -}
 +      CopiedBuffer()
 +              : m_string( copy( "" ) ){
 +      }
 +      explicit CopiedBuffer( const Allocator& allocator )
 +              : Allocator( allocator ), m_string( copy( "" ) ){
 +      }
 +      CopiedBuffer( const CopiedBuffer& other )
 +              : Allocator( other ), m_string( copy( other.m_string ) ){
 +      }
 +      CopiedBuffer( const char* string, const Allocator& allocator = Allocator() )
 +              : Allocator( allocator ), m_string( copy( string ) ){
 +      }
 +      CopiedBuffer( StringRange range, const Allocator& allocator = Allocator() )
 +              : Allocator( allocator ), m_string( copy_range( range ) ){
 +      }
 +      const char* c_str() const {
 +              return m_string;
 +      }
 +      void swap( CopiedBuffer& other ){
 +              string_swap( m_string, other.m_string );
 +      }
  };
  
  /// \brief A non-mutable string which uses copy-by-value for assignment.
@@@ -449,67 -450,65 +454,67 @@@ typedef String< CopiedBuffer< DefaultAl
  template<typename Allocator>
  class SmartBuffer : private Allocator
  {
 -char* m_buffer;
 -
 -char* copy_range( StringRange range ){
 -      char* buffer = Allocator::allocate( sizeof( std::size_t ) + ( range.last - range.first ) + 1 );
 -      strncpy( buffer + sizeof( std::size_t ), range.first, range.last - range.first );
 -      buffer[sizeof( std::size_t ) + ( range.last - range.first )] = '\0';
 -      *reinterpret_cast<std::size_t*>( buffer ) = 0;
 -      return buffer;
 -}
 -char* copy( const char* string ){
 -      char* buffer = Allocator::allocate( sizeof( std::size_t ) + string_length( string ) + 1 );
 -      strcpy( buffer + sizeof( std::size_t ), string );
 -      *reinterpret_cast<std::size_t*>( buffer ) = 0;
 -      return buffer;
 -}
 -void destroy( char* buffer ){
 -      Allocator::deallocate( buffer, sizeof( std::size_t ) + string_length( c_str() ) + 1 );
 -}
 +private:
 +      char* m_buffer;
 +
 +      char* copy_range( StringRange range ){
 +              char* buffer = Allocator::allocate( sizeof( std::size_t ) + ( range.last - range.first ) + 1 );
 +              strncpy( buffer + sizeof( std::size_t ), range.first, range.last - range.first );
 +              buffer[sizeof( std::size_t ) + ( range.last - range.first )] = '\0';
 +              *reinterpret_cast<std::size_t*>( buffer ) = 0;
 +              return buffer;
 +      }
 +      char* copy( const char* string ){
 +              char* buffer = Allocator::allocate( sizeof( std::size_t ) + string_length( string ) + 1 );
 +              strcpy( buffer + sizeof( std::size_t ), string );
 +              *reinterpret_cast<std::size_t*>( buffer ) = 0;
 +              return buffer;
 +      }
 +      void destroy( char* buffer ){
 +              Allocator::deallocate( buffer, sizeof( std::size_t ) + string_length( c_str() ) + 1 );
 +      }
  
 -void incref( char* buffer ){
 -      ++( *reinterpret_cast<std::size_t*>( buffer ) );
 -}
 -void decref( char* buffer ){
 -      if ( --( *reinterpret_cast<std::size_t*>( buffer ) ) == 0 ) {
 -              destroy( buffer );
 +      void incref( char* buffer ){
 +              ++( *reinterpret_cast<std::size_t*>( buffer ) );
 +      }
 +      void decref( char* buffer ){
 +              if ( --( *reinterpret_cast<std::size_t*>( buffer ) ) == 0 ) {
 +                      destroy( buffer );
 +              }
        }
 -}
  
  protected:
 -~SmartBuffer(){
 -      decref( m_buffer );
 -}
 +      ~SmartBuffer(){
 +              decref( m_buffer );
 +      }
 +
  public:
 -SmartBuffer()
 -      : m_buffer( copy( "" ) ){
 -      incref( m_buffer );
 -}
 -explicit SmartBuffer( const Allocator& allocator )
 -      : Allocator( allocator ), m_buffer( copy( "" ) ){
 -      incref( m_buffer );
 -}
 -SmartBuffer( const SmartBuffer& other )
 -      : Allocator( other ), m_buffer( other.m_buffer ){
 -      incref( m_buffer );
 -}
 -SmartBuffer( const char* string, const Allocator& allocator = Allocator() )
 -      : Allocator( allocator ), m_buffer( copy( string ) ){
 -      incref( m_buffer );
 -}
 -SmartBuffer( StringRange range, const Allocator& allocator = Allocator() )
 -      : Allocator( allocator ), m_buffer( copy_range( range ) ){
 -      incref( m_buffer );
 -}
 -const char* c_str() const {
 -      return m_buffer + sizeof( std::size_t );
 -}
 -void swap( SmartBuffer& other ){
 -      string_swap( m_buffer, other.m_buffer );
 -}
 +      SmartBuffer()
 +              : m_buffer( copy( "" ) ){
 +              incref( m_buffer );
 +      }
 +      explicit SmartBuffer( const Allocator& allocator )
 +              : Allocator( allocator ), m_buffer( copy( "" ) ){
 +              incref( m_buffer );
 +      }
 +      SmartBuffer( const SmartBuffer& other )
 +              : Allocator( other ), m_buffer( other.m_buffer ){
 +              incref( m_buffer );
 +      }
 +      SmartBuffer( const char* string, const Allocator& allocator = Allocator() )
 +              : Allocator( allocator ), m_buffer( copy( string ) ){
 +              incref( m_buffer );
 +      }
 +      SmartBuffer( StringRange range, const Allocator& allocator = Allocator() )
 +              : Allocator( allocator ), m_buffer( copy_range( range ) ){
 +              incref( m_buffer );
 +      }
 +      const char* c_str() const {
 +              return m_buffer + sizeof( std::size_t );
 +      }
 +      void swap( SmartBuffer& other ){
 +              string_swap( m_buffer, other.m_buffer );
 +      }
  };
  
  /// \brief A non-mutable string which uses copy-by-reference for assignment of SmartString.
@@@ -518,9 -517,9 +523,9 @@@ typedef String< SmartBuffer< DefaultAll
  class StringEqualNoCase
  {
  public:
 -bool operator()( const CopiedString& key, const CopiedString& other ) const {
 -      return string_equal_nocase( key.c_str(), other.c_str() );
 -}
 +      bool operator()( const CopiedString& key, const CopiedString& other ) const {
 +              return string_equal_nocase( key.c_str(), other.c_str() );
 +      }
  };
  
  struct StringLessNoCase
diff --combined radiant/build.cpp
index 179ae632342194b3148c7c78b90930845afb7e67,3a524a76465f7fa727d966f19eab7f445b7a820f..234367516271a9bdade050808122309d3bd5ef6b
@@@ -41,12 -41,12 +41,12 @@@ void build_set_variable( const char* na
        g_build_variables[name] = value;
  }
  
 -const char* build_get_variable( const char* name ){
 -      Variables::iterator i = g_build_variables.find( name );
 +const char* build_get_variable( const std::string& name ){
 +      Variables::iterator i = g_build_variables.find( name.c_str() );
        if ( i != g_build_variables.end() ) {
                return ( *i ).second.c_str();
        }
 -      globalErrorStream() << "undefined build variable: " << makeQuoted( name ) << "\n";
 +      globalErrorStream() << "undefined build variable: " << makeQuoted( name.c_str() ) << "\n";
        return "";
  }
  
@@@ -57,80 -57,55 +57,80 @@@ class Evaluatabl
  {
  public:
  virtual ~Evaluatable() = default;
 -virtual void evaluate( StringBuffer& output ) = 0;
 +virtual std::string evaluate() = 0;
  virtual void exportXML( XMLImporter& importer ) = 0;
  };
  
  class VariableString : public Evaluatable
  {
 -CopiedString m_string;
 +std::string m_string;
  public:
  VariableString() : m_string(){
  }
 -VariableString( const char* string ) : m_string( string ){
 +VariableString( std::string string ) : m_string( std::move(string) ){
  }
  const char* c_str() const {
        return m_string.c_str();
  }
 -void setString( const char* string ){
 +void setString( const std::string& string ){
        m_string = string;
  }
 -void evaluate( StringBuffer& output ){
 -      StringBuffer variable;
 +std::string evaluate(){
 +      // replace ".[ExecutableType]" with "[ExecutableExt]"
 +      {
 +              size_t pos;
 +              const std::string pattern = ".[ExecutableType]";
 +              while ( ( pos = m_string.find(pattern) ) != std::string::npos ) {
 +                      m_string.replace(pos, pattern.length(), "[ExecutableExt]");
 +              }
 +      }
 +
 +      // add missing [ExtraQ3map2Args] if "[RadiantPath]q3map2[ExecutableExt]"
 +      {
 +              size_t pos;
 +              const std::string pattern = "\"[RadiantPath]q3map2[ExecutableExt]\"";
 +              const std::string extra = "[ExtraQ3map2Args]";
 +              if ( ( pos = m_string.find(pattern) ) != std::string::npos
 +                              && m_string.find(extra) == std::string::npos )
 +              {
 +                      m_string.insert(pos + pattern.size(), " ");
 +                      m_string.insert(pos + pattern.size() + 1, extra);
 +              }
 +      }
 +
 +      std::string output;
 +      std::string variable_name;
        bool in_variable = false;
 -      for ( const char* i = m_string.c_str(); *i != '\0'; ++i )
 +      for ( const char c : m_string )
        {
                if ( !in_variable ) {
 -                      switch ( *i )
 +                      switch ( c )
                        {
                        case '[':
                                in_variable = true;
                                break;
                        default:
 -                              output.push_back( *i );
 +                              output += c;
                                break;
                        }
                }
                else
                {
 -                      switch ( *i )
 +                      switch ( c )
                        {
                        case ']':
                                in_variable = false;
 -                              output.push_string( build_get_variable( variable.c_str() ) );
 -                              variable.clear();
 +                              output += build_get_variable( variable_name );
 +                              variable_name.clear();
                                break;
                        default:
 -                              variable.push_back( *i );
 +                              variable_name += c;
                                break;
                        }
                }
        }
 +
 +      return output;
  }
  void exportXML( XMLImporter& importer ){
        importer << c_str();
@@@ -148,12 -123,12 +148,12 @@@ Conditional( VariableString* test ) : m
        delete m_test;
        delete m_result;
  }
 -void evaluate( StringBuffer& output ){
 -      StringBuffer buffer;
 -      m_test->evaluate( buffer );
 -      if ( !string_empty( buffer.c_str() ) ) {
 -              m_result->evaluate( output );
 +std::string evaluate(){
 +      std::string result = m_test->evaluate();
 +      if ( result.empty() ) {
 +              return result;
        }
 +      return m_result->evaluate();
  }
  void exportXML( XMLImporter& importer ){
        StaticElement conditionElement( "cond" );
@@@ -179,13 -154,11 +179,13 @@@ public
  void push_back( Evaluatable* evaluatable ){
        m_evaluatables.push_back( evaluatable );
  }
 -void evaluate( StringBuffer& output ){
 +std::string evaluate(){
 +      std::string result;
        for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
        {
 -              ( *i )->evaluate( output );
 +              result += ( *i )->evaluate();
        }
 +      return result;
  }
  void exportXML( XMLImporter& importer ){
        for ( Evaluatables::iterator i = m_evaluatables.begin(); i != m_evaluatables.end(); ++i )
@@@ -207,16 -180,16 +207,16 @@@ virtual void popElement( const char* na
  
  class VariableStringXMLConstructor : public XMLElementParser
  {
 -StringBuffer m_buffer;
 +std::string m_buffer;
  VariableString& m_variableString;
  public:
  VariableStringXMLConstructor( VariableString& variableString ) : m_variableString( variableString ){
  }
  ~VariableStringXMLConstructor(){
 -      m_variableString.setString( m_buffer.c_str() );
 +      m_variableString.setString( std::move(m_buffer) );
  }
  std::size_t write( const char* buffer, std::size_t length ){
 -      m_buffer.push_range( buffer, buffer + length );
 +      m_buffer.append( buffer, length );
        return length;
  }
  XMLElementParser& pushElement( const XMLElement& element ){
@@@ -229,16 -202,16 +229,16 @@@ void popElement( const char* name )
  
  class ConditionalXMLConstructor : public XMLElementParser
  {
 -StringBuffer m_buffer;
 +std::string m_buffer;
  Conditional& m_conditional;
  public:
  ConditionalXMLConstructor( Conditional& conditional ) : m_conditional( conditional ){
  }
  ~ConditionalXMLConstructor(){
 -      m_conditional.m_result = new VariableString( m_buffer.c_str() );
 +      m_conditional.m_result = new VariableString( std::move( m_buffer ) );
  }
  std::size_t write( const char* buffer, std::size_t length ){
 -      m_buffer.push_range( buffer, buffer + length );
 +      m_buffer.append( buffer, length );
        return length;
  }
  XMLElementParser& pushElement( const XMLElement& element ){
@@@ -251,7 -224,7 +251,7 @@@ void popElement( const char* name )
  
  class ToolXMLConstructor : public XMLElementParser
  {
 -StringBuffer m_buffer;
 +std::string m_buffer;
  Tool& m_tool;
  ConditionalXMLConstructor* m_conditional;
  public:
@@@ -261,7 -234,7 +261,7 @@@ ToolXMLConstructor( Tool& tool ) : m_to
        flush();
  }
  std::size_t write( const char* buffer, std::size_t length ){
 -      m_buffer.push_range( buffer, buffer + length );
 +      m_buffer.append( buffer, length );
        return length;
  }
  XMLElementParser& pushElement( const XMLElement& element ){
@@@ -286,7 -259,7 +286,7 @@@ void popElement( const char* name )
  
  void flush(){
        if ( !m_buffer.empty() ) {
 -              m_tool.push_back( new VariableString( m_buffer.c_str() ) );
 +              m_tool.push_back( new VariableString( std::move( m_buffer ) ) );
                m_buffer.clear();
        }
  }
@@@ -501,7 -474,8 +501,7 @@@ void project_verify( Project& project, 
  void build_run( const char* name, CommandListener& listener ){
        for ( Tools::iterator i = g_build_tools.begin(); i != g_build_tools.end(); ++i )
        {
 -              StringBuffer output;
 -              ( *i ).second.evaluate( output );
 +              std::string output = ( *i ).second.evaluate();
                build_set_variable( ( *i ).first.c_str(), output.c_str() );
        }
  
                        Build& build = ( *i ).second;
                        for ( Build::iterator j = build.begin(); j != build.end(); ++j )
                        {
 -                              StringBuffer output;
 -                              ( *j ).evaluate( output );
 +                              std::string output = ( *j ).evaluate();
                                listener.execute( output.c_str() );
                        }
                }
@@@ -785,7 -760,7 +785,7 @@@ gboolean project_selection_changed( ui:
        return FALSE;
  }
  
 -gboolean commands_cell_edited(ui::CellRendererText cell, gchar* path_string, gchar* new_text, ui::ListStore store ){
 +gboolean commands_cell_edited(ui::CellRendererText cell, const gchar* path_string, const gchar* new_text, ui::ListStore store ){
        if ( g_current_build == 0 ) {
                return FALSE;
        }
@@@ -851,12 -826,6 +851,12 @@@ gboolean commands_key_press( ui::TreeVi
  ui::Window BuildMenuDialog_construct( ModalDialog& modal, ProjectList& projectList ){
        ui::Window window = MainFrame_getWindow().create_dialog_window("Build Menu", G_CALLBACK(dialog_delete_callback ), &modal, -1, 400 );
  
 +      // FIXME: GTK_WIN_POS_CENTER_ON_PARENT must be used instead but does not work
 +      // for unknown reason.
 +      // It's possible MaingFrame_getWindow() does not return the main window.
 +      // It's known the preferences window has same issue when using MaingFrame_getWindow().
 +      gtk_window_set_position( window, GTK_WIN_POS_CENTER_ALWAYS );
 +
        {
                auto table1 = create_dialog_table( 2, 2, 4, 4, 4 );
                window.add(table1);
                        }
                }
                {
 -                      auto frame = create_dialog_frame( "Commandline" );
 +                      auto frame = create_dialog_frame( "Command line" );
              table1.attach(frame, {0, 1, 1, 2});
                        {
                                auto scr = create_scrolled_window( ui::Policy::NEVER, ui::Policy::AUTOMATIC, 4 );
  namespace
  {
  CopiedString g_buildMenu;
+ CopiedString g_lastExecutedBuild;
  }
  
  void LoadBuildMenu();
@@@ -994,6 -964,7 +995,7 @@@ BuildMenuItem( const char* name, ui::Me
        : m_name( name ), m_item( item ){
  }
  void run(){
+       g_lastExecutedBuild = m_name;
        RunBSP( m_name );
  }
  typedef MemberCaller<BuildMenuItem, void(), &BuildMenuItem::run> RunCaller;
@@@ -1067,3 -1038,13 +1069,13 @@@ void BuildMenu_Construct()
  void BuildMenu_Destroy(){
        SaveBuildMenu();
  }
+ void Build_runRecentExecutedBuild(){
+       if( g_lastExecutedBuild.empty() ){
+               g_BuildMenuItems.begin()->run();
+       }
+       else{
+               RunBSP( g_lastExecutedBuild.c_str() );
+       }
+ }
diff --combined radiant/csg.cpp
index 069c5c012e2c065966f8e003f97d62ae958c632b,2b7d82be09393aa61933a59c37c0808761a19925..be01396d0fa3b75898f09effce1f61f8866b8575
@@@ -34,7 -34,7 +34,7 @@@
  void Face_makeBrush( Face& face, const Brush& brush, brush_vector_t& out, float offset ){
        if ( face.contributes() ) {
                out.push_back( new Brush( brush ) );
 -              Face* newFace = out.back()->addFace( face );
 +              std::shared_ptr<Face> newFace = out.back()->addFace( face );
                face.getPlane().offset( -offset );
                face.planeChanged();
                if ( newFace != 0 ) {
@@@ -50,7 -50,7 +50,7 @@@ void Face_extrude( Face& face, const Br
                face.getPlane().offset( offset );
                out.push_back( new Brush( brush ) );
                face.getPlane().offset( -offset );
 -              Face* newFace = out.back()->addFace( face );
 +              std::shared_ptr<Face> newFace = out.back()->addFace( face );
                if ( newFace != 0 ) {
                        newFace->flipWinding();
                        newFace->planeChanged();
@@@ -143,7 -143,7 +143,7 @@@ void operator()( Face& face ) const 
                                if( caulk ){
                                        Brush_forEachFace( *out.back(), CaulkFace( ExclusionAxis, mindot, maxdot ) );
                                }
 -                              Face* newFace = out.back()->addFace( face );
 +                              std::shared_ptr<Face> newFace = out.back()->addFace( face );
                                if ( newFace != 0 ) {
                                        newFace->flipWinding();
                                }
                                out.push_back( new Brush( brush ) );
                                if( !RemoveInner && caulk )
                                        face.SetShader( getCaulkShader() );
 -                              Face* newFace = out.back()->addFace( face );
 +                              std::shared_ptr<Face> newFace = out.back()->addFace( face );
                                face.getPlane().offset( -offset );
                                face.planeChanged();
                                if( caulk )
                                out.push_back( new Brush( brush ) );
                                out.back()->clear();
  
 -                              Face* newFace = out.back()->addFace( face );
 +                              std::shared_ptr<Face> newFace = out.back()->addFace( face );
                                if ( newFace != 0 ) {
                                        newFace->getPlane().offset( offset );
                                        newFace->planeChanged();
                                out.push_back( new Brush( brush ) );
                                out.back()->clear();
  
 -                              Face* newFace = out.back()->addFace( face );
 +                              std::shared_ptr<Face> newFace = out.back()->addFace( face );
                                if ( newFace != 0 ) {
  
                                        newFace->planeChanged();
@@@ -344,10 -344,10 +344,10 @@@ bool pre( const scene::Path& path, scen
                        {
                                ( *i )->removeEmptyFaces();
                                if( ( *i )->hasContributingFaces() ){
 -                                      NodeSmartReference node( ( new BrushNode() )->node() );
 -                                      Node_getBrush( node )->copy( *( *i ) );
 -                                      delete ( *i );
 -                                      Node_getTraversable( path.parent() )->insert( node );
 +                              NodeSmartReference node( ( new BrushNode() )->node() );
 +                              Node_getBrush( node )->copy( *( *i ) );
 +                              delete ( *i );
 +                              Node_getTraversable( path.parent() )->insert( node );
                                        //path.push( makeReference( node.get() ) );
                                        //selectPath( path, true );
                                        //Instance_getSelectable( *GlobalSceneGraph().find( path ) )->setSelected( true );
@@@ -485,6 -485,15 +485,6 @@@ inline Dereference<Functor> makeDerefer
        return Dereference<Functor>( functor );
  }
  
 -typedef Face* FacePointer;
 -const FacePointer c_nullFacePointer = 0;
 -
 -template<typename Predicate>
 -Face* Brush_findIf( const Brush& brush, const Predicate& predicate ){
 -      Brush::const_iterator i = std::find_if( brush.begin(), brush.end(), makeDereference( predicate ) );
 -      return i == brush.end() ? c_nullFacePointer : *i; // uses c_nullFacePointer instead of 0 because otherwise gcc 4.1 attempts conversion to int
 -}
 -
  template<typename Caller>
  class BindArguments1
  {
@@@ -534,6 -543,7 +534,6 @@@ typedef Function<bool ( const Face &, c
  /// \li flipped && brush is FRONT or ON
  bool Brush_testPlane( const Brush& brush, const Plane3& plane, bool flipped ){
        brush.evaluateBRep();
 -#if 1
        for ( Brush::const_iterator i( brush.begin() ); i != brush.end(); ++i )
        {
                if ( Face_testPlane( *( *i ), plane, flipped ) ) {
                }
        }
        return true;
 -#else
 -      return Brush_findIf( brush, bindArguments( FaceTestPlane(), makeReference( plane ), flipped ) ) == 0;
 -#endif
  }
  
  brushsplit_t Brush_classifyPlane( const Brush& brush, const Plane3& plane ){
@@@ -561,24 -574,24 +561,24 @@@ bool Brush_subtract( const Brush& brush
                fragments.reserve( other.size() );
                Brush back( brush );
  
 -              for ( Brush::const_iterator i( other.begin() ); i != other.end(); ++i )
 +              for ( const std::shared_ptr<Face>& b : other )
                {
 -                      if ( ( *i )->contributes() ) {
 -                              brushsplit_t split = Brush_classifyPlane( back, ( *i )->plane3() );
 +                      if ( b->contributes() ) {
 +                              brushsplit_t split = Brush_classifyPlane( back, b->plane3() );
                                if ( split.counts[ePlaneFront] != 0
                                         && split.counts[ePlaneBack] != 0 ) {
                                        fragments.push_back( new Brush( back ) );
 -                                      Face* newFace = fragments.back()->addFace( *( *i ) );
 -                                      if ( newFace != 0 ) {
 +                                      std::shared_ptr<Face> newFace = fragments.back()->addFace( *b );
 +                                      if ( newFace != nullptr ) {
                                                newFace->flipWinding();
                                        }
 -                                      back.addFace( *( *i ) );
 +                                      back.addFace( *b );
                                }
                                else if ( split.counts[ePlaneBack] == 0 ) {
 -                                      for ( brush_vector_t::iterator i = fragments.begin(); i != fragments.end(); ++i )
 -                                      {
 -                                              delete( *i );
 +                                      for ( Brush *i : fragments ) {
 +                                              delete( i );
                                        }
 +                                      fragments.clear();
                                        return false;
                                }
                        }
@@@ -643,18 -656,23 +643,19 @@@ void post( const scene::Path& path, sce
                        else
                        {
                                ++m_before;
 -                              for ( brush_vector_t::const_iterator i = out.begin(); i != out.end(); ++i )
 -                              {
 +                              for ( Brush *b : out ) {
                                        ++m_after;
 -                                      ( *i )->removeEmptyFaces();
 -                                      if ( !( *i )->empty() ) {
 +                                      b->removeEmptyFaces();
 +                                      if ( !b->empty() ) {
                                                NodeSmartReference node( ( new BrushNode() )->node() );
 -                                              Node_getBrush( node )->copy( *( *i ) );
 -                                              delete ( *i );
 +                                              Node_getBrush( node )->copy( *b );
                                                Node_getTraversable( path.parent() )->insert( node );
                                        }
 -                                      else{
 -                                              delete ( *i );
 -                                      }
 +                                      delete b;
                                }
+                               scene::Node& parent = path.parent();
                                Path_deleteTop( path );
-                               if( Node_getTraversable( path.parent() )->empty() ){
+                               if( Node_getTraversable( parent )->empty() ){
                                        m_eraseParent = true;
                                }
                        }
@@@ -677,7 -695,9 +678,7 @@@ void CSG_Subtract()
  
        if ( selected_brushes.empty() ) {
                globalOutputStream() << "CSG Subtract: No brushes selected.\n";
 -      }
 -      else
 -      {
 +      } else {
                globalOutputStream() << "CSG Subtract: Subtracting " << Unsigned( selected_brushes.size() ) << " brushes.\n";
  
                UndoableCommand undo( "brushSubtract" );
@@@ -712,20 -732,12 +713,20 @@@ bool pre( const scene::Path& path, scen
  }
  
  void post( const scene::Path& path, scene::Instance& instance ) const {
 -      if ( path.top().get().visible() ) {
 +      if ( !path.top().get().visible() ) {
 +              return;
 +      }
 +
                Brush* brush = Node_getBrush( path.top() );
 -              if ( brush != 0
 -                       && Instance_getSelectable( instance )->isSelected() ) {
 +      if ( brush == nullptr || !Instance_getSelectable( instance )->isSelected() ) {
 +              return;
 +      }
 +
                        Plane3 plane( plane3_for_points( m_p0, m_p1, m_p2 ) );
 -                      if ( plane3_valid( plane ) ) {
 +      if ( !plane3_valid( plane ) ) {
 +              return;
 +      }
 +
                                brushsplit_t split = Brush_classifyPlane( *brush, m_split == eFront ? plane3_flipped( plane ) : plane );
                                if ( split.counts[ePlaneBack] && split.counts[ePlaneFront] ) {
                                        // the plane intersects this brush
                                                NodeSmartReference node( ( new BrushNode() )->node() );
                                                Brush* fragment = Node_getBrush( node );
                                                fragment->copy( *brush );
 -                                              Face* newFace = fragment->addPlane( m_p0, m_p1, m_p2, m_shader, m_projection );
 +                      std::shared_ptr<Face> newFace =
 +                              fragment->addPlane( m_p0, m_p1, m_p2, m_shader, m_projection );
                                                if ( newFace != 0 && m_split != eFront ) {
                                                        newFace->flipWinding();
                                                }
                                                }
                                        }
  
 -                                      Face* newFace = brush->addPlane( m_p0, m_p1, m_p2, m_shader, m_projection );
 +              std::shared_ptr<Face> newFace = brush->addPlane( m_p0, m_p1, m_p2, m_shader, m_projection );
                                        if ( newFace != 0 && m_split == eFront ) {
                                                newFace->flipWinding();
                                        }
                                if ( m_split != eFrontAndBack && split.counts[ePlaneBack] != 0 ) {
                                        // the brush is "behind" the plane
                                        Path_deleteTop( path );
 -                              }
 -                      }
 -              }
        }
  }
  };
diff --combined radiant/environment.cpp
index bd07533a6825ab1f06178d02ab57d606830f8194,a1a13b8686442e5faf01f6fdb7582ec56ae502cd..1e02a14153336f81fdb408ae11cc2b4d51b25b85
@@@ -140,10 -140,8 +140,10 @@@ void gamedetect()
                        if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "nexuiz.exe", buf, p - buf ) )
  #elif GDEF_OS_MACOS
                        if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "Nexuiz.app/Contents/Info.plist", buf, p - buf ) )
 -#else
 +#elif GDEF_OS_LINUX
                        if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", "nexuiz-linux-glx.sh", buf, p - buf ) )
 +#else
 +                      if ( gamedetect_check_game( "nexuiz.game", "data/common-spog.pk3", NULL, buf, p - buf ) )
  #endif
                        { return; }
  
  
  namespace
  {
 -CopiedString home_path;
 -CopiedString app_path;
 +      // executable file path
 +      CopiedString app_filepath;
 +      // directory paths
 +      CopiedString home_path;
 +      CopiedString app_path;
 +      CopiedString lib_path;
 +      CopiedString data_path;
 +}
 +
 +const char* environment_get_app_filepath(){
 +      return app_filepath.c_str();
  }
  
  const char* environment_get_home_path(){
@@@ -189,26 -178,30 +189,40 @@@ const char* environment_get_app_path()
        return app_path.c_str();
  }
  
 +const char *environment_get_lib_path()
 +{
 +      return lib_path.c_str();
 +}
 +
 +const char *environment_get_data_path()
 +{
 +      return data_path.c_str();
 +}
 +
  bool portable_app_setup(){
        StringOutputStream confdir( 256 );
        confdir << app_path.c_str() << "settings/";
 -      if ( file_exists( confdir.c_str() ) ) {
 +      if ( file_is_directory( confdir.c_str() ) ) {
                home_path = confdir.c_str();
                return true;
        }
        return false;
  }
  
+ const char* openCmdMap;
+ void cmdMap(){
+       openCmdMap = NULL;
+       for ( int i = 1; i < g_argc; ++i )
+       {
+               //if ( !stricmp( g_argv[i] + strlen(g_argv[i]) - 4, ".map" ) ){
+               if( string_equal_suffix_nocase( g_argv[i], ".map" ) ){
+                       openCmdMap = g_argv[i];
+               }
+       }
+ }
  #if GDEF_OS_POSIX
  
  #include <stdlib.h>
  const char* LINK_NAME =
  #if GDEF_OS_LINUX
        "/proc/self/exe"
 -#else // FreeBSD and OSX
 +#else // FreeBSD and macOS
        "/proc/curproc/file"
  #endif
  ;
  
 -/// brief Returns the filename of the executable belonging to the current process, or 0 if not found.
 +/// brief Returns the filename of the executable belonging to the current process, or empty string if not found.
  char const* getexename( char *buf ){
        /* Now read the symbolic link */
 -      int ret = readlink( LINK_NAME, buf, PATH_MAX );
 +      const int ret = readlink( LINK_NAME, buf, PATH_MAX );
  
        if ( ret == -1 ) {
                globalOutputStream() << "getexename: falling back to argv[0]: " << makeQuoted( g_argv[0] );
 -              const char* path = realpath( g_argv[0], buf );
 -              if ( path == 0 ) {
 +              if( realpath( g_argv[0], buf ) == 0 ) {
                        /* In case of an error, leave the handling up to the caller */
 -                      return "";
 +                      *buf = '\0';
                }
        }
 +      else {
 +              /* Ensure proper NUL termination */
 +              buf[ret] = 0;
 +      }
  
 -      /* Ensure proper NUL termination */
 -      buf[ret] = 0;
 +      return buf;
 +}
  
 +char const* getexepath( char *buf ) {
        /* delete the program name */
        *( strrchr( buf, '/' ) ) = '\0';
  
@@@ -273,102 -262,31 +287,91 @@@ void environment_init( int argc, char c
  
        {
                char real[PATH_MAX];
 -              app_path = getexename( real );
 -              ASSERT_MESSAGE( !string_empty( app_path.c_str() ), "failed to deduce app path" );
 +              app_filepath = getexename( real );
 +              ASSERT_MESSAGE( !string_empty( app_filepath.c_str() ), "failed to deduce app path" );
 +
 +              strncpy( real, app_filepath.c_str(), strlen( app_filepath.c_str() ) );
 +              app_path = getexepath( real );
 +      }
 +
 +      {
 +#if defined(RADIANT_FHS_INSTALL)
 +              StringOutputStream buffer;
 +      #if defined(RADIANT_ADDONS_DIR)
 +              buffer << RADIANT_ADDONS_DIR << "/";
 +      #else
 +              buffer << app_path.c_str() << "../lib/";
 +              buffer << RADIANT_LIB_ARCH << "/";
 +              buffer << RADIANT_BASENAME << "/";
 +      #endif
 +              lib_path = buffer.c_str();
 +#else
 +              lib_path = app_path.c_str();
 +#endif
 +      }
 +
 +      {
 +#if defined(RADIANT_FHS_INSTALL)
 +              StringOutputStream buffer;
 +      #if defined(RADIANT_DATA_DIR)
 +              buffer << RADIANT_DATA_DIR << "/";
 +      #else
 +              buffer << app_path.c_str() << "../share/";
 +              buffer << RADIANT_BASENAME << "/";
 +      #endif
 +              data_path = buffer.c_str();
 +#else
 +              data_path = app_path.c_str();
 +#endif
        }
  
        if ( !portable_app_setup() ) {
                StringOutputStream home( 256 );
 -              home << DirectoryCleaned( g_get_user_config_dir() ) << "netradiant/";
 +#if GDEF_OS_MACOS
 +              /* This is used on macOS, this will produce
 +              ~/Library/Application Support/NetRadiant folder. */
 +              home << DirectoryCleaned( g_get_home_dir() );
 +              Q_mkdir( home.c_str() );
 +              home << "Library/";
 +              Q_mkdir( home.c_str() );
 +              home << "Application Support/";
 +              Q_mkdir( home.c_str() );
 +              home << RADIANT_NAME << "/";
 +#else // if GDEF_OS_XDG
 +              /* This is used on both Linux and FreeBSD,
 +              this will produce ~/.config/netradiant folder
 +              when environment has default settings, the
 +              XDG_CONFIG_HOME variable modifies it. */
 +              home << DirectoryCleaned( g_get_user_config_dir() );
 +              Q_mkdir( home.c_str() );
 +              home << RADIANT_BASENAME << "/";
 +#endif // ! GDEF_OS_MACOS
                Q_mkdir( home.c_str() );
                home_path = home.c_str();
        }
 +
        gamedetect();
+       cmdMap();
  }
  
  #elif GDEF_OS_WINDOWS
  
  #include <windows.h>
  
- char* openCmdMap;
- void cmdMap(){
-       openCmdMap = NULL;
-       for ( int i = 1; i < g_argc; ++i )
-       {
-               if ( !stricmp( g_argv[i] + strlen(g_argv[i]) - 4, ".map" ) ){
-                       openCmdMap = g_argv[i];
-               }
-       }
- }
  void environment_init( int argc, char const* argv[] ){
        args_init( argc, argv );
  
        {
                // get path to the editor
                char filename[MAX_PATH + 1];
 +              StringOutputStream app_filepath_stream( 256 );
 +              StringOutputStream app_path_stream( 256 );
 +
                GetModuleFileName( 0, filename, MAX_PATH );
 +              
 +              app_filepath_stream << PathCleaned( filename );
 +              app_filepath = app_filepath_stream.c_str();
 +
                char* last_separator = strrchr( filename, '\\' );
                if ( last_separator != 0 ) {
                        *( last_separator + 1 ) = '\0';
                {
                        filename[0] = '\0';
                }
 -              StringOutputStream app( 256 );
 -              app << PathCleaned( filename );
 -              app_path = app.c_str();
 +
 +              app_path_stream << PathCleaned( filename );
 +              app_path = app_path_stream.c_str();
 +
 +              lib_path = app_path;
 +              data_path = app_path;
        }
  
        if ( !portable_app_setup() ) {
                char *appdata = getenv( "APPDATA" );
 +
                StringOutputStream home( 256 );
                home << PathCleaned( appdata );
 -              home << "/NetRadiantSettings/";
 +              home << "/";
 +              home << RADIANT_NAME;
 +              home << "/";
 +
                Q_mkdir( home.c_str() );
                home_path = home.c_str();
        }
diff --combined radiant/environment.h
index 2e57fb123c677ea1cf3fe0025f999ee591f20c2f,566a307e64874662845ce835551e689991f1495a..a7de6d4f0fbc896354a8eab7d6d9288d74001eb2
  #define INCLUDED_ENVIRONMENT_H
  
  void environment_init( int argc, char const* argv[] );
 +
 +const char* environment_get_app_filepath();
 +
  const char* environment_get_home_path();
  const char* environment_get_app_path();
 +const char *environment_get_lib_path();
 +const char *environment_get_data_path();
  
  extern int g_argc;
  extern char const** g_argv;
  
- #if defined( WIN32 )
- extern char* openCmdMap;
- #endif
+ extern const char* openCmdMap;
  
  #endif
diff --combined radiant/main.cpp
index a2d288f15eff91315e794a5a14f8ed1419ce370a,6093f87971528835fb0898f467869efed8ddb52b..20bea80fde8533800f6a74ef0f68226c95c745d6
@@@ -64,6 -64,8 +64,6 @@@
  #include "main.h"
  #include "globaldefs.h"
  
 -#include "version.h"
 -
  #include "debugging/debugging.h"
  
  #include "iundo.h"
@@@ -302,12 -304,12 +302,12 @@@ bool handleMessage()
                ScopedLock lock( m_lock );
          if (GDEF_DEBUG) {
              m_buffer << "Break into the debugger?\n";
 -            bool handled = ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::YESNO, ui::alert_icon::Error) == ui::alert_response::NO;
 +            bool handled = ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::YESNO, ui::alert_icon::Error) == ui::alert_response::NO;
              m_buffer.clear();
              return handled;
          } else {
              m_buffer << "Please report this error to the developers\n";
 -            ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::OK, ui::alert_icon::Error);
 +            ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::OK, ui::alert_icon::Error);
              m_buffer.clear();
          }
        }
@@@ -327,10 -329,7 +327,10 @@@ void paths_init()
  
        Q_mkdir( g_strSettingsPath.c_str() );
  
 +      g_strAppFilePath = environment_get_app_filepath();
        g_strAppPath = environment_get_app_path();
 +      g_strLibPath = environment_get_lib_path();
 +      g_strDataPath = environment_get_data_path();
  
        // radiant is installed in the parent dir of "tools/"
        // NOTE: this is not very easy for debugging
        // (for now I had to create symlinks)
        {
                StringOutputStream path( 256 );
 -              path << g_strAppPath.c_str() << "bitmaps/";
 +              path << g_strDataPath.c_str() << "bitmaps/";
                BitmapsPath_set( path.c_str() );
        }
  
        // we will set this right after the game selection is done
 -      g_strGameToolsPath = g_strAppPath;
 +      g_strGameToolsPath = g_strDataPath;
  }
  
  bool check_version_file( const char* filename, const char* version ){
        return false;
  }
  
 -bool check_version(){
 -      // a safe check to avoid people running broken installations
 -      // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
 -      // make something idiot proof and someone will make better idiots, this may be overkill
 -      // let's leave it disabled in debug mode in any case
 -      // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
 -    if (GDEF_DEBUG) {
 -        return true;
 -    }
 -    // locate and open RADIANT_MAJOR and RADIANT_MINOR
 -    bool bVerIsGood = true;
 -    {
 -        StringOutputStream ver_file_name(256);
 -        ver_file_name << AppPath_get() << "RADIANT_MAJOR";
 -        bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MAJOR_VERSION);
 -    }
 -    {
 -        StringOutputStream ver_file_name(256);
 -        ver_file_name << AppPath_get() << "RADIANT_MINOR";
 -        bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MINOR_VERSION);
 -    }
 -
 -    if (!bVerIsGood) {
 -        StringOutputStream msg(256);
 -        msg
 -                << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
 -                        "Make sure you run the right/latest editor binary you installed\n"
 -                << AppPath_get();
 -        ui::alert(ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Default);
 -    }
 -    return bVerIsGood;
 -}
 -
  void create_global_pid(){
        /*!
           the global prefs loading / game selection dialog might fail for any reason we don't know about
                if ( remove( g_pidFile.c_str() ) == -1 ) {
                        StringOutputStream msg( 256 );
                        msg << "WARNING: Could not delete " << g_pidFile.c_str();
 -                      ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
 +                      ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
                }
  
                // in debug, never prompt to clean registry, turn console logging auto after a failed start
                if (!GDEF_DEBUG) {
                        StringOutputStream msg(256);
 -                      msg << "Radiant failed to start properly the last time it was run.\n"
 +                      msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
                                        "The failure may be related to current global preferences.\n"
                                        "Do you want to reset global preferences to defaults?";
  
 -                      if (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
 +                      if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
                                g_GamesDialog.Reset();
                        }
  
                        msg.clear();
                        msg << "Logging console output to " << SettingsPath_get()
 -                              << "radiant.log\nRefer to the log if Radiant fails to start again.";
 +                              << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
  
 -                      ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
 +                      ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
                }
  
                // set without saving, the class is not in a coherent state yet
                // just do the value change and call to start logging, CGamesDialog will pickup when relevant
                g_GamesDialog.m_bForceLogConsole = true;
 -              Sys_LogFile( true );
 +              Sys_EnableLogFile( true );
        }
  
        // create a primary .pid for global init run
@@@ -425,7 -457,7 +425,7 @@@ void remove_global_pid()
        if ( remove( g_pidFile.c_str() ) == -1 ) {
                StringOutputStream msg( 256 );
                msg << "WARNING: Could not delete " << g_pidFile.c_str();
 -              ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
 +              ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
        }
  }
  
@@@ -443,30 -475,30 +443,30 @@@ void create_local_pid()
                if ( remove( g_pidGameFile.c_str() ) == -1 ) {
                        StringOutputStream msg;
                        msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
 -                      ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
 +                      ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
                }
  
                // in debug, never prompt to clean registry, turn console logging auto after a failed start
                if (!GDEF_DEBUG) {
                        StringOutputStream msg;
 -                      msg << "Radiant failed to start properly the last time it was run.\n"
 +                      msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
                                        "The failure may be caused by current preferences.\n"
                                        "Do you want to reset all preferences to defaults?";
  
 -                      if (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
 +                      if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
                                Preferences_Reset();
                        }
  
                        msg.clear();
                        msg << "Logging console output to " << SettingsPath_get()
 -                              << "radiant.log\nRefer to the log if Radiant fails to start again.";
 +                              << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
  
 -                      ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
 +                      ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
                }
  
                // force console logging on! (will go in prefs too)
                g_GamesDialog.m_bForceLogConsole = true;
 -              Sys_LogFile( true );
 +              Sys_EnableLogFile( true );
        }
        else
        {
@@@ -527,23 -559,7 +527,23 @@@ void add_local_rc_files()
  #endif // GARUX_DISABLE_GTKTHEME
  }
  
 +/* HACK: If ui::main is not called yet,
 +gtk_main_quit will not quit, so tell main
 +to not call ui::main. This happens when a
 +map is loaded from command line and require
 +a restart because of wrong format.
 +Delete this when the code to not have to
 +restart to load another format is merged. */
 +bool g_dontStart = false;
 +
  int main( int argc, char* argv[] ){
 +#if GTK_TARGET == 3
 +      // HACK: force legacy GL backend as we don't support GL3 yet
 +      setenv("GDK_GL", "LEGACY", 0);
 +#if GDEF_OS_LINUX || GDEF_OS_BSD
 +      setenv("GDK_BACKEND", "x11", 0);
 +#endif
 +#endif // GTK_TARGET == 3
        crt_init();
  
        streams_init();
  #endif
  
        const char* mapname = NULL;
 +
 +#if GDEF_OS_WINDOWS
 +      StringOutputStream mapname_buffer( 256 );
 +#endif
 +
      char const *error = NULL;
 +
        if ( !ui::init( &argc, &argv, "<filename.map>", &error) ) {
                g_print( "%s\n", error );
                return -1;
        // Gtk already removed parsed `--options`
        if (argc == 2) {
                if ( strlen( argv[1] ) > 1 ) {
 -                      if ( g_str_has_suffix( argv[1], ".map" ) ) {
 -                              if ( g_path_is_absolute( argv[1] ) ) {
                                        mapname = argv[1];
 +
 +                      if ( g_str_has_suffix( mapname, ".map" ) ) {
 +                              if ( !g_path_is_absolute( mapname ) ) {
 +                                      mapname = g_build_filename( g_get_current_dir(), mapname, NULL );
                                }
 -                              else {
 -                                      mapname = g_build_filename( g_get_current_dir(), argv[1], NULL );
 -                              }
 +
 +#if GDEF_OS_WINDOWS
 +                              mapname_buffer << PathCleaned( mapname );
 +                              mapname = mapname_buffer.c_str();
 +#endif
                        }
                        else {
 -                              g_print( "bad file name, will not load: %s\n", argv[1] );
 +                              g_print( "bad file name, will not load: %s\n", mapname );
 +                              mapname = NULL;
                        }
                }
        }
  
        add_local_rc_files();
  
 -      if ( !check_version() ) {
 -              return EXIT_FAILURE;
 -      }
 -
        show_splash();
  
        create_global_pid();
        // we may have the console turned on and want to keep it that way
        // so we use a latching system
        if ( g_GamesDialog.m_bForceLogConsole ) {
 -              Sys_LogFile( true );
 +              Sys_EnableLogFile( true );
                g_Console_enableLogging = true;
                g_GamesDialog.m_bForceLogConsole = false;
        }
  
        hide_splash();
  
- #ifdef WIN32
        if( openCmdMap && *openCmdMap ){
                Map_LoadFile( openCmdMap );
        }
-       else
- #endif // WIN32
-       if ( mapname != NULL ) {
+       else if ( mapname != NULL ) {
                Map_LoadFile( mapname );
        }
        else if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
  
        remove_local_pid();
  
 +      /* HACK: If ui::main is not called yet,
 +      gtk_main_quit will not quit, so tell main
 +      to not call ui::main. This happens when a
 +      map is loaded from command line and require
 +      a restart because of wrong format.
 +      Delete this when the code to not have to
 +      restart to load another format is merged. */
 +      if ( !g_dontStart )
 +      {
        ui::main();
 +      }
  
        // avoid saving prefs when the app is minimized
        if ( g_pParentWnd->IsSleeping() ) {
        Radiant_Shutdown();
  
        // close the log file if any
 -      Sys_LogFile( false );
 +      Sys_EnableLogFile( false );
  
        return EXIT_SUCCESS;
  }
diff --combined radiant/mainframe.cpp
index 7c74d2b465005c909086e0f077b7493a8df8df81,4bd30a9561f140282d9028b0245b36ac3cb46c3f..dd18db7101188bd41e12623e6e07687f7fadfda4
  #include "texwindow.h"
  #include "filterbar.h"
  
 +#if GDEF_OS_WINDOWS
 +#include <process.h>
 +#else
 +#include <spawn.h>
 +#endif
 +
 +#ifdef WORKAROUND_WINDOWS_GTK2_GLWIDGET
 +/* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 +#define WORKAROUND_GOBJECT_SET_GLWIDGET(window, widget) g_object_set_data( G_OBJECT( window ), "glwidget", G_OBJECT( widget ) )
 +#else
 +#define WORKAROUND_GOBJECT_SET_GLWIDGET(window, widget)
 +#endif
 +
  #define GARUX_DISABLE_GTKTHEME
  #ifndef GARUX_DISABLE_GTKTHEME
  #include "gtktheme.h"
  #endif
  
 -
  struct layout_globals_t
  {
        WindowPosition m_position;
        layout_globals_t() :
                m_position( -1, -1, 640, 480 ),
  
-               nXYHeight( 300 ),
-               nXYWidth( 300 ),
-               nCamWidth( 200 ),
-               nCamHeight( 200 ),
-               nState( GDK_WINDOW_STATE_MAXIMIZED ){
+               nXYHeight( 350 ),
+               nXYWidth( 600 ),
+               nCamWidth( 300 ),
+               nCamHeight( 210 ),
+               nState( 0 ){
        }
  };
  
@@@ -173,8 -161,6 +173,8 @@@ void VFS_Refresh()
        RefreshReferences();
        // also refresh texture browser
        TextureBrowser_RefreshShaders();
 +      // also show textures (all or common)
 +      TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
  }
  
  void VFS_Restart(){
@@@ -233,7 -219,9 +233,7 @@@ void HomePaths_Realise()
                        }
                        path.clear();
                        path << DirectoryCleaned( g_get_home_dir() ) << prefix << "/";
 -#endif
 -
 -#if GDEF_OS_WINDOWS
 +#elif GDEF_OS_WINDOWS
                        TCHAR mydocsdir[MAX_PATH + 1];
                        wchar_t *mydocsdirw;
                        HMODULE shfolder = LoadLibrary( "shfolder.dll" );
                                        break;
                                }
                        }
 -#endif
 -
 -#if GDEF_OS_POSIX
 +#elif GDEF_OS_XDG
 +                      path.clear();
 +                      path << DirectoryCleaned( g_get_user_data_dir() ) << ( prefix + 1 ) << "/";
 +                      if ( file_exists( path.c_str() ) && file_is_directory( path.c_str() ) ) {
 +                              g_qeglobals.m_userEnginePath = path.c_str();
 +                              break;
 +                      }
 +                      else {
                        path.clear();
                        path << DirectoryCleaned( g_get_home_dir() ) << prefix << "/";
                        g_qeglobals.m_userEnginePath = path.c_str();
                        break;
 +                      }
  #endif
                }
  
@@@ -458,32 -440,14 +458,32 @@@ void setPakPath( int num, const char* p
  }
  
  
 -// App Path
 +// executable file path (full path)
 +CopiedString g_strAppFilePath;
 +
 +// directory paths
 +CopiedString g_strAppPath; 
 +CopiedString g_strLibPath;
 +CopiedString g_strDataPath;
  
 -CopiedString g_strAppPath;                 ///< holds the full path of the executable
 +const char* AppFilePath_get(){
 +      return g_strAppFilePath.c_str();
 +}
  
  const char* AppPath_get(){
        return g_strAppPath.c_str();
  }
  
 +const char *LibPath_get()
 +{
 +    return g_strLibPath.c_str();
 +}
 +
 +const char *DataPath_get()
 +{
 +    return g_strDataPath.c_str();
 +}
 +
  /// the path to the local rc-dir
  const char* LocalRcPath_get( void ){
        static CopiedString rc_path;
@@@ -582,28 -546,39 +582,28 @@@ struct PakPath4 
  bool g_disableEnginePath = false;
  bool g_disableHomePath = false;
  
 -void Paths_constructPreferences( PreferencesPage& page ){
 +void Paths_constructBasicPreferences(  PreferencesPage& page ) {
        page.appendPathEntry( "Engine Path", true, make_property<EnginePath>(g_strEnginePath) );
 +}
  
 -      page.appendCheckBox(
 -              "", "Do not use Engine Path",
 -              g_disableEnginePath
 -                                                );
 +void Paths_constructPreferences( PreferencesPage& page ){
 +      Paths_constructBasicPreferences( page );
  
 -      page.appendCheckBox(
 -              "", "Do not use Home Path",
 -              g_disableHomePath
 -              );
 +      page.appendSpacer( 4 );
 +      page.appendLabel( "", "Advanced options" );
 +      page.appendCheckBox( "", "Do not use Engine Path", g_disableEnginePath );
 +      page.appendCheckBox( "", "Do not use Home Path", g_disableHomePath );
  
 -      for ( int i = 0; i < g_pakPathCount; i++ ) {
 -              std::string label = "Pak Path " + std::to_string(i);
 -              switch (i) {
 -                      case 0:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath0>( g_strPakPath[i] ) );
 -                      break;
 -                      case 1:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath1>( g_strPakPath[i] ) );
 -                      break;
 -                      case 2:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath2>( g_strPakPath[i] ) );
 -                      break;
 -                      case 3:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath3>( g_strPakPath[i] ) );
 -                      break;
 -                      case 4:
 -                      page.appendPathEntry( label.c_str(), true, make_property<PakPath4>( g_strPakPath[i] ) );
 -                      break;
 -}
 -      }
 +      page.appendSpacer( 4 );
 +      page.appendLabel( "", "Only a very few games support Pak Paths," );
 +      page.appendLabel( "", "if you don't know what it is, leave this blank." );
 +
 +      const char *label = "Pak Path ";
 +      page.appendPathEntry( label, true, make_property<PakPath0>( g_strPakPath[0] ) );
 +      page.appendPathEntry( label, true, make_property<PakPath1>( g_strPakPath[1] ) );
 +      page.appendPathEntry( label, true, make_property<PakPath2>( g_strPakPath[2] ) );
 +      page.appendPathEntry( label, true, make_property<PakPath3>( g_strPakPath[3] ) );
 +      page.appendPathEntry( label, true, make_property<PakPath4>( g_strPakPath[4] ) );
  }
  
  void Paths_constructPage( PreferenceGroup& group ){
@@@ -620,14 -595,14 +620,14 @@@ class PathsDialog : public Dialo
  {
  public:
  ui::Window BuildDialog(){
 -      auto frame = create_dialog_frame( "Path settings", ui::Shadow::ETCHED_IN );
 +      auto frame = create_dialog_frame( "Path Settings", ui::Shadow::ETCHED_IN );
  
        auto vbox2 = create_dialog_vbox( 0, 4 );
        frame.add(vbox2);
  
        {
 -              PreferencesPage preferencesPage( *this, vbox2 );
 -              Paths_constructPreferences( preferencesPage );
 +              PreferencesPage page( *this, vbox2 );
 +              Paths_constructBasicPreferences( page );
        }
  
        return ui::Window(create_simple_modal_dialog_window( "Engine Path Not Found", m_modal, frame ));
@@@ -786,7 -761,7 +786,7 @@@ void Radiant_detachGameToolsPathObserve
  void Radiant_Initialise(){
        GlobalModuleServer_Initialise();
  
 -      Radiant_loadModulesFromRoot( AppPath_get() );
 +      Radiant_loadModulesFromRoot( LibPath_get() );
  
        Preferences_Load();
  
@@@ -815,7 -790,7 +815,7 @@@ void Radiant_Shutdown()
  }
  
  void Exit(){
 -      if ( ConfirmModified( "Exit Radiant" ) ) {
 +      if ( ConfirmModified( "Exit " RADIANT_NAME ) ) {
                gtk_main_quit();
        }
  }
@@@ -976,53 -951,6 +976,53 @@@ void ColorScheme_Ydnar()
        XY_UpdateAllWindows();
  }
  
 +/* color scheme to fit the GTK Adwaita Dark theme */
 +void ColorScheme_AdwaitaDark()
 +{
 +      // SI_Colors0
 +      // GlobalTextureBrowser().color_textureback
 +      TextureBrowser_setBackgroundColour(GlobalTextureBrowser(), Vector3(0.25f, 0.25f, 0.25f));
 +
 +      // SI_Colors4
 +      g_camwindow_globals.color_cameraback = Vector3(0.25f, 0.25f, 0.25f);
 +      // SI_Colors12
 +      g_camwindow_globals.color_selbrushes3d = Vector3(1.0f, 0.0f, 0.0f);
 +      CamWnd_Update(*g_pParentWnd->GetCamWnd());
 +
 +      // SI_Colors1
 +      g_xywindow_globals.color_gridback = Vector3(0.25f, 0.25f, 0.25f);
 +      // SI_Colors2
 +      g_xywindow_globals.color_gridminor = Vector3(0.21f, 0.23f, 0.23f);
 +      // SI_Colors3
 +      g_xywindow_globals.color_gridmajor = Vector3(0.14f, 0.15f, 0.15f);
 +      // SI_Colors14
 +      g_xywindow_globals.color_gridmajor_alt = Vector3(1.0f, 0.0f, 0.0f);
 +      // SI_Colors6
 +      g_xywindow_globals.color_gridblock = Vector3(1.0f, 1.0f, 1.0f);
 +      // SI_Colors7
 +      g_xywindow_globals.color_gridtext = Vector3(0.0f, 0.0f, 0.0f);
 +      // ??
 +      g_xywindow_globals.color_selbrushes = Vector3(1.0f, 0.0f, 0.0f);
 +      // ??
 +      g_xywindow_globals.color_clipper = Vector3(0.0f, 0.0f, 1.0f);
 +      // SI_Colors8
 +      g_xywindow_globals.color_brushes = Vector3(0.73f, 0.73f, 0.73f);
 +
 +      // SI_AxisColors0
 +      g_xywindow_globals.AxisColorX = Vector3(1.0f, 0.0f, 0.0f);
 +      // SI_AxisColors1
 +      g_xywindow_globals.AxisColorY = Vector3(0.0f, 1.0f, 0.0f);
 +      // SI_AxisColors2
 +      g_xywindow_globals.AxisColorZ = Vector3(0.0f, 0.0f, 1.0f);
 +      SetWorldspawnColour(g_xywindow_globals.color_brushes);
 +      // ??
 +      g_xywindow_globals.color_viewname = Vector3(0.5f, 0.0f, 0.75f);
 +      XY_UpdateAllWindows();
 +
 +      // SI_Colors5
 +      // g_entity_globals.color_entity = Vector3(0.0f, 0.0f, 0.0f);
 +}
 +
  typedef Callback<void(Vector3&)> GetColourCallback;
  typedef Callback<void(const Vector3&)> SetColourCallback;
  
@@@ -1140,7 -1068,6 +1140,7 @@@ ui::MenuItem create_colours_menu()
        create_menu_item_with_mnemonic( menu_3, "Q3Radiant Original", "ColorSchemeQER" );
        create_menu_item_with_mnemonic( menu_3, "Black and Green", "ColorSchemeBlackAndGreen" );
        create_menu_item_with_mnemonic( menu_3, "Maya/Max/Lightwave Emulation", "ColorSchemeYdnar" );
 +      create_menu_item_with_mnemonic(menu_3, "Adwaita Dark", "ColorSchemeAdwaitaDark");
  
  #ifndef GARUX_DISABLE_GTKTHEME
        create_menu_item_with_mnemonic( menu_in_menu, "GTK Theme...", "gtkThemeDlg" );
@@@ -1839,11 -1766,9 +1839,11 @@@ void Selection_SnapToGrid()
  
  
  static gint qe_every_second( gpointer data ){
 -      GdkModifierType mask;
 +      if (g_pParentWnd == nullptr)
 +              return TRUE;
  
 -      gdk_window_get_pointer( 0, 0, 0, &mask );
 +      GdkModifierType mask;
 +      gdk_window_get_pointer( gtk_widget_get_window(g_pParentWnd->m_window), nullptr, nullptr, &mask );
  
        if ( ( mask & ( GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK ) ) == 0 ) {
                QE_CheckAutoSave();
@@@ -1955,18 -1880,15 +1955,18 @@@ void ScreenUpdates_Disable( const char
                bool isActiveApp = MainFrame_isActiveApp();
  
                g_wait = create_wait_dialog( title, message );
 -              gtk_grab_add( g_wait.m_window  );
  
                if ( isActiveApp ) {
                        g_wait.m_window.show();
 +                      gtk_grab_add( g_wait.m_window  );
                        ScreenUpdates_process();
                }
        }
        else if ( g_wait.m_window.visible() ) {
                g_wait.m_label.text(message);
 +              if ( GTK_IS_WINDOW(g_wait.m_window) ) {
 +                      gtk_grab_add(g_wait.m_window);
 +              }
                ScreenUpdates_process();
        }
        g_wait_stack.push_back( message );
@@@ -2191,9 -2113,6 +2191,9 @@@ ui::MenuItem create_view_menu( MainFram
                create_menu_item_with_mnemonic( camera_menu, "Far Clip Plane In", "CubicClipZoomIn" );
                create_menu_item_with_mnemonic( camera_menu, "Far Clip Plane Out", "CubicClipZoomOut" );
                menu_separator( camera_menu );
 +              create_menu_item_with_mnemonic( camera_menu, "Decrease FOV", "FOVDec" );
 +              create_menu_item_with_mnemonic( camera_menu, "Increase FOV", "FOVInc" );
 +              menu_separator( camera_menu );
                create_menu_item_with_mnemonic( camera_menu, "Next leak spot", "NextLeakSpot" );
                create_menu_item_with_mnemonic( camera_menu, "Previous leak spot", "PrevLeakSpot" );
                menu_separator( camera_menu );
@@@ -2347,6 -2266,7 +2347,7 @@@ ui::MenuItem create_bsp_menu()
        }
  
        create_menu_item_with_mnemonic( menu, "Customize...", "BuildMenuCustomize" );
+       create_menu_item_with_mnemonic( menu, "Run recent build", "Build_runRecentExecutedBuild" );
  
        menu_separator( menu );
  
@@@ -2388,6 -2308,7 +2389,7 @@@ ui::MenuItem create_misc_menu()
        // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=394
  //  create_menu_item_with_mnemonic(menu, "_Print XY View", FreeCaller<void(), WXY_Print>());
        create_menu_item_with_mnemonic( menu, "_Background select", makeCallbackF(WXY_BackgroundSelect) );
+       create_menu_item_with_mnemonic( menu, "Fullscreen", "Fullscreen" );
        return misc_menu_item;
  }
  
@@@ -2446,7 -2367,7 +2448,7 @@@ ui::MenuItem create_help_menu()
  
        create_menu_item_with_mnemonic( menu, "Bug report", makeCallbackF(OpenBugReportURL) );
        create_menu_item_with_mnemonic( menu, "Shortcuts list", makeCallbackF(DoCommandListDlg) );
 -      create_menu_item_with_mnemonic( menu, "_About", makeCallbackF(DoAbout) );
 +      create_menu_item_with_mnemonic( menu, "_About...", makeCallbackF(DoAbout) );
  
        return help_menu_item;
  }
@@@ -2836,14 -2757,10 +2838,14 @@@ MainFrame::~MainFrame()
  
        for ( std::vector<ui::Widget>::iterator i = g_floating_windows.begin(); i != g_floating_windows.end(); ++i )
        {
 +#ifndef WORKAROUND_MACOS_GTK2_DESTROY
                i->destroy();
 +#endif
        }
  
 +#ifndef WORKAROUND_MACOS_GTK2_DESTROY
        m_window.destroy();
 +#endif
  }
  
  void MainFrame::SetActiveXY( XYWnd* p ){
@@@ -3000,16 -2917,13 +3002,16 @@@ WindowPositionTracker g_posXZWnd
  WindowPositionTracker g_posYZWnd;
  
  static gint mainframe_delete( ui::Widget widget, GdkEvent *event, gpointer data ){
 -      if ( ConfirmModified( "Exit Radiant" ) ) {
 +      if ( ConfirmModified( "Exit " RADIANT_NAME ) ) {
                gtk_main_quit();
        }
  
        return TRUE;
  }
  
 +PanedState g_single_hpaned = { 0.75f, -1, };
 +PanedState g_single_vpaned = { 0.75f, -1, };
 +
  void MainFrame::Create(){
        ui::Window window = ui::Window( ui::window_type::TOP );
  
  #if GDEF_OS_WINDOWS
        if ( g_multimon_globals.m_bStartOnPrimMon ) {
                PositionWindowOnPrimaryScreen( g_layout_globals.m_position );
-               window_set_position( window, g_layout_globals.m_position );
        }
-       else
  #endif
-       if ( g_layout_globals.nState & GDK_WINDOW_STATE_MAXIMIZED ) {
-               gtk_window_maximize( window );
-               WindowPosition default_position( -1, -1, 640, 480 );
-               window_set_position( window, default_position );
-       }
-       else
-       {
 -      window_set_position( window, g_layout_globals.m_position );
 +              window_set_position( window, g_layout_globals.m_position );
-       }
  
        m_window = window;
  
        window.show();
  
 -      if ( CurrentStyle() == eRegular || CurrentStyle() == eRegularLeft ) {
 +      if ( CurrentStyle() == eRegular || CurrentStyle() == eRegularLeft )
 +      {
                {
                        ui::Widget hsplit = ui::HPaned(ui::New);
                        m_hSplit = hsplit;
                                m_vSplit2 = vsplit2;
  
                                if ( CurrentStyle() == eRegular ){
-                                       gtk_paned_add1( GTK_PANED( hsplit ), vsplit );
-                                       gtk_paned_add2( GTK_PANED( hsplit ), vsplit2 );
+                                       gtk_paned_pack1( GTK_PANED( hsplit ), vsplit, TRUE, TRUE );
+                                       gtk_paned_pack2( GTK_PANED( hsplit ), vsplit2, TRUE, TRUE );
                                }
                                else{
-                                       gtk_paned_add2( GTK_PANED( hsplit ), vsplit );
-                                       gtk_paned_add1( GTK_PANED( hsplit ), vsplit2 );
+                                       gtk_paned_pack2( GTK_PANED( hsplit ), vsplit, TRUE, TRUE );
+                                       gtk_paned_pack1( GTK_PANED( hsplit ), vsplit2, TRUE, TRUE );
                                }
  
                                // console
                                ui::Widget console_window = Console_constructWindow( window );
-                               gtk_paned_pack2( GTK_PANED( vsplit ), console_window, FALSE, TRUE );
+                               gtk_paned_pack2( GTK_PANED( vsplit ), console_window, TRUE, TRUE );
                                
                                // xy
                                m_pXYWnd = new XYWnd();
                                m_pXYWnd->SetViewType( XY );
                                ui::Widget xy_window = ui::Widget(create_framed_widget( m_pXYWnd->GetWidget( ) ));
-                               gtk_paned_add1( GTK_PANED( vsplit ), xy_window );
+                               gtk_paned_pack1( GTK_PANED( vsplit ), xy_window, TRUE, TRUE );
  
                                {
                                        // camera
                                        CamWnd_setParent( *m_pCamWnd, window );
                                        auto camera_window = create_framed_widget( CamWnd_getWidget( *m_pCamWnd ) );
  
-                                       gtk_paned_add1( GTK_PANED( vsplit2 ), camera_window  );
+                                       gtk_paned_pack1( GTK_PANED( vsplit2 ), GTK_WIDGET( camera_window ) , TRUE, TRUE);
  
                                        // textures
                                        auto texture_window = create_framed_widget( TextureBrowser_constructWindow( window ) );
  
-                                       gtk_paned_add2( GTK_PANED( vsplit2 ), texture_window  );
+                                       gtk_paned_pack2( GTK_PANED( vsplit2 ), GTK_WIDGET( texture_window ), TRUE, TRUE );
                                }
                        }
                }
-               gtk_paned_set_position( GTK_PANED( m_vSplit ), g_layout_globals.nXYHeight );
-               if ( CurrentStyle() == eRegular ) {
-                       gtk_paned_set_position( GTK_PANED( m_hSplit ), g_layout_globals.nXYWidth );
-               }
-               else
-               {
-                       gtk_paned_set_position( GTK_PANED( m_hSplit ), g_layout_globals.nCamWidth );
-               }
-               gtk_paned_set_position( GTK_PANED( m_vSplit2 ), g_layout_globals.nCamHeight );
        }
 -      else if ( CurrentStyle() == eFloating ) {
 +      else if ( CurrentStyle() == eFloating )
 +      {
                {
                        ui::Window window = ui::Window(create_persistent_floating_window( "Camera", m_window ));
                        global_accel_connect_window( window );
                                window.add(frame);
                        }
                        CamWnd_setParent( *m_pCamWnd, window );
 -#define GARUX_GTK_WORKAROUND
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", CamWnd_getWidget( *m_pCamWnd ) );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, CamWnd_getWidget( *m_pCamWnd ) );
  
                        g_floating_windows.push_back( window );
                }
                                window.add(frame);
                        }
                        XY_Top_Shown_Construct( window );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", m_pXYWnd->GetWidget() );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXYWnd->GetWidget() );
  
                        g_floating_windows.push_back( window );
                }
                        }
  
                        XZ_Front_Shown_Construct( window );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", m_pXZWnd->GetWidget() );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pXZWnd->GetWidget() );
  
                        g_floating_windows.push_back( window );
                }
                        }
  
                        YZ_Side_Shown_Construct( window );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( window ), "glwidget", m_pYZWnd->GetWidget() );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, m_pYZWnd->GetWidget() );
  
                        g_floating_windows.push_back( window );
                }
                {
                        auto frame = create_framed_widget( TextureBrowser_constructWindow( GroupDialog_getWindow() ) );
                        g_page_textures = GroupDialog_addPage( "Textures", frame, TextureBrowserExportTitleCaller() );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( GroupDialog_getWindow() ), "glwidget", TextureBrowser_getGLWidget() );
 -#endif
  
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( GroupDialog_getWindow(), TextureBrowser_getGLWidget() );
                }
  
                GroupDialog_show();
        }
 -      else // 4 way
 +      else if ( CurrentStyle() == eSplit )
        {
                m_pCamWnd = NewCamWnd();
                GlobalCamera_setCamWnd( *m_pCamWnd );
                {
              auto frame = create_framed_widget( TextureBrowser_constructWindow( GroupDialog_getWindow() ) );
                        g_page_textures = GroupDialog_addPage( "Textures", frame, TextureBrowserExportTitleCaller() );
 -#ifndef GARUX_GTK_WORKAROUND
 -                      /* workaround for gtk 2.24 issue: not displayed glwidget after toggle */
 -                      g_object_set_data( G_OBJECT( GroupDialog_getWindow() ), "glwidget", TextureBrowser_getGLWidget() );
 -#endif
 +
 +                      WORKAROUND_GOBJECT_SET_GLWIDGET( window, TextureBrowser_getGLWidget() );
                }
        }
 +      else // single window
 +      {
 +              m_pCamWnd = NewCamWnd();
 +              GlobalCamera_setCamWnd( *m_pCamWnd );
 +              CamWnd_setParent( *m_pCamWnd, window );
 +
 +              ui::Widget camera = CamWnd_getWidget( *m_pCamWnd );
 +
 +              m_pYZWnd = new XYWnd();
 +              m_pYZWnd->SetViewType( YZ );
 +
 +              ui::Widget yz = m_pYZWnd->GetWidget();
 +
 +              m_pXYWnd = new XYWnd();
 +              m_pXYWnd->SetViewType( XY );
 +
 +              ui::Widget xy = m_pXYWnd->GetWidget();
 +
 +              m_pXZWnd = new XYWnd();
 +              m_pXZWnd->SetViewType( XZ );
 +
 +              ui::Widget xz = m_pXZWnd->GetWidget();
 +
 +              ui::Widget hsplit = ui::HPaned(ui::New);
 +              vbox.pack_start( hsplit, TRUE, TRUE, 0 );
 +              hsplit.show();
 +
 +              ui::Widget split = create_split_views( camera, yz, xy, xz );
 +
 +              ui::Widget vsplit = ui::VPaned(ui::New);
 +              vsplit.show();
 +
 +              // textures
 +              ui::Widget texture_window = create_framed_widget( TextureBrowser_constructWindow( window ) );
 +
 +              // console
 +              ui::Widget console_window = create_framed_widget( Console_constructWindow( window ) );
 +
 +              gtk_paned_add1( GTK_PANED( hsplit ), split );
 +              gtk_paned_add2( GTK_PANED( hsplit ), vsplit );
 +
 +              gtk_paned_add1( GTK_PANED( vsplit ), texture_window  );
 +              gtk_paned_add2( GTK_PANED( vsplit ), console_window  );
 +
 +              hsplit.connect( "size_allocate", G_CALLBACK( hpaned_allocate ), &g_single_hpaned );
 +              hsplit.connect( "notify::position", G_CALLBACK( paned_position ), &g_single_hpaned );
 +
 +              vsplit.connect( "size_allocate", G_CALLBACK( vpaned_allocate ), &g_single_vpaned );
 +              vsplit.connect( "notify::position", G_CALLBACK( paned_position ), &g_single_vpaned );
 +      }
  
        EntityList_constructWindow( window );
        PreferencesDialog_constructWindow( window );
  
        EverySecondTimer_enable();
  
+       if ( g_layout_globals.nState & GDK_WINDOW_STATE_MAXIMIZED ) {
+               gtk_window_maximize( window );
+       }
+       if ( g_layout_globals.nState & GDK_WINDOW_STATE_FULLSCREEN ) {
+               gtk_window_fullscreen( window );
+       }
+       if ( !FloatingGroupDialog() ) {
+               gtk_paned_set_position( GTK_PANED( m_vSplit ), g_layout_globals.nXYHeight );
+               if ( CurrentStyle() == eRegular ) {
+                       gtk_paned_set_position( GTK_PANED( m_hSplit ), g_layout_globals.nXYWidth );
+               }
+               else
+               {
+                       gtk_paned_set_position( GTK_PANED( m_hSplit ), g_layout_globals.nCamWidth );
+               }
+               gtk_paned_set_position( GTK_PANED( m_vSplit2 ), g_layout_globals.nCamHeight );
+       }
        //GlobalShortcuts_reportUnregistered();
  }
  
@@@ -3397,7 -3270,9 +3396,9 @@@ void MainFrame::SaveWindowInfo()
                g_layout_globals.nCamHeight = gtk_paned_get_position( GTK_PANED( m_vSplit2 ) );
        }
  
 -              g_layout_globals.m_position = m_position_tracker.getPosition();
+       if( gdk_window_get_state( GTK_WIDGET( m_window )->window ) == 0 ){
 +      g_layout_globals.m_position = m_position_tracker.getPosition();
+       }
  
        g_layout_globals.nState = gdk_window_get_state( gtk_widget_get_window(m_window ) );
  }
@@@ -3448,7 -3323,7 +3449,7 @@@ void MainFrame::SetStatusText( CopiedSt
  }
  
  void Sys_Status( const char* status ){
 -      if ( g_pParentWnd != 0 ) {
 +      if ( g_pParentWnd != nullptr ) {
                g_pParentWnd->SetStatusText( g_pParentWnd->m_command_status, status );
        }
  }
@@@ -3480,7 -3355,7 +3481,7 @@@ void MainFrame::SetGridStatus()
  }
  
  void GridStatus_onTextureLockEnabledChanged(){
 -      if ( g_pParentWnd != 0 ) {
 +      if ( g_pParentWnd != nullptr ) {
                g_pParentWnd->SetGridStatus();
        }
  }
@@@ -3525,7 -3400,7 +3526,7 @@@ void GlobalGL_sharedContextDestroyed()
  
  void Layout_constructPreferences( PreferencesPage& page ){
        {
 -              const char* layouts[] = { "window1.png", "window2.png", "window3.png", "window4.png" };
 +              const char* layouts[] = { "window1.png", "window2.png", "window3.png", "window4.png", "window5.png" };
                page.appendRadioIcons(
                        "Window Layout",
                        STRING_ARRAY_RANGE( layouts ),
@@@ -3561,9 -3436,29 +3562,29 @@@ void Layout_registerPreferencesPage()
        PreferencesDialog_addInterfacePage( makeCallbackF(Layout_constructPage) );
  }
  
 -
+ void MainFrame_toggleFullscreen(){
+       GtkWindow* wnd = MainFrame_getWindow();
+       if( gdk_window_get_state( GTK_WIDGET( wnd )->window ) & GDK_WINDOW_STATE_FULLSCREEN ){
+               //some portion of buttsex, because gtk_window_unfullscreen doesn't work correctly after calling some modal window
+               bool maximize = ( gdk_window_get_state( GTK_WIDGET( wnd )->window ) & GDK_WINDOW_STATE_MAXIMIZED );
+               gtk_window_unfullscreen( wnd );
+               if( maximize ){
+                       gtk_window_unmaximize( wnd );
+                       gtk_window_maximize( wnd );
+               }
+               else{
+                       gtk_window_move( wnd, g_layout_globals.m_position.x, g_layout_globals.m_position.y );
+                       gtk_window_resize( wnd, g_layout_globals.m_position.w, g_layout_globals.m_position.h );
+               }
+       }
+       else{
+               gtk_window_fullscreen( wnd );
+       }
+ }
  #include "preferencesystem.h"
  #include "stringio.h"
 +#include "transformpath/transformpath.h"
  
  void MainFrame_Construct(){
        GlobalCommands_insert( "OpenManual", makeCallbackF(OpenHelpURL), Accelerator( GDK_KEY_F1 ) );
        GlobalCommands_insert( "ArbitraryScale", makeCallbackF(DoScaleDlg) );
  
        GlobalCommands_insert( "BuildMenuCustomize", makeCallbackF(DoBuildMenu) );
+       GlobalCommands_insert( "Build_runRecentExecutedBuild", makeCallbackF(Build_runRecentExecutedBuild), Accelerator( GDK_F5 ) );
  
        GlobalCommands_insert( "FindBrush", makeCallbackF(DoFind) );
  
        GlobalCommands_insert( "ColorSchemeQER", makeCallbackF(ColorScheme_QER) );
        GlobalCommands_insert( "ColorSchemeBlackAndGreen", makeCallbackF(ColorScheme_Black) );
        GlobalCommands_insert( "ColorSchemeYdnar", makeCallbackF(ColorScheme_Ydnar) );
 +      GlobalCommands_insert("ColorSchemeAdwaitaDark", makeCallbackF(ColorScheme_AdwaitaDark));
        GlobalCommands_insert( "ChooseTextureBackgroundColor", makeCallback( g_ColoursMenu.m_textureback ) );
        GlobalCommands_insert( "ChooseGridBackgroundColor", makeCallback( g_ColoursMenu.m_xyback ) );
        GlobalCommands_insert( "ChooseGridMajorColor", makeCallback( g_ColoursMenu.m_gridmajor ) );
        GlobalCommands_insert( "ChooseClipperColor", makeCallback( g_ColoursMenu.m_clipper ) );
        GlobalCommands_insert( "ChooseOrthoViewNameColor", makeCallback( g_ColoursMenu.m_viewname ) );
  
+       GlobalCommands_insert( "Fullscreen", makeCallbackF( MainFrame_toggleFullscreen ), Accelerator( GDK_F11 ) );
  
        GlobalCommands_insert( "CSGSubtract", makeCallbackF(CSG_Subtract), Accelerator( 'U', (GdkModifierType)GDK_SHIFT_MASK ) );
        GlobalCommands_insert( "CSGMerge", makeCallbackF(CSG_Merge), Accelerator( 'U', (GdkModifierType) GDK_CONTROL_MASK ) );
  #error "unknown platform"
  #endif
                ;
 +
                StringOutputStream path( 256 );
                path << DirectoryCleaned( g_pGameDescription->getRequiredKeyValue( ENGINEPATH_ATTRIBUTE ) );
 -              g_strEnginePath = path.c_str();
 +
 +              g_strEnginePath = transformPath( path.c_str() ).c_str();
                GlobalPreferenceSystem().registerPreference( "EnginePath", make_property_string( g_strEnginePath ) );
        }
  
        g_entityCount.setCountChangedCallback( makeCallbackF(QE_entityCountChanged) );
        GlobalEntityCreator().setCounter( &g_entityCount );
  
 -      GLWidget_sharedContextCreated = GlobalGL_sharedContextCreated;
 -      GLWidget_sharedContextDestroyed = GlobalGL_sharedContextDestroyed;
 +      glwidget_set_shared_context_constructors( GlobalGL_sharedContextCreated, GlobalGL_sharedContextDestroyed);
  
        GlobalEntityClassManager().attach( g_WorldspawnColourEntityClassObserver );
  }
@@@ -3782,61 -3678,3 +3806,61 @@@ void GLWindow_Construct()
  
  void GLWindow_Destroy(){
  }
 +
 +/* HACK: If ui::main is not called yet,
 +gtk_main_quit will not quit, so tell main
 +to not call ui::main. This happens when a
 +map is loaded from command line and require
 +a restart because of wrong format.
 +Delete this when the code to not have to
 +restart to load another format is merged. */
 +extern bool g_dontStart;
 +
 +void Radiant_Restart(){
 +      // preferences are expected to be already saved in any way
 +      // this is just to be sure and be future proof
 +      Preferences_Save();
 +
 +      // this asks user for saving if map is modified
 +      // user can chose to not save, it's ok
 +      ConfirmModified( "Restart " RADIANT_NAME );
 +
 +      int status;
 +
 +      char *argv[ 3 ];
 +      char exe_file[ 256 ];
 +      char map_file[ 256 ];
 +      bool with_map = false;
 +
 +      strncpy( exe_file, g_strAppFilePath.c_str(), 256 );
 +
 +      if ( !Map_Unnamed( g_map ) ) {
 +              strncpy( map_file, Map_Name( g_map ), 256 );
 +              with_map = true;
 +      }
 +
 +      argv[ 0 ] = exe_file;
 +      argv[ 1 ] = with_map ? map_file : NULL;
 +      argv[ 2 ] = NULL;
 +
 +#if GDEF_OS_WINDOWS
 +      status = !_spawnvpe( P_NOWAIT, exe_file, argv, environ );
 +#else
 +      pid_t pid;
 +
 +      status = posix_spawn( &pid, exe_file, NULL, NULL, argv, environ );
 +#endif
 +
 +      // quit if radiant successfully started
 +      if ( status == 0 ) {
 +              gtk_main_quit();
 +              /* HACK: If ui::main is not called yet,
 +              gtk_main_quit will not quit, so tell main
 +              to not call ui::main. This happens when a
 +              map is loaded from command line and require
 +              a restart because of wrong format.
 +              Delete this when the code to not have to
 +              restart to load another format is merged. */
 +              g_dontStart = true;
 +      }
 +}
diff --combined radiant/map.cpp
index e1e6a727bca7fad9cc8d51ffbc6fa9523edc3320,5f22997c5af01156d8e36732dea94119f60d9833..91699dbc7440a60371dc3f294d54e99e6bcadb69
@@@ -966,8 -966,6 +966,8 @@@ void Map_LoadFile( const char *filenam
        MRU_AddFile( filename );
        g_strLastMapFolder = g_path_get_dirname( filename );
  
 +      bool switch_format = false;
 +
        {
                ScopeTimer timer( "map load" );
  
                                if ( !format->wrongFormat ) {
                                        break;
                                }
 +                              switch_format = !switch_format;
                        }
                }
  
        Map_StartPosition();
  
        g_currentMap = &g_map;
 +
 +      Brush_switchFormat( switch_format );
  }
  
  class Excluder
@@@ -1583,55 -1578,54 +1583,55 @@@ tryDecompile
        const char *type = GlobalRadiant().getGameDescriptionKeyValue( "q3map2_type" );
        int n = string_length( path_get_extension( filename ) );
        if ( n && ( extension_equal( path_get_extension( filename ), "bsp" ) || extension_equal( path_get_extension( filename ), "map" ) ) ) {
 -              StringBuffer output;
 -              output.push_string( AppPath_get() );
 -              output.push_string( "q3map2." );
 -              output.push_string( RADIANT_EXECUTABLE );
 -              output.push_string( " -v -game " );
 -              output.push_string( ( type && *type ) ? type : "quake3" );
 -              output.push_string( " -fs_basepath \"" );
 -              output.push_string( EnginePath_get() );
 -              output.push_string( "\" -fs_homepath \"" );
 -              output.push_string( g_qeglobals.m_userEnginePath.c_str() );
 -              output.push_string( "\"" );
 +              std::string output;
 +              output += AppPath_get();
 +              output += "q3map2";
 +              output += GDEF_OS_EXE_EXT;
 +
 +              output += " -v -game ";
 +              output += ( type && *type ) ? type : "quake3";
 +              output += " -fs_basepath \"";
 +              output += EnginePath_get();
 +              output += "\" -fs_homepath \"";
 +              output += g_qeglobals.m_userEnginePath.c_str();
 +              output += "\"";
  
                // extra pakpaths
                for ( int i = 0; i < g_pakPathCount; i++ ) {
                        if ( g_strcmp0( g_strPakPath[i].c_str(), "") ) {
 -                              output.push_string( " -fs_pakpath \"" );
 -                              output.push_string( g_strPakPath[i].c_str() );
 -                              output.push_string( "\"" );
 +                              output += " -fs_pakpath \"";
 +                              output += g_strPakPath[i].c_str();
 +                              output += "\"";
                        }
                }
  
                // extra switches
                if ( g_disableEnginePath ) {
 -                      output.push_string( " -fs_nobasepath " );
 +                      output += " -fs_nobasepath ";
                }
  
                if ( g_disableHomePath ) {
 -                      output.push_string( " -fs_nohomepath " );
 +                      output += " -fs_nohomepath ";
                }
  
 -              output.push_string( " -fs_game " );
 -              output.push_string( gamename_get() );
 -              output.push_string( " -convert -format " );
 -              output.push_string( Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map" );
 +              output += " -fs_game ";
 +              output += gamename_get();
 +              output += " -convert -format ";
 +              output += Brush::m_type == eBrushTypeQuake3BP ? "map_bp" : "map";
                if ( extension_equal( path_get_extension( filename ), "map" ) ) {
 -                      output.push_string( " -readmap " );
 +                      output += " -readmap ";
                }
 -              output.push_string( " \"" );
 -              output.push_string( filename );
 -              output.push_string( "\"" );
 +              output += " \"";
 +              output += filename;
 +              output += "\"";
  
                // run
                Q_Exec( NULL, output.c_str(), NULL, false, true );
  
                // rebuild filename as "filenamewithoutext_converted.map"
 -              output.clear();
 -              output.push_range( filename, filename + string_length( filename ) - ( n + 1 ) );
 -              output.push_string( "_converted.map" );
 +              output = "";
 +              output.append( filename, string_length( filename ) - ( n + 1 ) );
 +              output += "_converted.map";
                filename = output.c_str();
  
                // open
@@@ -1949,9 -1943,7 +1949,7 @@@ void SaveMap()
        }
        else if ( Map_Modified( g_map ) ) {
                Map_Save();
- #ifdef WIN32
                MRU_AddFile( g_map.m_name.c_str() );    //add on saving, but not opening via cmd line: spoils the list
- #endif
        }
  }
  
diff --combined radiant/server.cpp
index 879770c5e341f169e656f41d30d2a15ee199b43d,679e56bbd626250ef74722d506d25c6b917d5553..993472ae6234f0a415c1d684eac09e5bab97890b
@@@ -144,11 -144,9 +144,11 @@@ FunctionPointer findSymbol( const char
  
  #include <dlfcn.h>
  
 +#if GDEF_OS_MACOS
  #ifndef RTLD_DEEPBIND
  #define RTLD_DEEPBIND 0
 -#endif
 +#endif // RTLD_DEEPBIND
 +#endif // GDEF_OS_MACOS
  
  class DynamicLibrary {
        void *m_library;
@@@ -156,7 -154,7 +156,7 @@@ public
  typedef int ( *FunctionPointer )();
  
  DynamicLibrary( const char* filename ){
-       m_library = dlopen( filename, RTLD_NOW );
+       m_library = dlopen( filename, RTLD_NOW | (RTLD_DEEPBIND + 0) );
  }
  ~DynamicLibrary(){
        if ( !failed() ) {
@@@ -178,9 -176,9 +178,9 @@@ FunctionPointer findSymbol( const char
  }
  };
  
 -#else
 +#else // !GDEF_OS_POSIX
  #error "unsupported platform"
 -#endif
 +#endif // !GDEF_OS_POSIX
  
  class DynamicLibraryModule
  {
index db04b8805e2faf08bc61ec8993df13e5c09964d8,7769a0c5cc4650483bb621f1e7bba07261787c01..d48089540546de331b1ccfe57f76223445415f8f
@@@ -94,11 -94,13 +94,12 @@@ brush_t *AllocBrush( int numSides )
        size_t c;
  
        /* allocate and clear */
-       if ( numSides <= 0 ) {
+       /*if ( numSides <= 0 ) {
                Error( "AllocBrush called with numsides = %d", numSides );
        }
-       c = (size_t)&( ( (brush_t*) 0 )->sides[ numSides ] );
+       c = (size_t)&( ( (brush_t*) 0 )->sides[ numSides ] );*/
+       c = sizeof(*bb) + (numSides > 6 ? sizeof(side_t)*(numSides - 6) : 0);
 -      bb = safe_malloc( c );
 -      memset( bb, 0, c );
 +      bb = safe_malloc0( c );
        if ( numthreads == 1 ) {
                numActiveBrushes++;
        }
@@@ -828,7 -830,8 +829,7 @@@ void FilterStructuralBrushesIntoTree( e
  tree_t *AllocTree( void ){
        tree_t  *tree;
  
 -      tree = safe_malloc( sizeof( *tree ) );
 -      memset( tree, 0, sizeof( *tree ) );
 +      tree = safe_malloc0( sizeof( *tree ) );
        ClearBounds( tree->mins, tree->maxs );
  
        return tree;
  node_t *AllocNode( void ){
        node_t  *node;
  
 -      node = safe_malloc( sizeof( *node ) );
 -      memset( node, 0, sizeof( *node ) );
 +      node = safe_malloc0( sizeof( *node ) );
  
        return node;
  }
index 7286b039a3391fe0ad648d5645fc52ea5a6b38f9,a751f83a5fd2e538ca2c6797ae800544ad3c7e5a..f49d93ee804314bdb456f237570185012f177dd5
@@@ -94,7 -94,9 +94,7 @@@ void SetDrawVerts( int n )
        numBSPDrawVerts = n;
        numBSPDrawVertsBuffer = numBSPDrawVerts;
  
 -      bspDrawVerts = safe_malloc_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
 -
 -      memset( bspDrawVerts, 0, n * sizeof( bspDrawVert_t ) );
 +      bspDrawVerts = safe_malloc0_info( sizeof( bspDrawVert_t ) * numBSPDrawVertsBuffer, "IncDrawVerts" );
  }
  
  int numBSPDrawSurfacesBuffer = 0;
@@@ -105,7 -107,9 +105,7 @@@ void SetDrawSurfacesBuffer()
  
        numBSPDrawSurfacesBuffer = MAX_MAP_DRAW_SURFS;
  
 -      bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
 -
 -      memset( bspDrawSurfaces, 0, MAX_MAP_DRAW_SURFS * sizeof( bspDrawSurface_t ) );
 +      bspDrawSurfaces = safe_malloc0_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
  }
  
  void SetDrawSurfaces( int n ){
        numBSPDrawSurfaces = n;
        numBSPDrawSurfacesBuffer = numBSPDrawSurfaces;
  
 -      bspDrawSurfaces = safe_malloc_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
 -
 -      memset( bspDrawSurfaces, 0, n * sizeof( bspDrawSurface_t ) );
 +      bspDrawSurfaces = safe_malloc0_info( sizeof( bspDrawSurface_t ) * numBSPDrawSurfacesBuffer, "IncDrawSurfaces" );
  }
  
  void BSPFilesCleanup(){
@@@ -344,16 -350,14 +344,16 @@@ int CopyLump_Allocate( bspHeader_t *hea
  void AddLump( FILE *file, bspHeader_t *header, int lumpNum, const void *data, int length ){
        bspLump_t   *lump;
  
 -
        /* add lump to bsp file header */
        lump = &header->lumps[ lumpNum ];
        lump->offset = LittleLong( ftell( file ) );
        lump->length = LittleLong( length );
  
        /* write lump to file */
 -      SafeWrite( file, data, ( length + 3 ) & ~3 );
 +      SafeWrite( file, data, length );
 +
 +      /* write padding zeros */
 +      SafeWrite( file, (const byte[3]){ 0, 0, 0 }, ( ( length + 3 ) & ~3 ) - length );
  }
  
  
@@@ -531,7 -535,8 +531,7 @@@ epair_t *ParseEPair( void )
  
  
        /* allocate and clear new epair */
 -      e = safe_malloc( sizeof( epair_t ) );
 -      memset( e, 0, sizeof( epair_t ) );
 +      e = safe_malloc0( sizeof( epair_t ) );
  
        /* handle key */
        if ( strlen( token ) >= ( MAX_KEY - 1 ) ) {
@@@ -627,10 -632,9 +627,9 @@@ void InjectCommandLine( char **argv, in
        char *sentinel = newCommandLine + sizeof( newCommandLine ) - 1;
        int i;
  
- if (nocmdline)
- {
-       return;
- }
+       if ( nocmdline ){
+               return;
+       }
        previousCommandLine = ValueForKey( &entities[0], "_q3map2_cmdline" );
        if ( previousCommandLine && *previousCommandLine ) {
                inpos = previousCommandLine;
  
        for ( i = beginArgs; i < endArgs; ++i )
        {
 +              if ( argv[i] == NULL ) {
 +                      continue;
 +              }
                if ( outpos != sentinel && i != beginArgs ) {
                        *outpos++ = ' ';
                }
index b0e6fa1969bd0d7bc248552c2acaba8bfb05bdd0,139a242a0ca5fafde3bec1f9aa298b009c04e352..3b87ee9f4b0c3a11b052e40ddbe92b48f9836968
@@@ -1007,8 -1007,14 +1007,14 @@@ skipEXfile
                                Sys_Printf( "++%s\n", temp );
                                continue;
                        }
-                       Sys_Printf( "  !FAIL! %s\n", pk3Shaders + i*65 );
-                       if ( i != pk3ShadersN - 1 ) packFAIL = qtrue; //levelshot typically
+                       if ( i == pk3ShadersN - 1 ){ //levelshot typically
+                               Sys_Printf( "  ~fail  %s\n", pk3Shaders + i*65 );
+                       }
+                       else{
+                               Sys_Printf( "  !FAIL! %s\n", pk3Shaders + i*65 );
+                               packFAIL = qtrue;
+                       }
                }
        }
  
                        Sys_Printf( "++%s\n", temp );
                }
        else{
-               Sys_Printf( "  !FAIL! %s\n", temp );
+               Sys_Printf( "  ~fail  %s\n", temp );
        }
  
        sprintf( temp, "scripts/%s.arena", nameOFmap );
                        Sys_Printf( "++%s\n", temp );
                }
        else{
-               Sys_Printf( "  !FAIL! %s\n", temp );
+               Sys_Printf( "  ~fail  %s\n", temp );
        }
  
        sprintf( temp, "scripts/%s.defi", nameOFmap );
                        Sys_Printf( "++%s\n", temp );
                }
        else{
-               Sys_Printf( "  !FAIL! %s\n", temp );
+               Sys_Printf( "  ~fail  %s\n", temp );
        }
  
        if ( !packFAIL ){
@@@ -2097,16 -2103,14 +2103,16 @@@ int main( int argc, char **argv )
                /* -help */
                if ( !strcmp( argv[ i ], "-h" ) || !strcmp( argv[ i ], "--help" )
                        || !strcmp( argv[ i ], "-help" ) ) {
 -                      HelpMain(argv[i+1]);
 +                      HelpMain( ( i + 1 < argc ) ? argv[ i + 1 ] : NULL );
                        return 0;
                }
  
                /* -connect */
                if ( !strcmp( argv[ i ], "-connect" ) ) {
 -                      argv[ i ] = NULL;
 -                      i++;
 +                      if ( ++i >= argc || !argv[ i ] ) {
 +                              Error( "Out of arguments: No address specified after %s", argv[ i - 1 ] );
 +                      }
 +                      argv[ i - 1 ] = NULL;
                        Broadcast_Setup( argv[ i ] );
                        argv[ i ] = NULL;
                }
  
                /* patch subdivisions */
                else if ( !strcmp( argv[ i ], "-subdivisions" ) ) {
 -                      argv[ i ] = NULL;
 -                      i++;
 +                      if ( ++i >= argc || !argv[ i ] ) {
 +                              Error( "Out of arguments: No value specified after %s", argv[ i - 1 ] );
 +                      }
 +                      argv[ i - 1 ] = NULL;
                        patchSubdivisions = atoi( argv[ i ] );
                        argv[ i ] = NULL;
                        if ( patchSubdivisions <= 0 ) {
  
                /* threads */
                else if ( !strcmp( argv[ i ], "-threads" ) ) {
 -                      argv[ i ] = NULL;
 -                      i++;
 +                      if ( ++i >= argc || !argv[ i ] ) {
 +                              Error( "Out of arguments: No value specified after %s", argv[ i - 1 ] );
 +                      }
 +                      argv[ i - 1 ] = NULL;
                        numthreads = atoi( argv[ i ] );
                        argv[ i ] = NULL;
                }
  
        Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
        Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
 -      Sys_Printf( "NetRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
 +      Sys_Printf( RADIANT_NAME "    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
        Sys_Printf( "%s\n", Q3MAP_MOTD );
        Sys_Printf( "%s\n", argv[0] );
  
  
        /* ydnar: otherwise create a bsp */
        else{
 +              /* used to write Smokin'Guns like tex file */
 +              compile_map = qtrue;
 +
                r = BSPMain( argc, argv );
        }
  
index 1a7dd34e205e1a30b6679b46c6f24a11cc34cc81,f6296f1b72aae3412f8dcd4c90c175d1503195a8..720dec29053fc042f0541d74295b7e0cc0d053cf
     ------------------------------------------------------------------------------- */
  
  /* platform-specific */
 -#if GDEF_OS_LINUX || GDEF_OS_MACOS
 -      #define Q_UNIX
 -#endif
 -
 -#ifdef Q_UNIX
 +#if GDEF_OS_POSIX
        #include <unistd.h>
        #include <pwd.h>
        #include <limits.h>
@@@ -62,6 -66,8 +62,6 @@@
  
  
  /* general */
 -#include "version.h"            /* ttimo: might want to guard that if built outside of the GtkRadiant tree */
 -
  #include "cmdlib.h"
  #include "mathlib.h"
  #include "md5lib.h"
  
     ------------------------------------------------------------------------------- */
  
 -#define MAC_STATIC_HACK         0
 -#if GDEF_OS_MACOS && MAC_STATIC_HACK
 -      #define MAC_STATIC          static
 -#else
 -      #define MAC_STATIC
 -#endif
 -
 -#if 1
        #if GDEF_OS_WINDOWS
                #define Q_stricmp           stricmp
                #define Q_strncasecmp       strnicmp
                #define Q_stricmp           strcasecmp
                #define Q_strncasecmp       strncasecmp
        #endif
 +
 +// hack to declare and define in the same file
 +#ifdef MAIN_C
 +      #define Q_EXTERN
 +      #define Q_ASSIGN( a )   = a
 +#else
 +      #define Q_EXTERN extern
 +      #define Q_ASSIGN( a )
  #endif
  
  /* macro version */
  /* general */
  #define MAX_QPATH               64
  
 -#define MAX_IMAGES              512
 +#define MAX_IMAGES              2048
  #define DEFAULT_IMAGE           "*default"
  
 -#define MAX_MODELS              512
 +#define MAX_MODELS              2048
  
  #define DEF_BACKSPLASH_FRACTION 0.05f   /* 5% backsplash by default */
  #define DEF_BACKSPLASH_DISTANCE 23
  #define C_DETAIL                0x08000000  /* THIS MUST BE THE SAME AS IN RADIANT! */
  
  
 +/* new tex surface flags, like Smokin'Guns */
 +#define TEX_SURF_METAL             0x00001000
 +#define TEX_SURF_WOOD              0x00080000
 +#define TEX_SURF_CLOTH             0x00100000
 +#define TEX_SURF_DIRT              0x00200000
 +#define TEX_SURF_GLASS             0x00400000
 +#define TEX_SURF_PLANT             0x00800000
 +#define TEX_SURF_SAND              0x01000000
 +#define TEX_SURF_SNOW              0x02000000
 +#define TEX_SURF_STONE             0x04000000
 +#define TEX_SURF_WATER             0x08000000
 +#define TEX_SURF_GRASS             0x10000000
 +#define TEX_SURF_BREAKABLE         0x20000000
 +
 +
  /* shadow flags */
  #define WORLDSPAWN_CAST_SHADOWS 1
  #define WORLDSPAWN_RECV_SHADOWS 1
  #define HINT_PRIORITY           1000        /* ydnar: force hint splits first and antiportal/areaportal splits last */
  #define ANTIPORTAL_PRIORITY     -1000
  #define AREAPORTAL_PRIORITY     -1000
- #define DETAIL_PRIORITY         -3000
+ #define DETAIL_PRIORITY     -3000
  
  #define PSIDE_FRONT             1
  #define PSIDE_BACK              2
  #define RAD_LUXEL_SIZE          3
  #define SUPER_LUXEL_SIZE        4
  #define SUPER_FLAG_SIZE         4
- #define FLAG_FORCE_SUBSAMPLING  1
+ #define FLAG_FORCE_SUBSAMPLING 1
  #define FLAG_ALREADY_SUBSAMPLED 2
  #define SUPER_ORIGIN_SIZE       3
  #define SUPER_NORMAL_SIZE       4
@@@ -560,13 -551,6 +560,13 @@@ typedef enu
  }
  miniMapMode_t;
  
 +typedef enum
 +{
 +      MINIMAP_SIDECAR_NONE,
 +      MINIMAP_SIDECAR_UNVANQUISHED
 +}
 +miniMapSidecarFormat_t;
 +
  typedef struct game_s
  {
        char                *arg;                           /* -game matches this */
        int maxLMSurfaceVerts;                              /* default maximum meta surface verts */
        int maxSurfaceVerts;                                /* default maximum surface verts */
        int maxSurfaceIndexes;                              /* default maximum surface indexes (tris * 3) */
 +      qboolean texFile;                                   /* enable per shader prefix surface flags and .tex file */
        qboolean emitFlares;                                /* when true, emit flare surfaces */
        char                *flareShader;                   /* default flare shader (MUST BE SET) */
        qboolean wolfLight;                                 /* when true, lights work like wolf q3map  */
        qboolean miniMapKeepAspect;                         /* minimap keep aspect ratio by letterboxing */
        miniMapMode_t miniMapMode;                          /* minimap mode */
        char                *miniMapNameFormat;             /* minimap name format */
 +      miniMapSidecarFormat_t miniMapSidecarFormat;        /* minimap sidecar format */
        char                *bspIdent;                      /* 4-letter bsp file prefix */
        int bspVersion;                                     /* bsp version to use */
        qboolean lumpSwap;                                  /* cod-style len/ofs order */
@@@ -848,7 -830,7 +848,7 @@@ typedef struct face_
        struct face_s       *next;
        int planenum;
        int priority;
-       //qboolean checked;
+       //qboolean                      checked;
        int compileFlags;
        winding_t           *w;
  }
@@@ -1024,7 -1006,7 +1024,7 @@@ typedef enu
  }
  surfaceType_t;
  
 -char            *surfaceTypes[ NUM_SURFACE_TYPES ]
 +Q_EXTERN char *surfaceTypes[ NUM_SURFACE_TYPES ]
  #ifndef MAIN_C
  ;
  #else
@@@ -1490,7 -1472,7 +1490,7 @@@ typedef struct rawLightmap_
        float                   *bspLuxels[ MAX_LIGHTMAPS ];
        float                   *radLuxels[ MAX_LIGHTMAPS ];
        float                   *superLuxels[ MAX_LIGHTMAPS ];
-       unsigned char           *superFlags;
+       unsigned char               *superFlags;
        float                   *superOrigins;
        float                   *superNormals;
        int                     *superClusters;
@@@ -1838,7 -1820,6 +1838,7 @@@ void                        RadFreeLigh
  
  /* light_ydnar.c */
  void                        ColorToBytes( const float *color, byte *colorBytes, float scale );
 +void                        ColorToBytesNonZero( const float *color, byte *colorBytes, float scale );
  void                        SmoothNormals( void );
  
  void                        MapRawLightmap( int num );
@@@ -1879,7 -1860,7 +1879,7 @@@ int                         ImportLight
  
  void                        SetupSurfaceLightmaps( void );
  void                        StitchSurfaceLightmaps( void );
 -void                        StoreSurfaceLightmaps( qboolean fastAllocate );
 +void                        StoreSurfaceLightmaps( qboolean fastAllocate, qboolean storeForReal );
  
  
  /* exportents.c */
@@@ -1936,11 -1917,6 +1936,11 @@@ void                        PartialLoad
  void                        WriteBSPFile( const char *filename );
  void                        PrintBSPFileSizes( void );
  
 +void                        WriteTexFile( char *name );
 +void                        LoadSurfaceFlags( char *filename );
 +int                         GetSurfaceParm( const char *tex );
 +void                        RestoreSurfaceFlags( char *filename );
 +
  epair_t                     *ParseEPair( void );
  void                        ParseEntities( void );
  void                        UnparseEntities( void );
@@@ -1976,6 -1952,14 +1976,6 @@@ void                        WriteRBSPFi
  
     ------------------------------------------------------------------------------- */
  
 -#ifdef MAIN_C
 -      #define Q_EXTERN
 -      #define Q_ASSIGN( a )   = a
 -#else
 -      #define Q_EXTERN extern
 -      #define Q_ASSIGN( a )
 -#endif
 -
  /* game support */
  Q_EXTERN game_t games[]
  #ifndef MAIN_C
        =
        {
                                                                #include "game_quake3.h"
 +      ,
 +                                                              #include "game_nexuiz.h" /* must be after game_quake3.h as they share defines! */
 +      ,
 +                                                              #include "game_oa.h" /* must be after game_quake3.h as they share defines! */
 +      ,
 +                                                              #include "game_q3rally.h" /* must be after game_quake3.h as they share defines! */
        ,
                                                                #include "game_quakelive.h" /* must be after game_quake3.h as they share defines! */
        ,
 -                                                              #include "game_nexuiz.h" /* must be after game_quake3.h as they share defines! */
 +                                                              #include "game_reaction.h" /* must be after game_quake3.h */
        ,
 -                                                              #include "game_xonotic.h" /* must be after game_quake3.h as they share defines! */
 +                                                              #include "game_smokinguns.h" /* must be after game_quake3.h */
        ,
                                                                #include "game_tremulous.h" /*LinuxManMikeC: must be after game_quake3.h, depends on #define's set in it */
        ,
                                                                #include "game_unvanquished.h" /* must be after game_tremulous.h as they share defines! */
 +      ,
 +                                                              #include "game_wop.h" /* must be after game_quake3.h as they share defines! */
 +      ,
 +                                                              #include "game_xonotic.h" /* must be after game_quake3.h as they share defines! */
        ,
                                                                #include "game_tenebrae.h"
        ,
        ,
                                                                #include "game_qfusion.h"   /* qfusion game */
        ,
 -                                                              #include "game_reaction.h" /* must be after game_quake3.h */
 +                                                              #include "game_warsow.h" /* must be after game_qfusion.h as they share defines! */
 +      ,
 +                                                              #include "game_warfork.h" /* must be after game_qfusion.h as they share defines! */
        ,
 -                                                              #include "game_darkplaces.h"    /* vortex: darkplaces q1 engine */
 +                                                              #include "game_darkplaces.h" /* darkplaces q1 engine */
        ,
 -                                                              #include "game_dq.h"    /* vortex: deluxe quake game ( darkplaces q1 engine) */
 +                                                              #include "game_dq.h" /* deluxe quake game ( darkplaces q1 engine) */
        ,
                                                                #include "game_prophecy.h"  /* vortex: prophecy game ( darkplaces q1 engine) */
        ,
@@@ -2060,11 -2032,11 +2060,10 @@@ Q_EXTERN qboolean warnImage Q_ASSIGN( q
  /* ydnar: sinusoid samples */
  Q_EXTERN float jitters[ MAX_JITTERS ];
  
 -/* can't code */
 +/*can't code*/
  Q_EXTERN qboolean doingBSP Q_ASSIGN( qfalse );
  
  /* commandline arguments */
- Q_EXTERN qboolean nocmdline Q_ASSIGN( qfalse );
 -Q_EXTERN qboolean verbose;
  Q_EXTERN qboolean verboseEntities Q_ASSIGN( qfalse );
  Q_EXTERN qboolean force Q_ASSIGN( qfalse );
  Q_EXTERN qboolean infoMode Q_ASSIGN( qfalse );
@@@ -2083,9 -2055,10 +2082,10 @@@ Q_EXTERN qboolean nofog Q_ASSIGN( qfals
  Q_EXTERN qboolean noHint Q_ASSIGN( qfalse );                        /* ydnar */
  Q_EXTERN qboolean renameModelShaders Q_ASSIGN( qfalse );            /* ydnar */
  Q_EXTERN qboolean skyFixHack Q_ASSIGN( qfalse );                    /* ydnar */
- Q_EXTERN qboolean bspAlternateSplitWeights Q_ASSIGN( qfalse );      /* 27 */
- Q_EXTERN qboolean deepBSP Q_ASSIGN( qfalse );                       /* div0 */
+ Q_EXTERN qboolean bspAlternateSplitWeights Q_ASSIGN( qfalse );                      /* 27 */
+ Q_EXTERN qboolean deepBSP Q_ASSIGN( qfalse );                   /* div0 */
  Q_EXTERN qboolean maxAreaFaceSurface Q_ASSIGN( qfalse );                    /* divVerent */
+ Q_EXTERN qboolean nocmdline Q_ASSIGN( qfalse );
  
  Q_EXTERN int patchSubdivisions Q_ASSIGN( 8 );                       /* ydnar: -patchmeta subdivisions */
  
@@@ -2581,7 -2554,7 +2581,7 @@@ Q_EXTERN int allocatedBSPBrushSides Q_A
  Q_EXTERN bspBrushSide_t*    bspBrushSides Q_ASSIGN( NULL );
  
  Q_EXTERN int numBSPLightBytes Q_ASSIGN( 0 );
- Q_EXTERN byte *bspLightBytes Q_ASSIGN( NULL );
+ Q_EXTERN byte               *bspLightBytes Q_ASSIGN( NULL );
  
  //%   Q_EXTERN int                            numBSPGridPoints Q_ASSIGN( 0 );
  //%   Q_EXTERN byte                           *bspGridPoints Q_ASSIGN( NULL );
@@@ -2593,11 -2566,11 +2593,11 @@@ Q_EXTERN int numBSPVisBytes Q_ASSIGN( 
  Q_EXTERN byte bspVisBytes[ MAX_MAP_VISIBILITY ];
  
  Q_EXTERN int numBSPDrawVerts Q_ASSIGN( 0 );
- Q_EXTERN bspDrawVert_t *bspDrawVerts Q_ASSIGN( NULL );
+ Q_EXTERN bspDrawVert_t          *bspDrawVerts Q_ASSIGN( NULL );
  
  Q_EXTERN int numBSPDrawIndexes Q_ASSIGN( 0 );
  Q_EXTERN int allocatedBSPDrawIndexes Q_ASSIGN( 0 );
- Q_EXTERN int *bspDrawIndexes Q_ASSIGN( NULL );
+ Q_EXTERN int                *bspDrawIndexes Q_ASSIGN( NULL );
  
  Q_EXTERN int numBSPDrawSurfaces Q_ASSIGN( 0 );
  Q_EXTERN bspDrawSurface_t   *bspDrawSurfaces Q_ASSIGN( NULL );
@@@ -2608,46 -2581,28 +2608,46 @@@ Q_EXTERN bspFog_t bspFogs[ MAX_MAP_FOG
  Q_EXTERN int numBSPAds Q_ASSIGN( 0 );
  Q_EXTERN bspAdvertisement_t bspAds[ MAX_MAP_ADVERTISEMENTS ];
  
 -#define AUTOEXPAND_BY_REALLOC( ptr, reqitem, allocated, def ) \
 +// Used for tex file support, Smokin'Guns globals
 +Q_EXTERN qboolean compile_map;
 +
 +#define _AUTOEXPAND_BY_REALLOC( ptr, reqitem, allocated, def, fillWithZeros ) \
        do \
        { \
 +              int prevAllocated = allocated; \
                if ( reqitem >= allocated )     \
                { \
                        if ( allocated == 0 ) { \
 -                              allocated = def; } \
 +                              allocated = def; \
 +                      } \
                        while ( reqitem >= allocated && allocated )     \
 +                      { \
                                allocated *= 2; \
 +                      } \
                        if ( !allocated || allocated > 2147483647 / (int)sizeof( *ptr ) ) \
                        { \
-                               Error( #ptr " over 2 GB" ); \
+                               Error( # ptr " over 2 GB" ); \
                        } \
                        ptr = realloc( ptr, sizeof( *ptr ) * allocated ); \
                        if ( !ptr ) { \
 -                              Error( # ptr " out of memory" ); } \
 +                              Error( #ptr " out of memory" ); \
 +                      } \
 +                      if ( fillWithZeros ) \
 +                      { \
 +                              memset( ptr + ( sizeof( *ptr ) * prevAllocated ), 0 , sizeof( *ptr ) * ( allocated - prevAllocated ) ); \
 +                      } \
                } \
        } \
        while ( 0 )
  
- #define AUTOEXPAND_BY_REALLOC_BSP( suffix, def ) AUTOEXPAND_BY_REALLOC( bsp##suffix, numBSP##suffix, allocatedBSP##suffix, def )
 +#define AUTOEXPAND_BY_REALLOC( ptr, reqitem, allocated, def ) _AUTOEXPAND_BY_REALLOC( ptr, reqitem, allocated, def, qfalse )
 +
 +#define AUTOEXPAND_BY_REALLOC0( ptr, reqitem, allocated, def ) _AUTOEXPAND_BY_REALLOC( ptr, reqitem, allocated, def, qtrue )
 +
+ #define AUTOEXPAND_BY_REALLOC_BSP( suffix, def ) AUTOEXPAND_BY_REALLOC( bsp ## suffix, numBSP ## suffix, allocatedBSP ## suffix, def )
  
 +#define AUTOEXPAND_BY_REALLOC0_BSP( suffix, def ) AUTOEXPAND_BY_REALLOC0( bsp##suffix, numBSP##suffix, allocatedBSP##suffix, def )
 +
  #define Image_LinearFloatFromsRGBFloat( c ) ( ( ( c ) <= 0.04045f ) ? ( c ) * ( 1.0f / 12.92f ) : (float)pow( ( ( c ) + 0.055f ) * ( 1.0f / 1.055f ), 2.4f ) )
  #define Image_sRGBFloatFromLinearFloat( c ) ( ( ( c ) < 0.0031308f ) ? ( c ) * 12.92f : 1.055f * (float)pow( ( c ), 1.0f / 2.4f ) - 0.055f )