diff --git a/CMakeLists.txt b/CMakeLists.txt
index ba6cd2d9..63f1ace6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -198,7 +198,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
     add_definitions(-DNOEXCEPT= -DHAS_MSVC_EXCEPTION_BUG)
 
     # Needed for Debug information (it's set to "No" by default for some reason)
-    set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
+    set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS} /DEBUG /NODEFAULTLIB:MSVCRTD /NODEFAULTLIB:LIBCMT")
     set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
 else()
     message(FATAL_ERROR "Your C++ compiler doesn't seem to be supported.")
@@ -230,7 +230,10 @@ option(DEV_BUILD "Enable development build (enables some debugging tools, local
 # PLEASE DO NOT USE ON UNOFFICIAL BUILDS. Thanks.
 option(OFFICIAL_COLOBOT_BUILD "Official build (changes crash screen text)" OFF)
 
-# Portable build - load all data from current directory
+# Hardcode relative paths instead of absolute paths
+option(USE_RELATIVE_PATHS "Generate relative paths from absolute paths" OFF)
+
+# Portable build - load all data from the base directory
 option(PORTABLE "Portable build" OFF)
 
 # Portable saves - suitable for e.g. putting the whole game on external storage and moving your saves with it
@@ -391,12 +394,12 @@ endif()
 
 # Installation paths defined before compiling sources
 if(PORTABLE OR (PLATFORM_WINDOWS AND MXE))
-    # We need to use STRING because PATH doesn't accept relative paths
-    set(COLOBOT_INSTALL_BIN_DIR ./ CACHE STRING "Colobot binary directory")
-    set(COLOBOT_INSTALL_LIB_DIR ./ CACHE STRING "Colobot libraries directory")
-    set(COLOBOT_INSTALL_DATA_DIR ./data CACHE STRING "Colobot shared data directory")
-    set(COLOBOT_INSTALL_I18N_DIR ./lang CACHE STRING "Colobot translations directory")
-    set(COLOBOT_INSTALL_DOC_DIR ./doc CACHE STRING "Colobot documentation directory")
+    set(COLOBOT_INSTALL_BIN_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot binary directory")
+    set(COLOBOT_INSTALL_LIB_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot libraries directory")
+    set(COLOBOT_INSTALL_DATA_DIR ${CMAKE_INSTALL_PREFIX}/data CACHE PATH "Colobot shared data directory")
+    set(COLOBOT_INSTALL_I18N_DIR ${CMAKE_INSTALL_PREFIX}/lang CACHE PATH "Colobot translations directory")
+    set(COLOBOT_INSTALL_DOC_DIR ${CMAKE_INSTALL_PREFIX}/doc CACHE PATH "Colobot documentation directory")
+    set(USE_RELATIVE_PATHS ON)
 elseif(PLATFORM_WINDOWS)
     set(COLOBOT_INSTALL_BIN_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot binary directory")
     set(COLOBOT_INSTALL_LIB_DIR ${CMAKE_INSTALL_PREFIX}/ CACHE PATH "Colobot libraries directory")
@@ -417,6 +420,18 @@ else()
     set(COLOBOT_INSTALL_DOC_DIR ${CMAKE_INSTALL_PREFIX}/share/doc/colobot CACHE PATH "Colobot documentation directory")
 endif()
 
+# Generate relative paths from absolute paths
+if(USE_RELATIVE_PATHS)
+    message(STATUS "Generating relative paths")
+    file(RELATIVE_PATH COLOBOT_DATA_DIR ${COLOBOT_INSTALL_BIN_DIR} ${COLOBOT_INSTALL_DATA_DIR})
+    file(RELATIVE_PATH COLOBOT_I18N_DIR ${COLOBOT_INSTALL_BIN_DIR} ${COLOBOT_INSTALL_I18N_DIR})
+
+    add_definitions(-DUSE_RELATIVE_PATHS)
+else()
+    set(COLOBOT_DATA_DIR ${COLOBOT_INSTALL_DATA_DIR})
+    set(COLOBOT_I18N_DIR ${COLOBOT_INSTALL_I18N_DIR})
+endif()
+
 # Subdirectory with sources
 add_subdirectory(src)
 
diff --git a/Jenkinsfile b/Jenkinsfile
index 47dbd3c2..35f66489 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -27,6 +27,7 @@ pipeline {
                         dir('build/windows') {
                             sh '''
                                 # FIXME: without -lsetupapi linking sdl2 fails
+                                rm -rf *
                                 /opt/mxe/usr/bin/i686-w64-mingw32.static-cmake \
                                     -DCMAKE_CXX_STANDARD_LIBRARIES="-lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -lsetupapi" \
                                     -DCMAKE_INSTALL_PREFIX=/install \
@@ -53,6 +54,7 @@ pipeline {
                         sh 'mkdir -p build/linux'
                         dir('build/linux') {
                             sh '''
+                                rm -rf *
                                 cmake \
                                     -DCMAKE_INSTALL_PREFIX=/install -DCMAKE_SKIP_INSTALL_RPATH=ON \
                                     -DCMAKE_BUILD_TYPE=RelWithDebInfo -DDEV_BUILD=1 -DPORTABLE=1 -DTOOLS=1 -DTESTS=1 -DDESKTOP=1 ../..
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 506046c1..fd2e2c3d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -65,6 +65,8 @@ elseif(PLATFORM_WINDOWS)
             find_library(TIFF_LIBRARY NAMES tiff.lib)
             find_library(LZMA_LIBRARY NAMES lzma.lib)
             find_library(FREETYPE_LIBRARY NAMES freetype.lib)
+            find_library(ICONV_LIBRARY NAMES libiconv.lib)
+            find_library(CHARSET_LIBRARY NAMES libcharset.lib)
             set(MSVC_LIBS
                 ${LIBINTL_LIBRARY}
                 ${OPENAL_MSVC_LIBS}
@@ -73,6 +75,8 @@ elseif(PLATFORM_WINDOWS)
                 ${BZ2_LIBRARY}
                 ${LZMA_LIBRARY}
                 ${FREETYPE_LIBRARY}
+                ${ICONV_LIBRARY}
+                ${CHARSET_LIBRARY}
                 winmm.lib
                 dxguid.lib
                 imm32.lib
@@ -81,6 +85,7 @@ elseif(PLATFORM_WINDOWS)
                 version.lib
                 wsock32.lib
                 ws2_32.lib
+                setupapi.lib
             )
         else(${MSVC_STATIC})
             set(MSVC_LIBS ${LIBINTL_LIBRARY})
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 6f864572..94b6319e 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -220,6 +220,30 @@ CSoundInterface* CApplication::GetSound()
     return m_sound.get();
 }
 
+void CApplication::LoadEnvironmentVariables()
+{
+    auto dataDir = m_systemUtils->GetEnvVar("COLOBOT_DATA_DIR");
+    if (!dataDir.empty())
+    {
+        m_pathManager->SetDataPath(dataDir);
+        GetLogger()->Info("Using data dir (based on environment variable): '%s'\n", dataDir.c_str());
+    }
+
+    auto langDir = m_systemUtils->GetEnvVar("COLOBOT_LANG_DIR");
+    if (!langDir.empty())
+    {
+        m_pathManager->SetLangPath(langDir);
+        GetLogger()->Info("Using lang dir (based on environment variable): '%s'\n", langDir.c_str());
+    }
+
+    auto saveDir = m_systemUtils->GetEnvVar("COLOBOT_SAVE_DIR");
+    if (!saveDir.empty())
+    {
+        m_pathManager->SetSavePath(saveDir);
+        GetLogger()->Info("Using save dir (based on environment variable): '%s'\n", saveDir.c_str());
+    }
+}
+
 ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
 {
     enum OptionType
@@ -286,15 +310,18 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
                 GetLogger()->Message("\n");
                 GetLogger()->Message("%s\n", COLOBOT_FULLNAME);
                 GetLogger()->Message("\n");
-                GetLogger()->Message("List of available options:\n");
+                GetLogger()->Message("List of available options and environment variables:\n");
                 GetLogger()->Message("  -help               this help\n");
                 GetLogger()->Message("  -debug modes        enable debug modes (more info printed in logs; see code for reference of modes)\n");
                 GetLogger()->Message("  -runscene sceneNNN  run given scene on start\n");
                 GetLogger()->Message("  -scenetest          win every mission right after it's loaded\n");
                 GetLogger()->Message("  -loglevel level     set log level to level (one of: trace, debug, info, warn, error, none)\n");
                 GetLogger()->Message("  -langdir path       set custom language directory path\n");
+                GetLogger()->Message("                      environment variable: COLOBOT_LANG_DIR\n");
                 GetLogger()->Message("  -datadir path       set custom data directory path\n");
+                GetLogger()->Message("                      environment variable: COLOBOT_DATA_DIR\n");
                 GetLogger()->Message("  -savedir path       set custom save directory path (must be writable)\n");
+                GetLogger()->Message("                      environment variable: COLOBOT_SAVE_DIR\n");
                 GetLogger()->Message("  -mod path           load datadir mod from given path\n");
                 GetLogger()->Message("  -resolution WxH     set resolution\n");
                 GetLogger()->Message("  -headless           headless mode - disables graphics, sound and user interaction\n");
diff --git a/src/app/app.h b/src/app/app.h
index ccae3a5c..0c7f6388 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -164,7 +164,9 @@ public:
     CSoundInterface* GetSound();
 
 public:
-    //! Parses commandline arguments
+    //! Loads some data from environment variables
+    void LoadEnvironmentVariables();
+    //! Parses commandline arguments (they take priority)
     ParseArgsStatus ParseArguments(int argc, char *argv[]);
     //! Initializes the application
     bool        Create();
diff --git a/src/app/main.cpp b/src/app/main.cpp
index 46b0b88f..8fdf1653 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -172,6 +172,8 @@ int main(int argc, char *argv[])
     int code = 0;
     CApplication app(systemUtils.get()); // single instance of the application
 
+    app.LoadEnvironmentVariables();
+
     ParseArgsStatus status = app.ParseArguments(argc, argv);
     if (status == PARSE_ARGS_FAIL)
     {
diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp
index 60715ac1..954fcc4e 100644
--- a/src/app/pathman.cpp
+++ b/src/app/pathman.cpp
@@ -41,7 +41,7 @@ CPathManager::CPathManager(CSystemUtils* systemUtils)
     : m_dataPath(systemUtils->GetDataPath())
     , m_langPath(systemUtils->GetLangPath())
     , m_savePath(systemUtils->GetSaveDir())
-    , m_modAutoloadDir{ m_dataPath + "/mods", m_savePath + "/mods" }
+    , m_modAutoloadDir{}
     , m_mods{}
 {
 }
@@ -130,6 +130,10 @@ void CPathManager::InitPaths()
 {
     GetLogger()->Info("Data path: %s\n", m_dataPath.c_str());
     GetLogger()->Info("Save path: %s\n", m_savePath.c_str());
+
+    m_modAutoloadDir.push_back(m_dataPath + "/mods");
+    m_modAutoloadDir.push_back(m_savePath + "/mods");
+
     if (!m_modAutoloadDir.empty())
     {
         GetLogger()->Info("Mod autoload dirs:\n");
diff --git a/src/common/config.h.cmake b/src/common/config.h.cmake
index f6d0bcae..556e067e 100644
--- a/src/common/config.h.cmake
+++ b/src/common/config.h.cmake
@@ -18,5 +18,5 @@
 
 #cmakedefine PORTABLE_SAVES @PORTABLE_SAVES@
 
-#define COLOBOT_DEFAULT_DATADIR "@COLOBOT_INSTALL_DATA_DIR@"
-#define COLOBOT_I18N_DIR "@COLOBOT_INSTALL_I18N_DIR@"
+#define COLOBOT_DEFAULT_DATADIR "@COLOBOT_DATA_DIR@"
+#define COLOBOT_I18N_DIR "@COLOBOT_I18N_DIR@"
diff --git a/src/common/system/system.cpp b/src/common/system/system.cpp
index 12dc1d36..d69fbf6c 100644
--- a/src/common/system/system.cpp
+++ b/src/common/system/system.cpp
@@ -36,6 +36,7 @@
 #include <iostream>
 #include <algorithm>
 
+#include <SDL2/SDL.h>
 
 std::unique_ptr<CSystemUtils> CSystemUtils::Create()
 {
@@ -176,17 +177,41 @@ float CSystemUtils::TimeStampDiff(SystemTimeStamp *before, SystemTimeStamp *afte
     return result;
 }
 
+std::string CSystemUtils::GetBasePath()
+{
+    if (m_basePath.empty())
+    {
+        auto* path = SDL_GetBasePath();
+        m_basePath = path;
+        SDL_free(path);
+    }
+    return m_basePath;
+}
+
 std::string CSystemUtils::GetDataPath()
 {
+#ifdef USE_RELATIVE_PATHS
+    return GetBasePath() + COLOBOT_DEFAULT_DATADIR;
+#else
     return COLOBOT_DEFAULT_DATADIR;
+#endif
 }
 
 std::string CSystemUtils::GetLangPath()
 {
+#ifdef USE_RELATIVE_PATHS
+    return GetBasePath() + COLOBOT_I18N_DIR;
+#else
     return COLOBOT_I18N_DIR;
+#endif
 }
 
 std::string CSystemUtils::GetSaveDir()
 {
-    return "./saves";
+    return GetBasePath() + "saves";
+}
+
+std::string CSystemUtils::GetEnvVar(const std::string& name)
+{
+    return "";
 }
diff --git a/src/common/system/system.h b/src/common/system/system.h
index 36e736c9..95901390 100644
--- a/src/common/system/system.h
+++ b/src/common/system/system.h
@@ -127,6 +127,9 @@ public:
     /** The difference is \a after - \a before. */
     virtual long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after) = 0;
 
+    //! Returns the path where the executable binary is located (ends with the path separator)
+    virtual std::string GetBasePath();
+
     //! Returns the data path (containing textures, levels, helpfiles, etc)
     virtual std::string GetDataPath();
 
@@ -136,9 +139,13 @@ public:
     //! Returns the save dir location
     virtual std::string GetSaveDir();
 
+    //! Returns the environment variable with the given name or an empty string if it does not exist
+    virtual std::string GetEnvVar(const std::string &name);
+
     //! Sleep for given amount of microseconds
     virtual void Usleep(int usecs) = 0;
 
 private:
+    std::string m_basePath;
     std::vector<std::unique_ptr<SystemTimeStamp>> m_timeStamps;
 };
diff --git a/src/common/system/system_linux.cpp b/src/common/system/system_linux.cpp
index 9bfe431a..69551bce 100644
--- a/src/common/system/system_linux.cpp
+++ b/src/common/system/system_linux.cpp
@@ -102,23 +102,23 @@ std::string CSystemUtilsLinux::GetSaveDir()
     std::string savegameDir;
 
     // Determine savegame dir according to XDG Base Directory Specification
-    char *envXDG_DATA_HOME = getenv("XDG_DATA_HOME");
-    if (envXDG_DATA_HOME == nullptr)
+    auto envXDG_DATA_HOME = GetEnvVar("XDG_DATA_HOME");
+    if (envXDG_DATA_HOME.empty())
     {
-        char *envHOME = getenv("HOME");
-        if (envHOME == nullptr)
+        auto envHOME = GetEnvVar("HOME");
+        if (envHOME.empty())
         {
-            GetLogger()->Warn("Unable to find directory for saves - using current directory");
-            savegameDir = "./saves";
+            GetLogger()->Warn("Unable to find directory for saves - using default directory");
+            savegameDir = CSystemUtils::GetSaveDir();
         }
         else
         {
-            savegameDir = std::string(envHOME) + "/.local/share/colobot";
+            savegameDir = envHOME + "/.local/share/colobot";
         }
     }
     else
     {
-        savegameDir = std::string(envXDG_DATA_HOME) + "/colobot";
+        savegameDir = envXDG_DATA_HOME + "/colobot";
     }
     GetLogger()->Trace("Saved game files are going to %s\n", savegameDir.c_str());
 
@@ -126,6 +126,17 @@ std::string CSystemUtilsLinux::GetSaveDir()
 #endif
 }
 
+std::string CSystemUtilsLinux::GetEnvVar(const std::string& name)
+{
+    char* envVar = getenv(name.c_str());
+    if (envVar != nullptr)
+    {
+        GetLogger()->Trace("Detected environment variable %s = %s\n", name.c_str(), envVar);
+        return std::string(envVar);
+    }
+    return "";
+}
+
 void CSystemUtilsLinux::Usleep(int usec)
 {
     usleep(usec);
diff --git a/src/common/system/system_linux.h b/src/common/system/system_linux.h
index f1576f31..c4de2c7f 100644
--- a/src/common/system/system_linux.h
+++ b/src/common/system/system_linux.h
@@ -45,6 +45,8 @@ public:
 
     std::string GetSaveDir() override;
 
+    std::string GetEnvVar(const std::string& name) override;
+
     void Usleep(int usec) override;
 
 private:
diff --git a/src/common/system/system_macosx.cpp b/src/common/system/system_macosx.cpp
index 9ef7ce1d..8a537e11 100644
--- a/src/common/system/system_macosx.cpp
+++ b/src/common/system/system_macosx.cpp
@@ -113,6 +113,12 @@ std::string CSystemUtilsMacOSX::GetSaveDir()
 #endif
 }
 
+std::string CSystemUtilsMacOSX::GetEnvVar(const std::string& str)
+{
+    // TODO: I have no Mac
+    return std::string();
+}
+
 void CSystemUtilsMacOSX::Usleep(int usec)
 {
     usleep(usec);
diff --git a/src/common/system/system_macosx.h b/src/common/system/system_macosx.h
index 5b572ec4..c8ea607a 100644
--- a/src/common/system/system_macosx.h
+++ b/src/common/system/system_macosx.h
@@ -36,6 +36,8 @@ public:
     std::string GetLangPath() override;
     std::string GetSaveDir() override;
 
+    std::string GetEnvVar(const std::string& name) override;
+
     void Usleep(int usec) override;
 
 private:
diff --git a/src/common/system/system_windows.cpp b/src/common/system/system_windows.cpp
index a20bfcf1..9a8e6833 100644
--- a/src/common/system/system_windows.cpp
+++ b/src/common/system/system_windows.cpp
@@ -115,15 +115,15 @@ std::string CSystemUtilsWindows::GetSaveDir()
 #else
     std::string savegameDir;
 
-    wchar_t* envUSERPROFILE = _wgetenv(L"USERPROFILE");
-    if (envUSERPROFILE == nullptr)
+    auto envUSERPROFILE = GetEnvVar("USERPROFILE");
+    if (envUSERPROFILE.empty())
     {
-        GetLogger()->Warn("Unable to find directory for saves - using current directory");
-        savegameDir = "./saves";
+        GetLogger()->Warn("Unable to find directory for saves - using default directory");
+        savegameDir = CSystemUtils::GetSaveDir();
     }
     else
     {
-        savegameDir = UTF8_Encode(std::wstring(envUSERPROFILE)) + "\\colobot";
+        savegameDir = envUSERPROFILE + "\\colobot";
     }
     GetLogger()->Trace("Saved game files are going to %s\n", savegameDir.c_str());
 
@@ -131,6 +131,22 @@ std::string CSystemUtilsWindows::GetSaveDir()
 #endif
 }
 
+std::string CSystemUtilsWindows::GetEnvVar(const std::string& name)
+{
+    std::wstring wname(name.begin(), name.end());
+    wchar_t* envVar = _wgetenv(wname.c_str());
+    if (envVar == nullptr)
+    {
+        return "";
+    }
+    else
+    {
+        std::string var = UTF8_Encode(std::wstring(envVar));
+        GetLogger()->Trace("Detected environment variable %s = %s\n", name.c_str(), var.c_str());
+        return var;
+    }
+}
+
 void CSystemUtilsWindows::Usleep(int usec)
 {
    LARGE_INTEGER ft;
diff --git a/src/common/system/system_windows.h b/src/common/system/system_windows.h
index 74f02455..05f3c910 100644
--- a/src/common/system/system_windows.h
+++ b/src/common/system/system_windows.h
@@ -43,6 +43,8 @@ public:
 
     std::string GetSaveDir() override;
 
+    std::string GetEnvVar(const std::string& name) override;
+
     void Usleep(int usec) override;
 
 public: