From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: safarp <safar.pavel@gmail.com>
Date: Mon, 21 Mar 2022 21:51:29 +0100
Subject: [PATCH] Make it build with SDL 2 instead of 1.2

---
 Build/Laptop/IMP_Begin_Screen.cc |   2 +-
 Build/Laptop/IMP_HomePage.cc     |   4 +-
 Build/SaveLoadGame.cc            |  10 +-
 Build/Utils/Quantize.cc          |   2 +-
 Build/Utils/Text_Input.cc        |  23 +-
 Makefile                         |  15 +-
 sgp/FileMan.cc                   | 211 +++++++-----
 sgp/FileMan.h                    |  15 +-
 sgp/Input.cc                     | 243 +++++++++-----
 sgp/Input.h                      |  22 +-
 sgp/PCX.cc                       |   2 +-
 sgp/Platform.h                   |   8 +-
 sgp/SGP.cc                       |  23 +-
 sgp/STCI.cc                      |   2 +-
 sgp/SoundMan.cc                  | 537 ++++++-------------------------
 sgp/VSurface.cc                  |  13 +-
 sgp/Video.cc                     | 282 ++++++----------
 sgp/Video.h                      |   5 +-
 18 files changed, 566 insertions(+), 853 deletions(-)

diff --git a/Build/Laptop/IMP_Begin_Screen.cc b/Build/Laptop/IMP_Begin_Screen.cc
index 81db086..63c6bab 100644
--- a/Build/Laptop/IMP_Begin_Screen.cc
+++ b/Build/Laptop/IMP_Begin_Screen.cc
@@ -398,7 +398,7 @@ static void GetPlayerKeyBoardInputForIMPBeginScreen(void)
 	// handle input events
   while( DequeueEvent(&InputEvent) )
   {
-    if(	!HandleTextInput( &InputEvent ) && (InputEvent.usEvent == KEY_DOWN || InputEvent.usEvent == KEY_REPEAT) )
+    if(	!HandleTextInput( &InputEvent ) && (InputEvent.usEvent == TEXT_INPUT || InputEvent.usEvent == KEY_DOWN || InputEvent.usEvent == KEY_REPEAT) )
 		{
 		  switch( InputEvent.usParam )
 			{
diff --git a/Build/Laptop/IMP_HomePage.cc b/Build/Laptop/IMP_HomePage.cc
index 1512a04..bc0157a 100644
--- a/Build/Laptop/IMP_HomePage.cc
+++ b/Build/Laptop/IMP_HomePage.cc
@@ -230,7 +230,7 @@ static void GetPlayerKeyBoardInputForIMPHomePage(void)
 	InputAtom					InputEvent;
 	while (DequeueEvent(&InputEvent))
   {
-		if(	!HandleTextInput( &InputEvent ) && (InputEvent.usEvent == KEY_DOWN || InputEvent.usEvent == KEY_REPEAT || InputEvent.usEvent == KEY_UP ) )
+		if(	!HandleTextInput( &InputEvent ) && (InputEvent.usEvent == TEXT_INPUT || InputEvent.usEvent == KEY_DOWN || InputEvent.usEvent == KEY_REPEAT || InputEvent.usEvent == KEY_UP ) )
 		{
 		  switch( InputEvent.usParam )
 			{
@@ -247,7 +247,7 @@ static void GetPlayerKeyBoardInputForIMPHomePage(void)
 				case SDLK_ESCAPE: HandleLapTopESCKey(); break;
 
 				default:
-					if(InputEvent.usEvent == KEY_DOWN || InputEvent.usEvent == KEY_REPEAT )
+					if(InputEvent.usEvent == TEXT_INPUT)
 					{
 						HandleTextEvent(&InputEvent);
 					}
diff --git a/Build/SaveLoadGame.cc b/Build/SaveLoadGame.cc
index dbf14b8..9f48503 100644
--- a/Build/SaveLoadGame.cc
+++ b/Build/SaveLoadGame.cc
@@ -2545,8 +2545,8 @@ static void UpdateMercMercContractInfo(void)
 INT8 GetNumberForAutoSave( BOOLEAN fLatestAutoSave )
 {
 	BOOLEAN	fFile1Exist, fFile2Exist;
-	SGP_FILETIME	CreationTime1, LastAccessedTime1, LastWriteTime1;
-	SGP_FILETIME	CreationTime2, LastAccessedTime2, LastWriteTime2;
+	time_t	LastWriteTime1;
+	time_t	LastWriteTime2;
 
 	fFile1Exist = FALSE;
 	fFile2Exist = FALSE;
@@ -2559,15 +2559,13 @@ INT8 GetNumberForAutoSave( BOOLEAN fLatestAutoSave )
 
 	if( GCM->doesGameResExists( zFileName1 ) )
 	{
-		AutoSGPFile hFile(GCM->openUserPrivateFileForReading(std::string(zFileName1)));
-		GetFileManFileTime( hFile, &CreationTime1, &LastAccessedTime1, &LastWriteTime1 );
+		GetFileManFileTime( zFileName1, &LastWriteTime1 );
 		fFile1Exist = TRUE;
 	}
 
 	if( GCM->doesGameResExists( zFileName2 ) )
 	{
-		AutoSGPFile hFile(GCM->openUserPrivateFileForReading(std::string(zFileName2)));
-		GetFileManFileTime( hFile, &CreationTime2, &LastAccessedTime2, &LastWriteTime2 );
+		GetFileManFileTime( zFileName2, &LastWriteTime2 );
 		fFile2Exist = TRUE;
 	}
 
diff --git a/Build/Utils/Quantize.cc b/Build/Utils/Quantize.cc
index 998179a..c036a78 100644
--- a/Build/Utils/Quantize.cc
+++ b/Build/Utils/Quantize.cc
@@ -127,7 +127,7 @@ static size_t GetPaletteColors(const NODE* const pTree, SGPPaletteEntry* const p
 		dst->r      = pTree->nRedSum   / pTree->nPixelCount;
 		dst->g      = pTree->nGreenSum / pTree->nPixelCount;
 		dst->b      = pTree->nBlueSum  / pTree->nPixelCount;
-		dst->unused = 0;
+		dst->a      = 0;
 	}
 	else
 	{
diff --git a/Build/Utils/Text_Input.cc b/Build/Utils/Text_Input.cc
index fc64d58..4869ce2 100644
--- a/Build/Utils/Text_Input.cc
+++ b/Build/Utils/Text_Input.cc
@@ -562,10 +562,19 @@ BOOLEAN HandleTextInput(InputAtom const* const a)
 	// Not in text input mode
 	if (!gfTextInputMode) return FALSE;
 	// Unless we are psycho typers, we only want to process these key events.
-	if (a->usEvent != KEY_DOWN && a->usEvent != KEY_REPEAT) return FALSE;
+	if (a->usEvent != TEXT_INPUT && a->usEvent != KEY_DOWN && a->usEvent != KEY_REPEAT) return FALSE;
 	// Currently in a user field, so return unless TAB is pressed.
 	if (!gfEditingText && a->usParam != SDLK_TAB) return FALSE;
 
+	if (a->usEvent == TEXT_INPUT) {
+		wchar_t const c = a->Char;
+		/* If the key has no character associated, bail out */
+		AssertMsg(c != L'\0', "TEXT_INPUT event sent null character");
+		DeleteHilitedText();
+		HandleRegularInput(c);
+		return TRUE;
+	}
+
 	switch (a->usKeyState)
 	{
 		case 0:
@@ -642,7 +651,8 @@ BOOLEAN HandleTextInput(InputAtom const* const a)
 					}
 					break;
 
-				default: goto enter_character;
+				default:
+					return TRUE;
 			}
 			break;
 
@@ -670,14 +680,9 @@ BOOLEAN HandleTextInput(InputAtom const* const a)
 					gubCursorPos = 0;
 					return TRUE;
 
-				default: // Check for typing keys
-enter_character:
-					wchar_t const c = a->Char;
-					/* If the key has no character associated, bail out */
-					if (c == L'\0') return FALSE;
-					DeleteHilitedText();
-					HandleRegularInput(c);
+				default:
 					return TRUE;
+
 			}
 
 		case CTRL_DOWN:
diff --git a/Makefile b/Makefile
index 6db811f..53103d3 100644
--- a/Makefile
+++ b/Makefile
@@ -38,12 +38,18 @@ CFLAGS += -DGAME_VERSION=\"$(GAME_VERSION)\"
 
 
 ifdef LOCAL_SDL_LIB
-CFLAGS_SDL= -I./$(LOCAL_SDL_LIB)/include/SDL -D_GNU_SOURCE=1 -Dmain=SDL_main
-LDFLAGS_SDL=-L./$(LOCAL_SDL_LIB)/lib -lmingw32 -lSDLmain -lSDL -mwindows
+CFLAGS_SDL= -I./$(LOCAL_SDL_LIB)/include/SDL2 -D_GNU_SOURCE=1 -Dmain=SDL_main
+LDFLAGS_SDL=-L./$(LOCAL_SDL_LIB)/lib -lmingw32 -lSDL2main -lSDL2 -mwindows
 endif
 
 ifndef LOCAL_SDL_LIB
 
+# using Minerva SDL2 library
+ifdef MINERVA
+CFLAGS_SDL= -I${MINERVA_BUILD_DIR}/Root/usr/local/include/SDL2 -D_REENTRANT
+LDFLAGS_SDL= -lSDL2
+endif
+
 # using system SDL library
 
 SDL_CONFIG  ?= sdl-config
@@ -156,6 +162,7 @@ CCFLAGS += -Wimplicit-int
 CCFLAGS += -Wmissing-prototypes
 
 CXXFLAGS += $(CFLAGS)
+CXXFLAGS += -std=c++11
 
 LDFLAGS += -lm
 
@@ -723,10 +730,10 @@ build-on-mac:
 	make "CFLAGS_SDL=$(MACOS_STATIC_CFLAGS_SDL)" "LDFLAGS_SDL=$(MACOS_STATIC_LDFLAGS_SDL)"
 
 build-on-win:
-	PATH=/cygdrive/c/MinGW/bin:$$PATH make all USE_MINGW=1 MINGW_PREFIX=/cygdrive/c/MinGW/bin/mingw32 LOCAL_SDL_LIB=_build/lib-SDL-devel-1.2.15-mingw32
+	PATH=/cygdrive/c/MinGW/bin:$$PATH make all USE_MINGW=1 MINGW_PREFIX=/cygdrive/c/MinGW/bin/mingw32 LOCAL_SDL_LIB=_build/lib-SDL2-2.0.4-mingw/i686-w64-mingw32 WITH_LPTHREAD=0
 	cp /cygdrive/c/MinGW/bin/libstdc++-6.dll .
 	cp /cygdrive/c/MinGW/bin/libgcc_s_dw2-1.dll .
-	cp _build/lib-SDL-devel-1.2.15-mingw32/bin/SDL.dll .
+	cp _build/lib-SDL2-2.0.4-mingw/i686-w64-mingw32/bin/SDL2.dll .
 
 SOURCE_DIR_NAME := ja2-stracciatella_$(VERSION)
 build-source-archive:
diff --git a/sgp/FileMan.cc b/sgp/FileMan.cc
index 0b607c2..cdfca08 100644
--- a/sgp/FileMan.cc
+++ b/sgp/FileMan.cc
@@ -9,12 +9,10 @@
 #include "LibraryDataBase.h"
 #include "MemMan.h"
 #include "PODObj.h"
-#include "Logger.h"
 
 #include "boost/filesystem.hpp"
 
 #include "slog/slog.h"
-#define TAG "FileMan"
 
 #if _WIN32
 #include <shlobj.h>
@@ -23,6 +21,7 @@
 #endif
 
 #include "PlatformIO.h"
+#include "Debug.h"
 
 #if MACOS_USE_RESOURCES_FROM_BUNDLE && defined __APPLE__  && defined __MACH__
 #include <CoreFoundation/CFBundle.h>
@@ -30,11 +29,15 @@
 
 #if CASE_SENSITIVE_FS
 #include <dirent.h>
+#include <SDL_rwops.h>
 #endif
 
 // XXX: remove FileMan class and make it into a namespace
 
 #define LOCAL_CURRENT_DIR "tmp"
+#define SDL_RWOPS_SGP 222
+
+#define DEBUG_TAG_FILEMAN "Fileman"
 
 enum FileOpenFlags
 {
@@ -117,18 +120,23 @@ std::string FileMan::findConfigFolderAndSwitchIntoIt()
 
 	if (mkdir(configFolderPath.c_str(), 0700) != 0 && errno != EEXIST)
 	{
-    LOG_ERROR("Unable to create directory '%s'\n", configFolderPath.c_str());
+    SLOGE(DEBUG_TAG_FILEMAN, "Unable to create tmp directory '%s'", configFolderPath.c_str());
 		throw std::runtime_error("Unable to local directory");
 	}
+	return switchTmpFolder(configFolderPath);
+}
 
+/** Switch config folder. */
+std::string FileMan::switchTmpFolder(std::string home)
+{
   // Create another directory and set is as the current directory for the process
   // Temporary files will be created in this directory.
   // ----------------------------------------------------------------------------
 
-  std::string tmpPath = FileMan::joinPaths(configFolderPath, LOCAL_CURRENT_DIR);
+  std::string tmpPath = FileMan::joinPaths(home, LOCAL_CURRENT_DIR);
 	if (mkdir(tmpPath.c_str(), 0700) != 0 && errno != EEXIST)
 	{
-    LOG_ERROR("Unable to create tmp directory '%s'\n", tmpPath.c_str());
+    SLOGE(DEBUG_TAG_FILEMAN, "Unable to create tmp directory '%s'", tmpPath.c_str());
 		throw std::runtime_error("Unable to create tmp directory");
 	}
   else
@@ -136,7 +144,7 @@ std::string FileMan::findConfigFolderAndSwitchIntoIt()
     SetFileManCurrentDirectory(tmpPath.c_str());
   }
 
-  return configFolderPath;
+  return home;
 }
 
 
@@ -237,19 +245,8 @@ void FileClose(SGPFile* f)
 	MemFree(f);
 }
 
-
-#ifdef JA2TESTVERSION
-#	include "Timer_Control.h"
-extern UINT32 uiTotalFileReadTime;
-extern UINT32 uiTotalFileReadCalls;
-#endif
-
 void FileRead(SGPFile* const f, void* const pDest, size_t const uiBytesToRead)
 {
-#ifdef JA2TESTVERSION
-	const UINT32 uiStartTime = GetJA2Clock();
-#endif
-
 	BOOLEAN ret;
 	if (f->flags & SGPFILE_REAL)
 	{
@@ -260,12 +257,6 @@ void FileRead(SGPFile* const f, void* const pDest, size_t const uiBytesToRead)
 		ret = LoadDataFromLibrary(&f->u.lib, pDest, (UINT32)uiBytesToRead);
 	}
 
-#ifdef JA2TESTVERSION
-	//Add the time that we spent in this function to the total.
-	uiTotalFileReadTime += GetJA2Clock() - uiStartTime;
-	uiTotalFileReadCalls++;
-#endif
-
 	if (!ret) throw std::runtime_error("Reading from file failed");
 }
 
@@ -276,6 +267,80 @@ void FileWrite(SGPFile* const f, void const* const pDest, size_t const uiBytesTo
 	if (fwrite(pDest, uiBytesToWrite, 1, f->u.file) != 1) throw std::runtime_error("Writing to file failed");
 }
 
+static int64_t SGPSeekRW(SDL_RWops *context, int64_t offset, int whence)
+{
+	SGPFile* sgpFile = (SGPFile*)(context->hidden.unknown.data1);
+	FileSeekMode mode = FILE_SEEK_FROM_CURRENT;
+	switch (whence) {
+		case RW_SEEK_SET:
+			mode = FILE_SEEK_FROM_START;
+			break;
+		case RW_SEEK_END:
+			mode = FILE_SEEK_FROM_END;
+			break;
+		default:
+			break;
+	}
+
+	FileSeek(sgpFile, offset, mode);
+
+	return int64_t(FileGetPos(sgpFile));
+}
+
+static int64_t SGPSizeRW(SDL_RWops *context)
+{
+	SGPFile* sgpFile = (SGPFile*)(context->hidden.unknown.data1);
+
+	return FileGetSize(sgpFile);
+}
+
+static size_t SGPReadRW(SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
+{
+	SGPFile* sgpFile = (SGPFile*)(context->hidden.unknown.data1);
+	UINT32 posBefore = UINT32(FileGetPos(sgpFile));
+
+	FileRead(sgpFile, ptr, size * maxnum);
+
+	UINT32 posAfter = UINT32(FileGetPos(sgpFile));
+
+	return (posAfter - posBefore) / size;
+}
+
+static size_t SGPWriteRW(SDL_RWops *context, const void *ptr, size_t size, size_t num)
+{
+	AssertMsg(false, "SGPWriteRW not supported");
+	return 0;
+}
+
+static int SGPCloseRW(SDL_RWops *context)
+{
+	if(context->type != SDL_RWOPS_SGP)
+	{
+		return SDL_SetError("Wrong kind of SDL_RWops for SGPCloseRW()");
+	}
+	SGPFile* sgpFile = (SGPFile*)(context->hidden.unknown.data1);
+
+	FileClose(sgpFile);
+	SDL_FreeRW(context);
+
+	return 0;
+}
+
+SDL_RWops* FileGetRWOps(SGPFile* const f) {
+	SDL_RWops* rwOps = SDL_AllocRW();
+	if(rwOps == NULL) {
+		return NULL;
+	}
+	rwOps->type = SDL_RWOPS_SGP;
+	rwOps->size = SGPSizeRW;
+	rwOps->seek = SGPSeekRW;
+	rwOps->read = SGPReadRW;
+	rwOps->write= SGPWriteRW;
+	rwOps->close= SGPCloseRW;
+	rwOps->hidden.unknown.data1 = f;
+
+	return rwOps;
+}
 
 void FileSeek(SGPFile* const f, INT32 distance, FileSeekMode const how)
 {
@@ -397,63 +462,36 @@ BOOLEAN FileClearAttributes(const std::string &filename)
 
 BOOLEAN FileClearAttributes(const char* const filename)
 {
-#if 1 // XXX TODO
-  SLOGW(TAG, "ignoring %s(\"%s\")", __func__, filename);
-	return FALSE;
-	// UNIMPLEMENTED
-#else
-	return SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL);
-#endif
+	using namespace boost::filesystem;
+
+	permissions(filename, ( add_perms | owner_read | owner_write | group_read | group_write ));
+	return true;
 }
 
 
-BOOLEAN GetFileManFileTime(const SGPFile* f, SGP_FILETIME* const pCreationTime, SGP_FILETIME* const pLastAccessedTime, SGP_FILETIME* const pLastWriteTime)
+BOOLEAN GetFileManFileTime(const char* fileName, time_t* const pLastWriteTime)
 {
-#if 1 // XXX TODO
-	UNIMPLEMENTED;
-  return FALSE;
-#else
-	//Initialize the passed in variables
-	memset(pCreationTime,     0, sizeof(*pCreationTime));
-	memset(pLastAccessedTime, 0, sizeof(*pLastAccessedTime));
-	memset(pLastWriteTime,    0, sizeof(*pLastWriteTime));
-
-	if (f->flags & SGPFILE_REAL)
+	using namespace boost::filesystem;
+	*pLastWriteTime = last_write_time(fileName);
+	if(*pLastWriteTime == -1)
 	{
-		const HANDLE hRealFile = f->u.file;
-
-		//Gets the UTC file time for the 'real' file
-		SGP_FILETIME sCreationUtcFileTime;
-		SGP_FILETIME sLastAccessedUtcFileTime;
-		SGP_FILETIME sLastWriteUtcFileTime;
-		GetFileTime(hRealFile, &sCreationUtcFileTime, &sLastAccessedUtcFileTime, &sLastWriteUtcFileTime);
-
-		//converts the creation UTC file time to the current time used for the file
-		FileTimeToLocalFileTime(&sCreationUtcFileTime, pCreationTime);
-
-		//converts the accessed UTC file time to the current time used for the file
-		FileTimeToLocalFileTime(&sLastAccessedUtcFileTime, pLastAccessedTime);
-
-		//converts the write UTC file time to the current time used for the file
-		FileTimeToLocalFileTime(&sLastWriteUtcFileTime, pLastWriteTime);
-		return TRUE;
+		return FALSE;
 	}
-	else
-	{
-		return GetLibraryFileTime(&f->u.lib, pLastWriteTime);
-	}
-#endif
+	return TRUE;
 }
 
 
-INT32	CompareSGPFileTimes(const SGP_FILETIME* const pFirstFileTime, const SGP_FILETIME* const pSecondFileTime)
+INT32 CompareSGPFileTimes(const time_t* const pFirstFileTime, const time_t* const pSecondFileTime)
 {
-#if 1 // XXX TODO
-	UNIMPLEMENTED;
-  return 0;
-#else
-	return CompareFileTime(pFirstFileTime, pSecondFileTime);
-#endif
+	if ( *pFirstFileTime < *pSecondFileTime )
+	{
+		return -1;
+	}
+	if ( *pFirstFileTime > *pSecondFileTime )
+	{
+		return 1;
+	}
+	return 0;
 }
 
 
@@ -462,19 +500,19 @@ FILE* GetRealFileHandleFromFileManFileHandle(const SGPFile* f)
 	return f->flags & SGPFILE_REAL ? f->u.file : f->u.lib.lib->hLibraryHandle;
 }
 
-UINT32 GetFreeSpaceOnHardDriveWhereGameIsRunningFrom(void)
+uintmax_t GetFreeSpaceOnHardDriveWhereGameIsRunningFrom(void)
 {
-#if 1 // XXX TODO
-	FIXME
-	return 1024 * 1024 * 1024; // XXX TODO return an arbitrary number for now
-#else
-	//get the drive letter from the exec dir
-  STRING512 zDrive;
-	_splitpath(GetExecutableDirectory(), zDrive, NULL, NULL, NULL);
-
-	sprintf(zDrive, "%s\\", zDrive);
-	return GetFreeSpaceOnHardDrive(zDrive);
-#endif
+	using namespace boost::filesystem;
+	space_info si = space(current_path());
+	if (si.available == -1)
+	{
+		/* something is wrong, tell everyone no space available */
+		return 0;
+	}
+	else
+	{
+		return si.available;
+	}
 }
 
 /** Join two path components. */
@@ -562,7 +600,7 @@ bool FileMan::findObjectCaseInsensitive(const char *directory, const char *name,
     }
   }
 
-  // LOG_INFO("XXXXX Looking for %s/[ %s ] : %s\n", directory, name, result ? "success" : "failure");
+  // SLOGI(DEBUG_TAG_FILEMAN,"Looking for %s/[ %s ] : %s", directory, name, result ? "success" : "failure");
   return result;
 }
 #endif
@@ -820,3 +858,10 @@ bool FileMan::checkFileExistance(const char *folder, const char *fileName)
   path /= fileName;
   return boost::filesystem::exists(path);
 }
+
+void FileMan::moveFile(const char *from, const char *to)
+{
+	boost::filesystem::path fromPath(from);
+	boost::filesystem::path toPath(to);
+	boost::filesystem::rename(fromPath, toPath);
+}
diff --git a/sgp/FileMan.h b/sgp/FileMan.h
index a7f96cb..d4fdf3c 100644
--- a/sgp/FileMan.h
+++ b/sgp/FileMan.h
@@ -10,8 +10,6 @@
 #ifdef _WIN32
 #	define WIN32_LEAN_AND_MEAN
 #	include <windows.h>
-#else
-#	include <glob.h>
 #endif
 
 /* Delete the file at path. Returns true iff deleting the file succeeded or
@@ -21,6 +19,7 @@ void FileDelete(const std::string &path);
 
 void FileRead( SGPFile*, void*       pDest, size_t uiBytesToRead);
 void FileWrite(SGPFile*, void const* pDest, size_t uiBytesToWrite);
+SDL_RWops* FileGetRWOps(SGPFile* const f);
 
 template<typename T, typename U> static inline void FileWriteArray(SGPFile* const f, T const& n, U const* const data)
 {
@@ -51,13 +50,13 @@ BOOLEAN        FileClearAttributes(const char* filename);
 BOOLEAN FileClearAttributes(const std::string &filename);
 
 
-BOOLEAN GetFileManFileTime(const SGPFile* hFile, SGP_FILETIME* pCreationTime, SGP_FILETIME* pLastAccessedTime, SGP_FILETIME* pLastWriteTime);
+BOOLEAN GetFileManFileTime(const char* fileName, time_t* pLastWriteTime);
 
 /* returns
  * - -1 if the First file time is less than second file time. (first file is older)
  * -  0 First file time is equal to second file time.
  * - +1 First file time is greater than second file time (first file is newer). */
-INT32 CompareSGPFileTimes(const SGP_FILETIME* const pFirstFileTime, const SGP_FILETIME* const pSecondFileTime);
+INT32 CompareSGPFileTimes(const time_t* const pFirstFileTime, const time_t* const pSecondFileTime);
 
 /* Pass in the Fileman file handle of an OPEN file and it will return..
  * - if its a Real File, the return will be the handle of the REAL file
@@ -65,7 +64,7 @@ INT32 CompareSGPFileTimes(const SGP_FILETIME* const pFirstFileTime, const SGP_FI
 FILE* GetRealFileHandleFromFileManFileHandle(const SGPFile* hFile);
 
 //Gets the amount of free space on the hard drive that the main executeablt is runnning from
-UINT32 GetFreeSpaceOnHardDriveWhereGameIsRunningFrom(void);
+uintmax_t GetFreeSpaceOnHardDriveWhereGameIsRunningFrom(void);
 
 /***
  * New file manager.
@@ -78,6 +77,9 @@ public:
   /** Find config folder and switch into it. */
   static std::string findConfigFolderAndSwitchIntoIt();
 
+	/** Switch config folder into it. */
+	static std::string switchTmpFolder(std::string homeDir);
+
   /** Open file for writing.
    * If file is missing it will be created.
    * If file exists, it's content will be removed. */
@@ -156,6 +158,9 @@ public:
   /** Check file existance. */
   static bool checkFileExistance(const char *folder, const char *fileName);
 
+	/** Move a file */
+	static void moveFile(const char *from, const char *to);
+
 private:
   /** Private constructor to avoid instantiation. */
   FileMan() {};
diff --git a/sgp/Input.cc b/sgp/Input.cc
index 3b64602..6d82a14 100644
--- a/sgp/Input.cc
+++ b/sgp/Input.cc
@@ -1,3 +1,5 @@
+#include <bitset>
+#include "UTF8String.h"
 #include "Types.h"
 #include "Input.h"
 #include "MemMan.h"
@@ -7,11 +9,12 @@
 #include "Local.h"
 #include "UILayout.h"
 
+#include "slog/slog.h"
 
-// The gfKeyState table is used to track which of the keys is up or down at any one time. This is used while polling
-// the interface.
 
-BOOLEAN gfKeyState[SDLK_LAST]; // TRUE = Pressed, FALSE = Not Pressed
+// The gfKeyState table is used to track which of the keys is up or down at any one time. This is used while polling
+// the interface.  true = pressed, false = not pressed.
+static std::bitset<2 * SDL_NUM_SCANCODES> gfKeyState;
 static BOOLEAN fCursorWasClipped = FALSE;
 static SGPRect gCursorClipRect;
 
@@ -22,9 +25,11 @@ static UINT32 guiSingleClickTimer;
 
 static UINT32 guiLeftButtonRepeatTimer;
 static UINT32 guiRightButtonRepeatTimer;
+static UINT32 guiMiddleButtonRepeatTimer;
 
 BOOLEAN gfLeftButtonState;  // TRUE = Pressed, FALSE = Not Pressed
 BOOLEAN gfRightButtonState; // TRUE = Pressed, FALSE = Not Pressed
+BOOLEAN gfMiddleButtonState;// TRUE = Pressed, FALSE = Not Pressed
 UINT16  gusMouseXPos;       // X position of the mouse on screen
 UINT16  gusMouseYPos;       // y position of the mouse on screen
 
@@ -49,7 +54,7 @@ static void QueueMouseEvent(UINT16 ubInputEvent)
 }
 
 
-static void QueueKeyEvent(UINT16 ubInputEvent, SDLKey Key, SDLMod Mod, wchar_t Char)
+static void QueueKeyEvent(UINT16 ubInputEvent, SDL_Keycode Key, SDL_Keymod Mod, wchar_t Char)
 {
 	// Can we queue up one more event, if not, the event is lost forever
 	if (gusQueueCount == lengthof(gEventQueue)) return;
@@ -68,6 +73,16 @@ static void QueueKeyEvent(UINT16 ubInputEvent, SDLKey Key, SDLMod Mod, wchar_t C
 	gusTailIndex = (gusTailIndex + 1) % lengthof(gEventQueue);
 }
 
+void SetSafeMousePosition(int x, int y) {
+	if (x < 0) x = 0;
+	if (y < 0) y = 0;
+	if (x > SCREEN_WIDTH) x = SCREEN_WIDTH;
+	if (y > SCREEN_HEIGHT) y = SCREEN_HEIGHT;
+
+	gusMouseXPos = x;
+	gusMouseYPos = y;
+}
+
 
 BOOLEAN DequeueSpecificEvent(InputAtom* Event, UINT32 uiMaskFlags)
 {
@@ -103,12 +118,11 @@ BOOLEAN DequeueEvent(InputAtom* Event)
 
 static void UpdateMousePos(const SDL_MouseButtonEvent* BtnEv)
 {
-	gusMouseXPos = BtnEv->x;
-	gusMouseYPos = BtnEv->y;
+	SetSafeMousePosition(BtnEv->x, BtnEv->y);
 }
 
 
-#if defined WITH_MAEMO
+#if defined(WITH_MAEMO) || defined __APPLE__
 static BOOLEAN g_down_right;
 #endif
 
@@ -123,8 +137,13 @@ void MouseButtonDown(const SDL_MouseButtonEvent* BtnEv)
 #if defined WITH_MAEMO
 			/* If the menu button (mapped to F4) is pressed, then treat the event as
 			 * right click */
-			const Uint8* const key_state = SDL_GetKeyState(NULL);
-			g_down_right = key_state[SDLK_F4];
+			const Uint8* const key_state = SDL_GetKeyboardState(NULL);
+			g_down_right = key_state[SDL_SCANCODE_F4];
+			if (g_down_right) goto right_button;
+#endif
+#if defined(__APPLE__)
+			const Uint8* const key_state = SDL_GetKeyboardState(NULL);
+			g_down_right = key_state[SDL_SCANCODE_LGUI] || key_state[SDL_SCANCODE_RGUI];
 			if (g_down_right) goto right_button;
 #endif
 			guiLeftButtonRepeatTimer = GetClock() + BUTTON_REPEAT_TIMEOUT;
@@ -134,7 +153,7 @@ void MouseButtonDown(const SDL_MouseButtonEvent* BtnEv)
 		}
 
 		case SDL_BUTTON_RIGHT:
-#if defined WITH_MAEMO
+#if defined(WITH_MAEMO) || defined(__APPLE__)
 right_button:
 #endif
 			guiRightButtonRepeatTimer = GetClock() + BUTTON_REPEAT_TIMEOUT;
@@ -142,8 +161,11 @@ right_button:
 			QueueMouseEvent(RIGHT_BUTTON_DOWN);
 			break;
 
-		case SDL_BUTTON_WHEELUP:   QueueMouseEvent(MOUSE_WHEEL_UP);   break;
-		case SDL_BUTTON_WHEELDOWN: QueueMouseEvent(MOUSE_WHEEL_DOWN); break;
+		case SDL_BUTTON_MIDDLE:
+			guiMiddleButtonRepeatTimer = GetClock() + BUTTON_REPEAT_TIMEOUT;
+			gfMiddleButtonState = TRUE;
+			QueueMouseEvent(MIDDLE_BUTTON_DOWN);
+			break;
 	}
 }
 
@@ -155,7 +177,7 @@ void MouseButtonUp(const SDL_MouseButtonEvent* BtnEv)
 	{
 		case SDL_BUTTON_LEFT:
 		{
-#if defined WITH_MAEMO
+#if defined(WITH_MAEMO) || defined(__APPLE__)
 			if (g_down_right) goto right_button;
 #endif
 			guiLeftButtonRepeatTimer = 0;
@@ -174,21 +196,55 @@ void MouseButtonUp(const SDL_MouseButtonEvent* BtnEv)
 		}
 
 		case SDL_BUTTON_RIGHT:
-#if defined WITH_MAEMO
+#if defined WITH_MAEMO || defined(__APPLE__)
 right_button:
 #endif
 			guiRightButtonRepeatTimer = 0;
 			gfRightButtonState = FALSE;
 			QueueMouseEvent(RIGHT_BUTTON_UP);
 			break;
+
+		case SDL_BUTTON_MIDDLE:
+			guiMiddleButtonRepeatTimer = 0;
+			gfMiddleButtonState = FALSE;
+			QueueMouseEvent(MIDDLE_BUTTON_UP);
+			break;
+	}
+}
+
+void MouseWheelScroll(const SDL_MouseWheelEvent* WheelEv)
+{
+	if (WheelEv->y > 0)
+	{
+		QueueMouseEvent(MOUSE_WHEEL_UP);
+	}
+	else
+	{
+		QueueMouseEvent(MOUSE_WHEEL_DOWN);
 	}
 }
 
 
-static void KeyChange(SDL_keysym const* const key_sym, bool const pressed)
+// Remap SDL keycodes with bit 30 set to the range 512..1023
+// Necessary to be able to use the keycode as an index for the gfKeyState bitset.
+static SDL_Keycode RemapKeycode(SDL_Keycode const key)
+{
+	return (key & SDLK_SCANCODE_MASK)
+		? (key & ~SDLK_SCANCODE_MASK) + SDL_NUM_SCANCODES
+		: key;
+}
+
+
+bool _KeyDown(SDL_Keycode const keycode)
+{
+	return gfKeyState[RemapKeycode(keycode)];
+}
+
+
+static void KeyChange(SDL_Keysym const* const key_sym, bool const pressed)
 {
-	SDLKey       key = key_sym->sym;
-	SDLMod const mod = key_sym->mod;
+	SDL_Keycode      key = key_sym->sym;
+	SDL_Keymod const mod = (SDL_Keymod) key_sym->mod;
 	bool   const num = mod & KMOD_NUM;
 	switch (key)
 	{
@@ -197,19 +253,19 @@ static void KeyChange(SDL_keysym const* const key_sym, bool const pressed)
 		case SDLK_F4: return;
 #endif
 
-		case SDLK_KP0:         key = num ? SDLK_0      : SDLK_INSERT;   break;
-		case SDLK_KP1:         key = num ? SDLK_1      : SDLK_END;      break;
-		case SDLK_KP2:         key = num ? SDLK_2      : SDLK_DOWN;     break;
-		case SDLK_KP3:         key = num ? SDLK_3      : SDLK_PAGEDOWN; break;
-		case SDLK_KP4:         key = num ? SDLK_4      : SDLK_LEFT;     break;
-		case SDLK_KP5:
+		case SDLK_KP_0:         key = num ? SDLK_0      : SDLK_INSERT;   break;
+		case SDLK_KP_1:         key = num ? SDLK_1      : SDLK_END;      break;
+		case SDLK_KP_2:         key = num ? SDLK_2      : SDLK_DOWN;     break;
+		case SDLK_KP_3:         key = num ? SDLK_3      : SDLK_PAGEDOWN; break;
+		case SDLK_KP_4:         key = num ? SDLK_4      : SDLK_LEFT;     break;
+		case SDLK_KP_5:
 			if (!num) return;
 			key = SDLK_5;
 			break;
-		case SDLK_KP6:         key = num ? SDLK_6      : SDLK_RIGHT;    break;
-		case SDLK_KP7:         key = num ? SDLK_7      : SDLK_HOME;     break;
-		case SDLK_KP8:         key = num ? SDLK_8      : SDLK_UP;       break;
-		case SDLK_KP9:         key = num ? SDLK_9      : SDLK_PAGEUP;   break;
+		case SDLK_KP_6:         key = num ? SDLK_6      : SDLK_RIGHT;    break;
+		case SDLK_KP_7:         key = num ? SDLK_7      : SDLK_HOME;     break;
+		case SDLK_KP_8:         key = num ? SDLK_8      : SDLK_UP;       break;
+		case SDLK_KP_9:         key = num ? SDLK_9      : SDLK_PAGEUP;   break;
 		case SDLK_KP_PERIOD:   key = num ? SDLK_PERIOD : SDLK_DELETE;   break;
 		case SDLK_KP_DIVIDE:   key = SDLK_SLASH;                        break;
 		case SDLK_KP_MULTIPLY: key = SDLK_ASTERISK;                     break;
@@ -218,12 +274,12 @@ static void KeyChange(SDL_keysym const* const key_sym, bool const pressed)
 		case SDLK_KP_ENTER:    key = SDLK_RETURN;                       break;
 
 		default:
-			if (key >= lengthof(gfKeyState)) return;
+			if ((key & ~SDLK_SCANCODE_MASK) >= SDL_NUM_SCANCODES) return;
 			break;
 	}
 
 	UINT     event_type;
-	BOOLEAN& key_state = gfKeyState[key];
+	bool key_state = _KeyDown(key);
 	if (pressed)
 	{
 		event_type = key_state ? KEY_REPEAT : KEY_DOWN;
@@ -233,32 +289,33 @@ static void KeyChange(SDL_keysym const* const key_sym, bool const pressed)
 		if (!key_state) return;
 		event_type = KEY_UP;
 	}
-	key_state = pressed;
-	QueueKeyEvent(event_type, key, mod, key_sym->unicode);
+	gfKeyState[RemapKeycode(key)] = pressed;
+
+	QueueKeyEvent(event_type, key, mod, '\0');
 }
 
 
-void KeyDown(const SDL_keysym* KeySym)
+void KeyDown(const SDL_Keysym* KeySym)
 {
 	switch (KeySym->sym)
 	{
 		case SDLK_LSHIFT:
 		case SDLK_RSHIFT:
-			_KeyDown(SHIFT) = TRUE;
+			gfKeyState.set(SHIFT);
 			break;
 
 		case SDLK_LCTRL:
 		case SDLK_RCTRL:
-			_KeyDown(CTRL) = TRUE;
+			gfKeyState.set(CTRL);
 			break;
 
 		case SDLK_LALT:
 		case SDLK_RALT:
-			_KeyDown(ALT) = TRUE;
+			gfKeyState.set(ALT);
 			break;
 
-		case SDLK_PRINT:
-		case SDLK_SCROLLOCK:
+		case SDLK_PRINTSCREEN:
+		case SDLK_SCROLLLOCK:
 			break;
 
 		default:
@@ -268,34 +325,35 @@ void KeyDown(const SDL_keysym* KeySym)
 }
 
 
-void KeyUp(const SDL_keysym* KeySym)
+void KeyUp(const SDL_Keysym* KeySym)
 {
 	switch (KeySym->sym)
 	{
 		case SDLK_LSHIFT:
 		case SDLK_RSHIFT:
-			_KeyDown(SHIFT) = FALSE;
+			gfKeyState.reset(SHIFT);
 			break;
 
 		case SDLK_LCTRL:
 		case SDLK_RCTRL:
-			_KeyDown(CTRL) = FALSE;
+			gfKeyState.reset(CTRL);
 			break;
 
 		case SDLK_LALT:
 		case SDLK_RALT:
-			_KeyDown(ALT) = FALSE;
+			gfKeyState.reset(ALT);
 			break;
 
-		case SDLK_PRINT:
-			if (KeySym->mod & KMOD_CTRL) VideoCaptureToggle(); else PrintScreen();
+		case SDLK_PRINTSCREEN:
+			PrintScreen();
 			break;
 
-		case SDLK_SCROLLOCK:
-			SDL_WM_GrabInput
+		case SDLK_SCROLLLOCK:
+			SDL_SetWindowGrab
 			(
-				SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF ?
-					SDL_GRAB_ON : SDL_GRAB_OFF
+				GAME_WINDOW,
+				SDL_GetWindowGrab(GAME_WINDOW) == SDL_FALSE ?
+					SDL_TRUE : SDL_FALSE
 			);
 			break;
 
@@ -313,14 +371,28 @@ void KeyUp(const SDL_keysym* KeySym)
 	}
 }
 
+void TextInput(const SDL_TextInputEvent* TextEv) {
+	try {
+		UTF8String utf8String = UTF8String(TextEv->text);
+		QueueKeyEvent(TEXT_INPUT, SDLK_UNKNOWN, KMOD_NONE, utf8String.getUTF16()[0]);
+	}
+	catch (const InvalidEncodingException&)
+	{
+		// ignore invalid inputs
+		static bool warn = true;
+		if (warn)
+		{
+			SLOGW("SGP", "Received invalid utf-8 character.");
+			warn = false;
+		}
+	}
+}
+
 
 void GetMousePos(SGPPoint* Point)
 {
-	int x;
-	int y;
-	SDL_GetMouseState(&x, &y);
-	Point->iX = x;
-	Point->iY = y;
+	Point->iX = gusMouseXPos;
+	Point->iY = gusMouseYPos;
 }
 
 
@@ -356,20 +428,6 @@ void FreeMouseCursor(void)
 	fCursorWasClipped = FALSE;
 }
 
-
-void RestoreCursorClipRect(void)
-{
-#if 1 // XXX TODO0000
-	UNIMPLEMENTED
-#else
-	if (fCursorWasClipped)
-	{
-		ClipCursor(&gCursorClipRect);
-	}
-#endif
-}
-
-
 void GetRestrictedClipCursor(SGPRect* pRectangle)
 {
 #if 1 // XXX TODO0000
@@ -391,26 +449,27 @@ BOOLEAN IsCursorRestricted(void)
 
 void SimulateMouseMovement( UINT32 uiNewXPos, UINT32 uiNewYPos )
 {
-	// Wizardry NOTE: This function currently doesn't quite work right for in any Windows resolution other than 640x480.
-	// mouse_event() uses your current Windows resolution to calculate the resulting x,y coordinates.  So in order to get
-	// the right coordinates, you'd have to find out the current Windows resolution through a system call, and then do:
-	//		uiNewXPos = uiNewXPos * SCREEN_WIDTH  / WinScreenResX;
-	//		uiNewYPos = uiNewYPos * SCREEN_HEIGHT / WinScreenResY;
-	//
-	// JA2 doesn't have this problem, 'cause they use DirectDraw calls that change the Windows resolution properly.
-	//
-	// Alex Meduna, Dec. 3, 1997
-
-#if 1
-	FIXME
-	SDL_WarpMouse(uiNewXPos, uiNewYPos);
-#else
-	// Adjust coords based on our resolution
-	FLOAT flNewXPos = (FLOAT)uiNewXPos / SCREEN_WIDTH  * 65536;
-	FLOAT flNewYPos = (FLOAT)uiNewYPos / SCREEN_HEIGHT * 65536;
+	int windowWidth, windowHeight;
+	SDL_GetWindowSize(GAME_WINDOW, &windowWidth, &windowHeight);
 
-	mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, (UINT32)flNewXPos, (UINT32)flNewYPos, 0, 0);
-#endif
+	double windowWidthD = windowWidth;
+	double windowHeightD = windowHeight;
+	double screenWidthD = SCREEN_WIDTH;
+	double screenHeightD = SCREEN_HEIGHT;
+
+	double scaleFactorX = windowWidthD / screenWidthD;
+	double scaleFactorY = windowHeightD / screenHeightD;
+	double scaleFactor = windowWidth > windowHeight ? scaleFactorY : scaleFactorX;
+
+	double scaledWindowWidth = scaleFactor * screenWidthD;
+	double scaledWindowHeight = scaleFactor * screenHeightD;
+
+	double paddingX = (windowWidthD - scaledWindowWidth) / 2.0;
+	double paddingY = (windowHeight - scaledWindowHeight) / 2.0;
+	int windowPositionX = paddingX + (double)uiNewXPos * scaledWindowWidth / screenWidthD;
+	int windowPositionY = paddingY + (double)uiNewYPos * scaledWindowHeight / screenHeightD;
+
+	SDL_WarpMouseInWindow(GAME_WINDOW, windowPositionX, windowPositionY);
 }
 
 
@@ -465,4 +524,18 @@ static void HandleSingleClicksAndButtonRepeats(void)
 	{
 		guiRightButtonRepeatTimer = 0;
 	}
+
+	// Is there a MIDDLE mouse button repeat
+	if (gfMiddleButtonState)
+	{
+		if ((guiMiddleButtonRepeatTimer > 0)&&(guiMiddleButtonRepeatTimer <= uiTimer))
+		{
+			QueueMouseEvent(MIDDLE_BUTTON_REPEAT);
+			guiMiddleButtonRepeatTimer = uiTimer + BUTTON_REPEAT_TIME;
+		}
+	}
+	else
+	{
+		guiMiddleButtonRepeatTimer = 0;
+	}
 }
diff --git a/sgp/Input.h b/sgp/Input.h
index 84d7cd1..0da0517 100644
--- a/sgp/Input.h
+++ b/sgp/Input.h
@@ -8,6 +8,7 @@
 #define KEY_DOWN									0x0001
 #define KEY_UP										0x0002
 #define KEY_REPEAT								0x0004
+#define TEXT_INPUT								0x0006
 #define LEFT_BUTTON_DOWN					0x0008
 #define LEFT_BUTTON_UP						0x0010
 #define LEFT_BUTTON_DBL_CLK				0x0020
@@ -18,7 +19,11 @@
 #define MOUSE_POS									0x0400
 #define MOUSE_WHEEL_UP      0x0800
 #define MOUSE_WHEEL_DOWN    0x1000
-#define MOUSE_EVENTS        0x1FF8
+#define MOUSE_EVENTS        0xFFF8
+
+#define MIDDLE_BUTTON_DOWN		0x2000
+#define MIDDLE_BUTTON_UP		0x4000
+#define MIDDLE_BUTTON_REPEAT	0x8000
 
 #define SHIFT_DOWN								0x01
 #define CTRL_DOWN									0x02
@@ -41,9 +46,11 @@ extern BOOLEAN			DequeueEvent(InputAtom *Event);
 
 void MouseButtonDown(const SDL_MouseButtonEvent*);
 void MouseButtonUp(const SDL_MouseButtonEvent*);
+void MouseWheelScroll(const SDL_MouseWheelEvent*);
 
-void KeyDown(const SDL_keysym*);
-void KeyUp(  const SDL_keysym*);
+void KeyDown(const SDL_Keysym*);
+void KeyUp(  const SDL_Keysym*);
+void TextInput(  const SDL_TextInputEvent*);
 
 extern void					GetMousePos(SGPPoint *Point);
 
@@ -51,10 +58,10 @@ extern BOOLEAN DequeueSpecificEvent(InputAtom *Event, UINT32 uiMaskFlags );
 
 extern void					RestrictMouseToXYXY(UINT16 usX1, UINT16 usY1, UINT16 usX2, UINT16 usY2);
 void RestrictMouseCursor(const SGPRect* pRectangle);
+extern void					SetSafeMousePosition(int x, int y);
 extern void					FreeMouseCursor(void);
 extern BOOLEAN			IsCursorRestricted( void );
 extern void					GetRestrictedClipCursor( SGPRect *pRectangle );
-extern void         RestoreCursorClipRect( void );
 
 
 void SimulateMouseMovement( UINT32 uiNewXPos, UINT32 uiNewYPos );
@@ -62,16 +69,15 @@ void SimulateMouseMovement( UINT32 uiNewXPos, UINT32 uiNewYPos );
 void DequeueAllKeyBoardEvents(void);
 
 
-extern BOOLEAN gfKeyState[SDLK_LAST]; // TRUE = Pressed, FALSE = Not Pressed
-
 extern UINT16    gusMouseXPos;       // X position of the mouse on screen
 extern UINT16    gusMouseYPos;       // y position of the mouse on screen
 extern BOOLEAN   gfLeftButtonState;  // TRUE = Pressed, FALSE = Not Pressed
 extern BOOLEAN   gfRightButtonState; // TRUE = Pressed, FALSE = Not Pressed
+extern BOOLEAN   gfMiddleButtonState;
 
-
-#define _KeyDown(a)        gfKeyState[(a)]
+bool _KeyDown(SDL_Keycode);
 #define _LeftButtonDown    gfLeftButtonState
 #define _RightButtonDown   gfRightButtonState
+#define _MiddleButtonDown   gfMiddleButtonState
 
 #endif
diff --git a/sgp/PCX.cc b/sgp/PCX.cc
index cf1719b..bfea6df 100644
--- a/sgp/PCX.cc
+++ b/sgp/PCX.cc
@@ -75,7 +75,7 @@ SGPImage* LoadPCXFileToImage(char const* const filename, UINT16 const contents)
 			dst[i].r      = palette[i * 3 + 0];
 			dst[i].g      = palette[i * 3 + 1];
 			dst[i].b      = palette[i * 3 + 2];
-			dst[i].unused = 0;
+			dst[i].a      = 0;
 		}
 		img->pui16BPPPalette = Create16BPPPalette(dst);
 	}
diff --git a/sgp/Platform.h b/sgp/Platform.h
index 263f9b1..c232813 100644
--- a/sgp/Platform.h
+++ b/sgp/Platform.h
@@ -43,11 +43,9 @@
   #define __func__ __FUNCTION__
 #endif
 
-#if !defined(_WIN32)
-  /* Not Visual Studio, not MINGW */
-  #define __max(a, b) ((a) > (b) ? (a) : (b))
-  #define __min(a, b) ((a) < (b) ? (a) : (b))
-#endif
+/* Not Visual Studio, not MINGW */
+#define __max(a, b) ((a) > (b) ? (a) : (b))
+#define __min(a, b) ((a) < (b) ? (a) : (b))
 
 /**************************************************************
  *
diff --git a/sgp/SGP.cc b/sgp/SGP.cc
index 7b9b710..1c43c4c 100644
--- a/sgp/SGP.cc
+++ b/sgp/SGP.cc
@@ -212,28 +212,28 @@ static void MainLoop(int msPerGameCycle)
 		{
 			switch (event.type)
 			{
-				case SDL_ACTIVEEVENT:
-					if (event.active.state & SDL_APPACTIVE)
-					{
-						s_doGameCycles = (event.active.gain != 0);
-						break;
-					}
+				case SDL_APP_WILLENTERBACKGROUND:
+					s_doGameCycles = false;
+					break;
+
+				case SDL_APP_WILLENTERFOREGROUND:
+					s_doGameCycles = true;
 					break;
 
 				case SDL_KEYDOWN: KeyDown(&event.key.keysym); break;
 				case SDL_KEYUP:   KeyUp(  &event.key.keysym); break;
+				case SDL_TEXTINPUT: TextInput(&event.text); break;
 
 				case SDL_MOUSEBUTTONDOWN: MouseButtonDown(&event.button); break;
 				case SDL_MOUSEBUTTONUP:   MouseButtonUp(&event.button);   break;
 
 				case SDL_MOUSEMOTION:
-					gusMouseXPos = event.motion.x;
-					gusMouseYPos = event.motion.y;
+					SetSafeMousePosition(event.motion.x, event.motion.y);
 					break;
 
-				case SDL_QUIT:
-          deinitGameAndExit();
-					break;
+				case SDL_MOUSEWHEEL: MouseWheelScroll(&event.wheel); break;
+
+				case SDL_QUIT: deinitGameAndExit(); break;
 			}
 		}
 		else
@@ -358,7 +358,6 @@ try
   ////////////////////////////////////////////////////////////
 
 	SDL_Init(SDL_INIT_VIDEO);
-	SDL_EnableUNICODE(SDL_ENABLE);
 
 #ifdef __APPLE__
   // Enable 3-button mouse support if the user haven't instructed
diff --git a/sgp/STCI.cc b/sgp/STCI.cc
index c5d958e..bc200f7 100644
--- a/sgp/STCI.cc
+++ b/sgp/STCI.cc
@@ -113,7 +113,7 @@ static SGPImage* STCILoadIndexed(UINT16 const contents, HWFILE const f, STCIHead
 			palette[i].r      = pSTCIPalette[i].ubRed;
 			palette[i].g      = pSTCIPalette[i].ubGreen;
 			palette[i].b      = pSTCIPalette[i].ubBlue;
-			palette[i].unused = 0;
+			palette[i].a      = 0;
 		}
 
 		img->fFlags |= IMAGE_PALETTE;
diff --git a/sgp/SoundMan.cc b/sgp/SoundMan.cc
index e37d632..aff2980 100644
--- a/sgp/SoundMan.cc
+++ b/sgp/SoundMan.cc
@@ -14,22 +14,18 @@
 #include "Timer.h"
 #include <SDL.h>
 #include <assert.h>
+#include <stdexcept>
 
 #include "ContentManager.h"
 #include "GameInstance.h"
+#include "slog/slog.h"
 
+#define DEBUG_TAG_SOUND "Sound"
+#define DEBUG_TAG_ASSERTS "Asserts"
 
 // Uncomment this to disable the startup of sound hardware
 //#define SOUND_DISABLE
 
-
-#ifdef WITH_SOUND_DEBUG
-#	define SNDDBG(fmt, ...) (void)fprintf(stderr, ">>>> SND: " fmt, __VA_ARGS__)
-#else
-#	define SNDDBG(fmt, ...) (void)0
-#endif
-
-
 /*
  * from\to FREE PLAY STOP DEAD
  *    FREE       M
@@ -59,25 +55,17 @@ enum
 	SAMPLE_ALLOCATED = 1U << 0,
 	SAMPLE_LOCKED    = 1U << 1,
 	SAMPLE_RANDOM    = 1U << 2,
-	SAMPLE_STEREO    = 1U << 3,
-	SAMPLE_16BIT     = 1U << 4
+	SAMPLE_STEREO    = 1U << 3
 };
 
 
 #define SOUND_MAX_CACHED 128 // number of cache slots
+#define SOUND_MAX_CHANNELS 16 // number of mixer channels
 
-#ifdef JA2
-#	define SOUND_MAX_CHANNELS 16 // number of mixer channels
-#else
-#	define SOUND_MAX_CHANNELS 32 // number of mixer channels
-#endif
-
-
-#define SOUND_DEFAULT_MEMORY (16 * 1024 * 1024) // default memory limit
+#define SOUND_DEFAULT_MEMORY (32 * 1024 * 1024) // default memory limit
 #define SOUND_DEFAULT_THRESH ( 2 * 1024 * 1024) // size for sample to be double-buffered
 #define SOUND_DEFAULT_STREAM (64 * 1024)        // double-buffered buffer size
 
-
 // Struct definition for sample slots in the cache
 // Holds the regular sample data, as well as the data for the random samples
 struct SAMPLETAG
@@ -85,7 +73,6 @@ struct SAMPLETAG
 	CHAR8   pName[128];  // Path to sample data
 	UINT32  n_samples;
 	UINT32  uiFlags;     // Status flags
-	UINT32  uiSpeed;     // Playback frequency
 	PTR     pData;       // pointer to sample data memory
 	UINT32  uiCacheHits;
 
@@ -121,15 +108,19 @@ struct SOUNDTAG
 	UINT32        Pan;
 };
 
-
+static size_t GetSampleSize(const SAMPLETAG* const s);
 static const UINT32 guiSoundDefaultVolume  = MAXVOLUME;
 static const UINT32 guiSoundMemoryLimit    = SOUND_DEFAULT_MEMORY; // Maximum memory used for sounds
 static       UINT32 guiSoundMemoryUsed     = 0;                    // Memory currently in use
 static const UINT32 guiSoundCacheThreshold = SOUND_DEFAULT_THRESH; // Double-buffered threshold
+static void IncreaseSoundMemoryUsedBySample(SAMPLETAG *sample) { guiSoundMemoryUsed += sample->n_samples * GetSampleSize(sample); }
+static void DecreaseSoundMemoryUsedBySample(SAMPLETAG *sample) { guiSoundMemoryUsed -= sample->n_samples * GetSampleSize(sample); }
 
 static BOOLEAN fSoundSystemInit = FALSE; // Startup called
 static BOOLEAN gfEnableStartup  = TRUE;  // Allow hardware to start up
 
+SDL_AudioSpec gTargetAudioSpec;
+
 // Sample cache list for files loaded
 static SAMPLETAG pSampleList[SOUND_MAX_CACHED];
 // Sound channel list for output channels
@@ -188,13 +179,7 @@ UINT32 SoundPlay(const char* pFilename, UINT32 volume, UINT32 pan, UINT32 loop,
 	if (SoundPlayStreamed(pFilename))
 	{
 		//Trying to play a sound which is bigger then the 'guiSoundCacheThreshold'
-
-		// This line was causing a page fault in the Wiz 8 project, so
-		// I changed it to the second line, which works OK. -- DB
-
-		//DebugMsg(TOPIC_JA2, DBG_LEVEL_3, String("\n*******\nSoundPlay():  ERROR:  trying to play %s which is bigger then the 'guiSoundCacheThreshold', use SoundPlayStreamedFile() instead\n", pFilename));
-
-		FastDebugMsg(String("SoundPlay: ERROR: Trying to play %s sound is too lardge to load into cache, use SoundPlayStreamedFile() instead\n", pFilename));
+		SLOGE(DEBUG_TAG_SOUND, "Trying to play %s sound is too large to load into cache, use SoundPlayStreamedFile() instead", pFilename));
 		return SOUND_ERROR;
 	}
 #endif
@@ -211,7 +196,6 @@ UINT32 SoundPlay(const char* pFilename, UINT32 volume, UINT32 pan, UINT32 loop,
 static SAMPLETAG* SoundGetEmptySample(void);
 static BOOLEAN    SoundCleanCache(void);
 static SAMPLETAG* SoundGetEmptySample(void);
-static size_t GetSampleSize(const SAMPLETAG* const s);
 
 UINT32 SoundPlayFromBuffer(INT16* pbuffer, UINT32 size, UINT32 volume, UINT32 pan, UINT32 loop, void (*end_callback)(void*), void* data)
 {
@@ -223,14 +207,13 @@ UINT32 SoundPlayFromBuffer(INT16* pbuffer, UINT32 size, UINT32 volume, UINT32 pa
       SoundCleanCache();
       buffertag = SoundGetEmptySample();
     }
-  sprintf(buffertag->pName, "SmackBuff %p - SampleSize %u", pbuffer, size); 
-  buffertag->uiSpeed=22050;
+  sprintf(buffertag->pName, "SmackBuff %p - SampleSize %u", pbuffer, size);
   buffertag->n_samples = size;
   buffertag->pData = pbuffer;
-  buffertag->uiFlags =  SAMPLE_16BIT | SAMPLE_STEREO | SAMPLE_ALLOCATED;
+  buffertag->uiFlags =  SAMPLE_STEREO | SAMPLE_ALLOCATED;
   buffertag->uiPanMax        = 64;
   buffertag->uiMaxInstances  = 1;
-  guiSoundMemoryUsed += size * GetSampleSize(buffertag);
+  IncreaseSoundMemoryUsedBySample(buffertag);
 
   SOUNDTAG* const channel = SoundGetFreeChannel();
   if (channel == NULL) return SOUND_ERROR;
@@ -263,7 +246,7 @@ try
 	FILE* hRealFileHandle = GetRealFileHandleFromFileManFileHandle(hFile);
 	if (hRealFileHandle == NULL)
 	{
-		FastDebugMsg(String("\n*******\nSoundPlayStreamedFile():  ERROR:  Couldnt get a real file handle for '%s' in SoundPlayStreamedFile()\n", pFilename ) );
+		SLOGE(DEBUG_TAG_SOUND, "SoundPlayStreamedFile(): Couldnt get a real file handle for '%s' in SoundPlayStreamedFile()", pFilename );
 		return SOUND_ERROR;
 	}
 
@@ -285,14 +268,14 @@ try
 }
 catch (...)
 {
-	FastDebugMsg(String("\n*******\nSoundPlayStreamedFile():  ERROR:  Failed to play '%s'\n", pFilename));
+	SLOGE(DEBUG_TAG_SOUND, "SoundPlayStreamedFile(): Failed to play '%s'", pFilename);
 	return SOUND_ERROR;
 }
 
 
 UINT32 SoundPlayRandom(const char* pFilename, UINT32 time_min, UINT32 time_max, UINT32 vol_min, UINT32 vol_max, UINT32 pan_min, UINT32 pan_max, UINT32 max_instances)
 {
-	SNDDBG("RAND \"%s\"\n", pFilename);
+	SLOGD(DEBUG_TAG_SOUND, "playing random Sound: \"%s\"", pFilename);
 
 	if (!fSoundSystemInit) return SOUND_ERROR;
 
@@ -479,7 +462,7 @@ void SoundServiceStreams(void)
 		SOUNDTAG* Sound = &pSoundList[i];
 		if (Sound->State == CHANNEL_DEAD)
 		{
-			SNDDBG("DEAD channel %u file \"%s\" (refcount %u)\n", i, Sound->pSample->pName, Sound->pSample->uiInstances);
+			SLOGD(DEBUG_TAG_SOUND, "DEAD channel %u file \"%s\" (refcount %u)", i, Sound->pSample->pName, Sound->pSample->uiInstances);
 			if (Sound->EOSCallback != NULL) Sound->EOSCallback(Sound->pCallbackData);
 			assert(Sound->pSample->uiInstances != 0);
 			Sound->pSample->uiInstances--;
@@ -556,274 +539,37 @@ static SAMPLETAG* SoundGetCached(const char* pFilename)
 
 static size_t GetSampleSize(const SAMPLETAG* const s)
 {
-	return
-		(s->uiFlags & SAMPLE_16BIT  ? 2 : 1) *
-		(s->uiFlags & SAMPLE_STEREO ? 2 : 1);
-}
-
-
-static BOOLEAN HalfSampleRate(SAMPLETAG* const s)
-{
-	SNDDBG("SMPL \"%s\" from %uHz to %uHz\n", s->pName, s->uiSpeed, s->uiSpeed / 2);
-
-	UINT32 const n_samples = s->n_samples / 2;
-	void*  const ndata     = malloc(n_samples * GetSampleSize(s));
-	if (ndata == NULL) return FALSE;
-	void*  const odata     = s->pData;
-	if (s->uiFlags & SAMPLE_16BIT)
-	{
-		INT16*       const dst = (INT16*)ndata;
-		const INT16* const src = (const INT16*)odata;
-		if (s->uiFlags & SAMPLE_STEREO)
-		{
-			for (size_t i = 0; i < n_samples; ++i)
-			{
-				dst[2 * i + 0] = (src[4 * i + 0] + src[4 * i + 2]) / 2;
-				dst[2 * i + 1] = (src[4 * i + 1] + src[4 * i + 3]) / 2;
-			}
-		}
-		else
-		{
-			for (size_t i = 0; i < n_samples; ++i)
-			{
-				dst[i] = (src[2 * i] + src[2 * i + 1]) / 2;
-			}
-		}
-	}
-	else
-	{
-		UINT8*       const dst = (UINT8*)ndata;
-		const UINT8* const src = (const UINT8*)odata;
-		if (s->uiFlags & SAMPLE_STEREO)
-		{
-			for (size_t i = 0; i < n_samples; ++i)
-			{
-				dst[2 * i + 0] = (src[4 * i + 0] + src[4 * i + 2]) / 2;
-				dst[2 * i + 1] = (src[4 * i + 1] + src[4 * i + 3]) / 2;
-			}
-		}
-		else
-		{
-			for (size_t i = 0; i < n_samples; ++i)
-			{
-				dst[i] = (src[2 * i] + src[2 * i + 1]) / 2;
-			}
-		}
-	}
-	s->pData = ndata;
-	free(odata);
-
-	s->n_samples  = n_samples;
-	s->uiSpeed   /= 2;
-	return TRUE;
-}
-
-static BOOLEAN DoubleSampleRate(SAMPLETAG* const s)
-{
-	UINT8 bitcount = s->uiFlags & SAMPLE_16BIT ? 16 : 8;
-
-	SNDDBG("SMPL \"%s\" %dbit from %uHz to %uHz\n", s->pName, bitcount, s->uiSpeed, s->uiSpeed * 2);
-
-	UINT32 const n_samples = s->n_samples * 2;
-	void*  const ndata     = malloc(n_samples * GetSampleSize(s));
-	if (ndata == NULL) return FALSE;
-	void*  const odata     = s->pData;
-	if (bitcount == 16)
-	{
-		INT16*       const dst = (INT16*)ndata;
-		const INT16* const src = (const INT16*)odata;
-		if (s->uiFlags & SAMPLE_STEREO)
-		{
-			for (size_t i = 0; i < s->n_samples; ++i)
-			{
-				INT16 i1c1 = src[2 * i + 0];
-				INT16 i1c2 = src[2 * i + 1];
-				INT16 i2c1 = i != s->n_samples-1 ? src[2 * i + 2] : i1c1;
-				INT16 i2c2 = i != s->n_samples-1 ? src[2 * i + 3] : i1c2;
-
-				dst[4 * i + 0] = i1c1;
-				dst[4 * i + 1] = i1c2;
-				dst[4 * i + 2] = (i1c1 + i2c1) / 2;
-				dst[4 * i + 3] = (i1c2 + i2c2) / 2;
-			}
-		}
-		else
-		{
-			for (size_t i = 0; i < s->n_samples; ++i)
-			{
-				INT16 i1 = src[i];
-				INT16 i2 = i != s->n_samples-1 ? src[i+1] : i1;
-				dst[i*2] = i1;
-				dst[i*2+1] = (i1 + i2) / 2;
-			}
-		}
-	}
-	else
-	{
-		UINT8*       const dst = (UINT8*)ndata;
-		const UINT8* const src = (const UINT8*)odata;
-		if (s->uiFlags & SAMPLE_STEREO)
-		{
-			for (size_t i = 0; i < s->n_samples; ++i)
-			{
-				UINT8 i1c1 = src[2 * i + 0];
-				UINT8 i1c2 = src[2 * i + 1];
-				UINT8 i2c1 = i != s->n_samples-1 ? src[2 * i + 2] : i1c1;
-				UINT8 i2c2 = i != s->n_samples-1 ? src[2 * i + 3] : i1c2;
-
-				dst[4 * i + 0] = i1c1;
-				dst[4 * i + 1] = i1c2;
-				dst[4 * i + 2] = (i1c1 + i2c1) / 2;
-				dst[4 * i + 3] = (i1c2 + i2c2) / 2;
-			}
-		}
-		else
-		{
-			for (size_t i = 0; i < s->n_samples; ++i)
-			{
-				UINT8 i1 = src[i];
-				UINT8 i2 = i != s->n_samples-1 ? src[i+1] : i1;
-				dst[i*2] = i1;
-				dst[i*2+1] = (i1 + i2) / 2;
-			}
-		}
-	}
-	s->pData = ndata;
-	free(odata);
-
-	s->n_samples  = n_samples;
-	s->uiSpeed   *= 2;
-	return TRUE;
-}
-
-
-#define FOURCC(a, b, c, d) ((UINT8)(d) << 24 | (UINT8)(c) << 16 | (UINT8)(b) << 8 | (UINT8)(a))
-
-
-enum WaveFormatTag
-{
-	WAVE_FORMAT_UNKNOWN   = 0x0000,
-	WAVE_FORMAT_PCM       = 0x0001,
-	WAVE_FORMAT_DVI_ADPCM = 0x0011
-};
-
-
-static void LoadPCM(SAMPLETAG* const s, HWFILE const file, UINT32 const size)
-{
-	SGP::Buffer<UINT8> data(size);
-	FileRead(file, data, size);
-
-	s->n_samples = size / GetSampleSize(s);
-	s->pData     = data.Release();
-}
-
-
-static inline int Clamp(int min, int x, int max)
-{
-	if (x < min) return min;
-	if (x > max) return max;
-	return x;
+	return 2u * (s->uiFlags & SAMPLE_STEREO ? 2 : 1);
 }
 
-
-static void LoadDVIADPCM(SAMPLETAG* const s, HWFILE const file, UINT16 const block_align)
-{
-	s->uiFlags |= SAMPLE_16BIT;
-
-	size_t       CountSamples = s->n_samples;
-	INT16* const Data         = (INT16*)malloc(CountSamples * GetSampleSize(s));
-	INT16*       D            = Data;
-
-	for (;;)
-	{
-		INT16 CurSample_;
-		FileRead(file, &CurSample_, sizeof(CurSample_));
-
-		UINT8 StepIndex_;
-		FileRead(file, &StepIndex_, sizeof(StepIndex_));
-
-		FileSeek(file, 1 , FILE_SEEK_FROM_CURRENT); // reserved byte
-
-		INT32 CurSample = CurSample_;
-		INT32 StepIndex = StepIndex_;
-
-		*D++ = CurSample;
-		if (--CountSamples == 0)
-		{
-			s->pData  = Data;
-			return;
-		}
-
-		UINT DataCount = block_align / 4;
-		while (--DataCount != 0)
-		{
-			UINT32 DataWord;
-			FileRead(file, &DataWord, sizeof(DataWord));
-			for (UINT i = 0; i < 8; i++)
-			{
-				static const INT16 StepTable[] =
-				{
-							7,     8,     9,    10,    11,    12,    13,    14,
-						 16,    17,    19,    21,    23,    25,    28,    31,
-						 34,    37,    41,    45,    50,    55,    60,    66,
-						 73,    80,    88,    97,   107,   118,   130,   143,
-						157,   173,   190,   209,   230,   253,   279,   307,
-						337,   371,   408,   449,   494,   544,   598,   658,
-						724,   796,   876,   963,  1060,  1166,  1282,  1411,
-					 1552,  1707,  1878,  2066,  2272,  2499,  2749,  3024,
-					 3327,  3660,  4026,  4428,  4871,  5358,  5894,  6484,
-					 7132,  7845,  8630,  9493, 10442, 11487, 12635, 13899,
-					15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
-					32767
-				};
-
-				static const INT8 IndexTable[] =
-				{
-					-1, -1, -1, -1, 2, 4, 6, 8
-				};
-
-#if 1
-				INT32 Diff = ((DataWord & 7) * 2 + 1) * StepTable[StepIndex] >> 3;
-#else
-				INT32 Diff = 0;
-				if (DataWord & 4) Diff += StepTable[StepIndex];
-				if (DataWord & 2) Diff += StepTable[StepIndex] >> 1;
-				if (DataWord & 1) Diff += StepTable[StepIndex] >> 2;
-				Diff += StepTable[StepIndex] >> 3;
-#endif
-				if (DataWord & 8) Diff = -Diff;
-				CurSample = Clamp(-32768, CurSample + Diff, 32767);
-				StepIndex = Clamp(0, StepIndex + IndexTable[DataWord & 7], 88);
-				DataWord >>= 4;
-
-				*D++ = CurSample;
-				if (--CountSamples == 0)
-				{
-					s->pData  = Data;
-					return;
-				}
-			}
-		}
-	}
-}
-
-
-
-
-
 /* Loads a sound file from disk into the cache, allocating memory and a slot
  * for storage.
  *
  * Returns: The sample index if successful, NO_SAMPLE if the file wasn't found
  *          in the cache. */
 static SAMPLETAG* SoundLoadDisk(const char* pFilename)
-try
 {
 	Assert(pFilename != NULL);
 
-	AutoSGPFile hFile(GCM->openGameResForReading(pFilename));
+	if(pFilename[0] == '\0') {
+		SLOGE(DEBUG_TAG_ASSERTS, "SoundLoadDisk Error: pFilename is an empty string.");
+		return NULL;
+	}
 
-	UINT32 uiSize = FileGetSize(hFile);
+	AutoSGPFile hFile;
+
+	try
+	{
+		hFile = GCM->openGameResForReading(pFilename);
+	}
+	catch (const std::runtime_error& err)
+	{
+		SLOGE(DEBUG_TAG_ASSERTS, "SoundLoadDisk Error: %s", err.what());
+		return NULL;
+	}
+
+	// A pessimistic approach as we dont know the decoded size yet
+	UINT32 uiSize = FileGetSize(hFile) * 2;
 
 	// if insufficient memory, start unloading old samples until either
 	// there's nothing left to unload, or we fit
@@ -831,8 +577,8 @@ try
 	{
 		if (!SoundCleanCache())
 		{
-			SNDDBG("Not enough memory. Size: %u, Used: %u, Max: %u\n", uiSize, guiSoundMemoryUsed, guiSoundMemoryLimit);
-			FastDebugMsg(String("SoundLoadDisk:  ERROR:  trying to play %s, not enough memory\n", pFilename));
+			SLOGE(DEBUG_TAG_SOUND, "SoundLoadDisk: trying to play %s, not enough memory\nSize: %u, Used: %u, Max: %u",
+				pFilename, uiSize, guiSoundMemoryUsed, guiSoundMemoryLimit);
 			return NULL;
 		}
 	}
@@ -848,107 +594,63 @@ try
 	// if we still don't have a sample slot
 	if (s == NULL)
 	{
-		FastDebugMsg(String("SoundLoadDisk:  ERROR: Trying to play %s, sound channels are full\n", pFilename));
+		SLOGE(DEBUG_TAG_SOUND, "SoundLoadDisk: Trying to play %s, sound channels are full", pFilename);
 		return NULL;
 	}
 
 	memset(s, 0, sizeof(*s));
 
-	FileSeek(hFile, 12, FILE_SEEK_FROM_CURRENT);
-
-	UINT16 FormatTag = WAVE_FORMAT_UNKNOWN;
-	UINT16 BlockAlign = 0;
-	for (;;)
-	{
-		UINT32 Tag;
-		UINT32 Size;
-
-		FileRead(hFile, &Tag,  sizeof(Tag));
-		FileRead(hFile, &Size, sizeof(Size));
+	SDL_RWops* rwOps = FileGetRWOps(hFile);
+	SDL_AudioSpec wavSpec;
+	Uint32 wavLength;
+	Uint8 *wavBuffer;
+	SDL_AudioCVT cvt;
 
-		switch (Tag)
-		{
-			case FOURCC('f', 'm', 't', ' '):
-				{
-					UINT16 Channels;
-					UINT32 Rate;
-					UINT16 BitsPerSample;
-
-					FileRead(hFile, &FormatTag,     sizeof(FormatTag));
-					FileRead(hFile, &Channels,      sizeof(Channels));
-					FileRead(hFile, &Rate,          sizeof(Rate));
-					FileSeek(hFile, 4 , FILE_SEEK_FROM_CURRENT);
-					FileRead(hFile, &BlockAlign,    sizeof(BlockAlign));
-					FileRead(hFile, &BitsPerSample, sizeof(BitsPerSample));
-					SNDDBG("LOAD file \"%s\" format %u channels %u rate %u bits %u to slot %u\n", pFilename, FormatTag, Channels, Rate, BitsPerSample, s - pSampleList);
-					switch (FormatTag)
-					{
-						case WAVE_FORMAT_PCM: break;
+	if (SDL_LoadWAV_RW(rwOps, 0,  &wavSpec, &wavBuffer, &wavLength) == NULL) {
+		SLOGE(DEBUG_TAG_SOUND, "Error loading sound file: %s", SDL_GetError());
+		return NULL;
+	}
 
-						case WAVE_FORMAT_DVI_ADPCM:
-							FileSeek(hFile, 4 , FILE_SEEK_FROM_CURRENT);
-							break;
+	SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq, gTargetAudioSpec.format, wavSpec.channels, gTargetAudioSpec.freq);
+	cvt.len = wavLength;
+	cvt.buf = MALLOCN(UINT8, cvt.len * cvt.len_mult);
+	memcpy(cvt.buf, wavBuffer, wavLength);
+	SDL_FreeWAV(wavBuffer);
+	SDL_FreeRW(rwOps);
 
-						default: return NULL;
-					}
+	if (cvt.needed) {
+		if (SDL_ConvertAudio(&cvt) != 0) {
+			SLOGE(DEBUG_TAG_SOUND, "Error converting sound file: %s", SDL_GetError());
+			return NULL;
+		};
+	}
 
-					s->uiSpeed = Rate;
-					if (Channels      !=  1) s->uiFlags |= SAMPLE_STEREO;
-					if (BitsPerSample == 16) s->uiFlags |= SAMPLE_16BIT;
-					break;
-				}
+	UINT32 convertedSize = cvt.len * cvt.len_ratio;
 
-			case FOURCC('f', 'a', 'c', 't'):
-				{
-					UINT32 Samples;
-					FileRead(hFile, &Samples, sizeof(Samples));
-					s->n_samples = Samples;
-					break;
-				}
+	strcpy(s->pName, pFilename);
+	s->n_samples = UINT32(convertedSize / (wavSpec.channels * 2));
+	s->uiFlags     |= SAMPLE_ALLOCATED;
+	if (wavSpec.channels != 1) {
+		s->uiFlags |= SAMPLE_STEREO;
+	}
 
-			case FOURCC('d', 'a', 't', 'a'):
-				{
-					switch (FormatTag)
-					{
-						case WAVE_FORMAT_PCM:
-							LoadPCM(s, hFile, Size);
-							goto sound_loaded;
+	s->uiInstances  = 0;
+	s->pData = MALLOCN(UINT8, convertedSize);
+	memcpy(s->pData, cvt.buf, convertedSize);
 
-						case WAVE_FORMAT_DVI_ADPCM:
-							LoadDVIADPCM(s, hFile, BlockAlign);
-							goto sound_loaded;
+	free(cvt.buf);
 
-						default: return NULL;
-					}
-				}
-
-			default:
-				FileSeek(hFile, Size, FILE_SEEK_FROM_CURRENT);
-				break;
-		}
-	}
+	IncreaseSoundMemoryUsedBySample(s);
 
-sound_loaded:
-	strcpy(s->pName, pFilename);
-	if (s->uiSpeed == 44100 && !HalfSampleRate(s))
-	{
-		free(s->pData);
-		return NULL;
-	}
-	if (s->uiSpeed == 11025 && !DoubleSampleRate(s))
-	{
-		free(s->pData);
-		return NULL;
-	}
-	guiSoundMemoryUsed += s->n_samples * GetSampleSize(s);
-	s->uiFlags     |= SAMPLE_ALLOCATED;
-	s->uiInstances  = 0;
 	return s;
 }
-catch (...) { return 0; }
 
 
-static BOOLEAN SoundSampleIsPlaying(const SAMPLETAG* s);
+// Returns TRUE/FALSE that a sample is currently in use for playing a sound.
+static BOOLEAN SoundSampleIsPlaying(const SAMPLETAG* s)
+{
+	return s->uiInstances > 0;
+}
 
 
 /* Removes the least-used sound from the cache to make room.
@@ -970,7 +672,7 @@ static BOOLEAN SoundCleanCache(void)
 
 	if (candidate != NULL)
 	{
-		SNDDBG("FREE sample %u \"%s\" with %u hits\n", candidate - pSampleList, candidate->pName, candidate->uiCacheHits);
+		SLOGD(DEBUG_TAG_SOUND, "freeing sample %u \"%s\" with %u hits", candidate - pSampleList, candidate->pName, candidate->uiCacheHits);
 		SoundFreeSample(candidate);
 		return TRUE;
 	}
@@ -979,13 +681,6 @@ static BOOLEAN SoundCleanCache(void)
 }
 
 
-// Returns TRUE/FALSE that a sample is currently in use for playing a sound.
-static BOOLEAN SoundSampleIsPlaying(const SAMPLETAG* s)
-{
-	return s->uiInstances > 0;
-}
-
-
 /* Returns an available sample.
  *
  * Returns: A free sample or NULL if none are left. */
@@ -1007,8 +702,8 @@ static void SoundFreeSample(SAMPLETAG* s)
 
 	assert(s->uiInstances == 0);
 
-	guiSoundMemoryUsed -= s->n_samples * GetSampleSize(s);
-	free(s->pData);
+	DecreaseSoundMemoryUsedBySample(s);
+	MemFree(s->pData);
 	memset(s, 0, sizeof(*s));
 }
 
@@ -1030,6 +725,8 @@ static SOUNDTAG* SoundGetChannelByID(UINT32 uiSoundID)
 
 static void SoundCallback(void* userdata, Uint8* stream, int len)
 {
+	SDL_memset(stream, 0, len);
+
 	UINT16* Stream = (UINT16*)stream;
 
 	// XXX TODO proper mixing, mainly clipping
@@ -1058,48 +755,23 @@ static void SoundCallback(void* userdata, Uint8* stream, int len)
 
 mixing:
 				amount = MIN(samples, s->n_samples - Sound->pos);
-				if (s->uiFlags & SAMPLE_16BIT)
+				if (s->uiFlags & SAMPLE_STEREO)
 				{
-					if (s->uiFlags & SAMPLE_STEREO)
+					const INT16* const src = (const INT16*)s->pData + Sound->pos * 2;
+					for (UINT32 i = 0; i < amount; ++i)
 					{
-						const INT16* const src = (const INT16*)s->pData + Sound->pos * 2;
-						for (UINT32 i = 0; i < amount; ++i)
-						{
-							Stream[2 * i + 0] += src[2 * i + 0] * vol_l >> 7;
-							Stream[2 * i + 1] += src[2 * i + 1] * vol_r >> 7;
-						}
-					}
-					else
-					{
-						const INT16* const src = (const INT16*)s->pData + Sound->pos;
-						for (UINT32 i = 0; i < amount; i++)
-						{
-							const INT data = src[i];
-							Stream[2 * i + 0] += data * vol_l >> 7;
-							Stream[2 * i + 1] += data * vol_r >> 7;
-						}
+						Stream[2 * i + 0] += src[2 * i + 0] * vol_l >> 7;
+						Stream[2 * i + 1] += src[2 * i + 1] * vol_r >> 7;
 					}
 				}
 				else
 				{
-					if (s->uiFlags & SAMPLE_STEREO)
-					{
-						const UINT8* const src = (const UINT8*)s->pData + Sound->pos * 2;
-						for (UINT32 i = 0; i < amount; ++i)
-						{
-							Stream[2 * i + 0] += (src[2 * i + 0] - 128) * vol_l << 1;
-							Stream[2 * i + 1] += (src[2 * i + 1] - 128) * vol_r << 1;
-						}
-					}
-					else
+					const INT16* const src = (const INT16*)s->pData + Sound->pos;
+					for (UINT32 i = 0; i < amount; i++)
 					{
-						const UINT8* const src = (const UINT8*)s->pData + Sound->pos;
-						for (UINT32 i = 0; i < amount; ++i)
-						{
-							const INT data = (src[i] - 128) << 1;
-							Stream[2 * i + 0] += data * vol_l;
-							Stream[2 * i + 1] += data * vol_r;
-						}
+						const INT data = src[i];
+						Stream[2 * i + 0] += data * vol_l >> 7;
+						Stream[2 * i + 1] += data * vol_r >> 7;
 					}
 				}
 
@@ -1128,15 +800,14 @@ static BOOLEAN SoundInitHardware(void)
 {
 	SDL_InitSubSystem(SDL_INIT_AUDIO);
 
-	SDL_AudioSpec spec;
-	spec.freq     = 22050;
-	spec.format   = AUDIO_S16SYS;
-	spec.channels = 2;
-	spec.samples  = 1024;
-	spec.callback = SoundCallback;
-	spec.userdata = NULL;
+	gTargetAudioSpec.freq     = 44100;
+	gTargetAudioSpec.format   = AUDIO_S16SYS;
+	gTargetAudioSpec.channels = 2;
+	gTargetAudioSpec.samples  = 1024;
+	gTargetAudioSpec.callback = SoundCallback;
+	gTargetAudioSpec.userdata = NULL;
 
-	if (SDL_OpenAudio(&spec, NULL) != 0) return FALSE;
+	if (SDL_OpenAudio(&gTargetAudioSpec, NULL) != 0) return FALSE;
 
 	memset(pSoundList, 0, sizeof(pSoundList));
 	SDL_PauseAudio(0);
@@ -1174,7 +845,7 @@ static UINT32 SoundGetUniqueID(void);
  * Returns: Unique sound ID if successful, SOUND_ERROR if not. */
 static UINT32 SoundStartSample(SAMPLETAG* sample, SOUNDTAG* channel, UINT32 volume, UINT32 pan, UINT32 loop, void (*end_callback)(void*), void* data)
 {
-	SNDDBG("PLAY channel %u sample %u file \"%s\"\n", channel - pSoundList, sample - pSampleList, sample->pName);
+	SLOGD(DEBUG_TAG_SOUND, "playing channel %u sample %u file \"%s\"", channel - pSoundList, sample - pSampleList, sample->pName);
 
 	if (!fSoundSystemInit) return SOUND_ERROR;
 
@@ -1220,7 +891,7 @@ static BOOLEAN SoundStopChannel(SOUNDTAG* channel)
 
 	if (channel->pSample == NULL) return FALSE;
 
-	SNDDBG("STOP channel %u\n", channel - pSoundList);
+	SLOGD(DEBUG_TAG_SOUND, "stopping channel channel %u", channel - pSoundList);
 	channel->State = CHANNEL_STOP;
 	return TRUE;
 }
diff --git a/sgp/VSurface.cc b/sgp/VSurface.cc
index 9147259..4c68143 100644
--- a/sgp/VSurface.cc
+++ b/sgp/VSurface.cc
@@ -28,13 +28,13 @@ SGPVSurface::SGPVSurface(UINT16 const w, UINT16 const h, UINT8 const bpp) :
 	switch (bpp)
 	{
 		case 8:
-			s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp, 0, 0, 0, 0);
+			s = SDL_CreateRGBSurface(0, w, h, bpp, 0, 0, 0, 0);
 			break;
 
 		case 16:
 		{
-			SDL_PixelFormat const* f = SDL_GetVideoSurface()->format;
-			s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp, f->Rmask, f->Gmask, f->Bmask, 0);
+			SDL_PixelFormat const* f = SDL_AllocFormat(SDL_PIXELFORMAT_RGB565);
+			s = SDL_CreateRGBSurface(0, w, h, bpp, f->Rmask, f->Gmask, f->Bmask, f->Amask);
 			break;
 		}
 
@@ -112,7 +112,7 @@ void SGPVSurface::SetTransparency(const COLORVAL colour)
 
 		default: abort(); // HACK000E
 	}
-	SDL_SetColorKey(surface_, SDL_SRCCOLORKEY, colour_key);
+	SDL_SetColorKey(surface_, SDL_TRUE, colour_key);
 }
 
 
@@ -370,9 +370,10 @@ void BltStretchVideoSurface(SGPVSurface* const dst, SGPVSurface const* const src
 	UINT const dx     = src_rect->w;
 	UINT const dy     = src_rect->h;
 	UINT py = 0;
-	if (ssurface->flags & SDL_SRCCOLORKEY)
+	if (ssurface->flags & SDL_TRUE)
 	{
-		const UINT16 key = ssurface->format->colorkey;
+//		const UINT16 key = ssurface->format->colorkey;
+		const UINT16 key = 0;
 		for (UINT iy = 0; iy < height; ++iy)
 		{
 			const UINT16* s = os;
diff --git a/sgp/Video.cc b/sgp/Video.cc
index 1c6c217..57fab03 100644
--- a/sgp/Video.cc
+++ b/sgp/Video.cc
@@ -22,13 +22,13 @@
 #include <stdarg.h>
 #include "UILayout.h"
 #include "PlatformIO.h"
-#include "PlatformSDL.h"
 #include "Font.h"
 #include "Icon.h"
 
 #include "ContentManager.h"
 #include "GameInstance.h"
 
+#include "slog/slog.h"
 
 #define BUFFER_READY      0x00
 #define BUFFER_DIRTY      0x02
@@ -44,6 +44,12 @@
 
 #define MAX_NUM_FRAMES    25
 
+#define RED_MASK 0xF800
+#define GREEN_MASK 0x07E0
+#define BLUE_MASK 0x001F
+#define ALPHA_MASK 0
+
+#define DEBUG_TAG_VIDEO "Video"
 
 static BOOLEAN gfVideoCapture = FALSE;
 static UINT32  guiFramePeriod = 1000 / 15;
@@ -80,9 +86,12 @@ static UINT32  guiPrintFrameBufferIndex;
 
 static SDL_Surface* MouseCursor;
 static SDL_Surface* FrameBuffer;
-static SDL_Surface* ScreenBuffer;
-static Uint32       g_video_flags = SDL_SWSURFACE | SDL_HWPALETTE;
+static SDL_Renderer*  GameRenderer;
+SDL_Window* g_game_window;
 
+static SDL_Surface* ScreenBuffer;
+static SDL_Texture* ScreenTexture;
+static Uint32       g_window_flags = 0;
 
 static void RecreateBackBuffer();
 static void DeletePrimaryVideoSurfaces(void);
@@ -91,48 +100,25 @@ void VideoSetFullScreen(const BOOLEAN enable)
 {
 	if (enable)
 	{
-		g_video_flags |= SDL_FULLSCREEN;
+		g_window_flags |= SDL_WINDOW_FULLSCREEN;
 	}
 	else
 	{
-		g_video_flags &= ~SDL_FULLSCREEN;
+		g_window_flags &= ~SDL_WINDOW_FULLSCREEN;
 	}
 }
 
 
 void VideoToggleFullScreen(void)
 {
-	SDL_Surface* const scr = ScreenBuffer;
-
-	// First try using SDL magic to toggle fullscreen
-	if (SDL_WM_ToggleFullScreen(scr))
+	if (SDL_GetWindowFlags(g_game_window) & SDL_WINDOW_FULLSCREEN)
 	{
-		g_video_flags ^= SDL_FULLSCREEN;
-		return;
+		SDL_SetWindowFullscreen(g_game_window, 0);
+	}
+	else
+	{
+		SDL_SetWindowFullscreen(g_game_window, SDL_WINDOW_FULLSCREEN);
 	}
-
-	// Fallback to manual toggling
-	SDL_PixelFormat const& fmt = *scr->format;
-	int             const  w   = scr->w;
-	int             const  h   = scr->h;
-	Uint8           const  bpp = fmt.BitsPerPixel;
-
-	SGP::AutoObj<SDL_Surface, SDL_FreeSurface> tmp(SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, bpp, fmt.Rmask, fmt.Gmask, fmt.Bmask, fmt.Amask));
-	if (!tmp) return;
-
-	SDL_BlitSurface(scr, 0, tmp, 0);
-
-	Uint32       const new_vflags = g_video_flags ^ SDL_FULLSCREEN;
-	SDL_Surface* const new_scr    = SDL_SetVideoMode(w, h, bpp, new_vflags);
-	if (!new_scr) return;
-
-	g_video_flags = new_vflags;
-
-	ScreenBuffer  = new_scr;
-  RecreateBackBuffer();
-
-	SDL_BlitSurface(tmp, 0, new_scr, 0);
-	SDL_UpdateRect(new_scr, 0, 0, 0, 0);
 }
 
 
@@ -141,9 +127,21 @@ static void GetRGBDistribution();
 
 void InitializeVideoManager(void)
 {
-	DebugMsg(TOPIC_VIDEO, DBG_LEVEL_0, "Initializing the video manager");
+	SLOGD(DEBUG_TAG_VIDEO, "Initializing the video manager");
+	SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
+	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
+
+	g_window_flags |= SDL_WINDOW_RESIZABLE;
+
+	g_game_window = SDL_CreateWindow(APPLICATION_NAME,
+					SDL_WINDOWPOS_UNDEFINED,
+					SDL_WINDOWPOS_UNDEFINED,
+					SCREEN_WIDTH, SCREEN_HEIGHT,
+					g_window_flags);
+
+	GameRenderer = SDL_CreateRenderer(g_game_window, -1, 0);
+	SDL_RenderSetLogicalSize(GameRenderer, SCREEN_WIDTH, SCREEN_HEIGHT);
 
-	SDL_WM_SetCaption(APPLICATION_NAME, NULL);
 	SDL_Surface* windowIcon = SDL_CreateRGBSurfaceFrom(
 		(void*)gWindowIconData.pixel_data,
     gWindowIconData.width,
@@ -151,29 +149,56 @@ void InitializeVideoManager(void)
     gWindowIconData.bytes_per_pixel*8,
     gWindowIconData.bytes_per_pixel*gWindowIconData.width,
     0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
-	SDL_WM_SetIcon(windowIcon, NULL);
+	SDL_SetWindowIcon(g_game_window, windowIcon);
 	SDL_FreeSurface(windowIcon);
 
+
   ClippingRect.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
 
-	ScreenBuffer = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_DEPTH, g_video_flags);
-	if (!ScreenBuffer) throw std::runtime_error("Failed to set up video mode");
+	ScreenBuffer = SDL_CreateRGBSurface(
+					0,
+					SCREEN_WIDTH,
+					SCREEN_HEIGHT,
+					PIXEL_DEPTH,
+					RED_MASK,
+					GREEN_MASK,
+					BLUE_MASK,
+					ALPHA_MASK
+	);
+
+	if (ScreenBuffer == NULL) {
+		SLOGE(DEBUG_TAG_VIDEO, "SDL_CreateRGBSurface for ScreenBuffer failed: %s\n", SDL_GetError());
+	}
 
-	Uint32 Rmask = ScreenBuffer->format->Rmask;
-	Uint32 Gmask = ScreenBuffer->format->Gmask;
-	Uint32 Bmask = ScreenBuffer->format->Bmask;
-	Uint32 Amask = ScreenBuffer->format->Amask;
+	ScreenTexture = SDL_CreateTexture(GameRenderer,
+					SDL_PIXELFORMAT_RGB565,
+					SDL_TEXTUREACCESS_STREAMING,
+					SCREEN_WIDTH, SCREEN_HEIGHT);
+
+	if (ScreenTexture == NULL) {
+		SLOGE(DEBUG_TAG_VIDEO, "SDL_CreateTexture for ScreenTexture failed: %s\n", SDL_GetError());
+	}
 
 	FrameBuffer = SDL_CreateRGBSurface(
 		SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_DEPTH,
-		Rmask, Gmask, Bmask, Amask
+		RED_MASK, GREEN_MASK, BLUE_MASK, ALPHA_MASK
 	);
 
+	if (FrameBuffer == NULL)
+	{
+		SLOGE(DEBUG_TAG_VIDEO, "SDL_CreateRGBSurface for FrameBuffer failed: %s\n", SDL_GetError());
+	}
+
 	MouseCursor = SDL_CreateRGBSurface(
-		SDL_SWSURFACE, MAX_CURSOR_WIDTH, MAX_CURSOR_HEIGHT, PIXEL_DEPTH,
-		Rmask, Gmask, Bmask, Amask
+		0, MAX_CURSOR_WIDTH, MAX_CURSOR_HEIGHT, PIXEL_DEPTH,
+		RED_MASK, GREEN_MASK, BLUE_MASK, ALPHA_MASK
 	);
-	SDL_SetColorKey(MouseCursor, SDL_SRCCOLORKEY, 0);
+	SDL_SetColorKey(MouseCursor, SDL_TRUE, 0);
+
+	if (MouseCursor == NULL)
+	{
+		SLOGE(DEBUG_TAG_VIDEO, "SDL_CreateRGBSurface for MouseCursor failed: %s\n", SDL_GetError());
+	}
 
 	SDL_ShowCursor(SDL_DISABLE);
 
@@ -192,8 +217,7 @@ void InitializeVideoManager(void)
 
 void ShutdownVideoManager(void)
 {
-	DebugMsg(TOPIC_VIDEO, DBG_LEVEL_0, "Shutting down the video manager");
-
+	SLOGD(DEBUG_TAG_VIDEO, "Shutting down the video manager");
 	/* Toggle the state of the video manager to indicate to the refresh thread
 	 * that it needs to shut itself down */
 
@@ -211,32 +235,6 @@ void SuspendVideoManager(void)
 	guiVideoManagerState = VIDEO_SUSPENDED;
 }
 
-
-BOOLEAN RestoreVideoManager(void)
-{
-#if 1 // XXX TODO
-	UNIMPLEMENTED;
-  return false;
-#else
-	// Make sure the video manager is indeed suspended before moving on
-
-	if (guiVideoManagerState == VIDEO_SUSPENDED)
-	{
-		// Set the video state to VIDEO_ON
-
-		guiFrameBufferState = BUFFER_DIRTY;
-		gfForceFullScreenRefresh = TRUE;
-		guiVideoManagerState = VIDEO_ON;
-		return TRUE;
-	}
-	else
-	{
-		return FALSE;
-	}
-#endif
-}
-
-
 void InvalidateRegion(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom)
 {
 	if (gfForceFullScreenRefresh)
@@ -346,7 +344,7 @@ void InvalidateScreen(void)
 static void ScrollJA2Background(INT16 sScrollXIncrement, INT16 sScrollYIncrement)
 {
 	SDL_Surface* Frame  = FrameBuffer;
-	SDL_Surface* Source = ScreenBuffer; // Primary
+	SDL_Surface* Source = SDL_CreateRGBSurface(0, ScreenBuffer->w, ScreenBuffer->h, PIXEL_DEPTH, RED_MASK, GREEN_MASK, BLUE_MASK, ALPHA_MASK);
 	SDL_Surface* Dest   = ScreenBuffer; // Back
 	SDL_Rect     SrcRect;
 	SDL_Rect     DstRect;
@@ -356,6 +354,8 @@ static void ScrollJA2Background(INT16 sScrollXIncrement, INT16 sScrollYIncrement
 	const UINT16 usWidth  = SCREEN_WIDTH;
 	const UINT16 usHeight = gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y;
 
+	SDL_BlitSurface(ScreenBuffer, NULL, Source, NULL);
+
 	if (sScrollXIncrement < 0)
 	{
 		SrcRect.x = 0;
@@ -444,14 +444,17 @@ static void ScrollJA2Background(INT16 sScrollXIncrement, INT16 sScrollYIncrement
 	// BLIT NEW
 	ExecuteVideoOverlaysToAlternateBuffer(BACKBUFFER);
 
-	SDL_UpdateRect
-	(
-		Dest,
-		gsVIEWPORT_START_X,
-		gsVIEWPORT_WINDOW_START_Y,
-		gsVIEWPORT_END_X - gsVIEWPORT_START_X,
-		gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y
-	);
+	SDL_Texture* screenTexture = SDL_CreateTextureFromSurface(GameRenderer, ScreenBuffer);
+
+	SDL_Rect r;
+	r.x = gsVIEWPORT_START_X;
+	r.y = gsVIEWPORT_WINDOW_START_Y;
+	r.w = gsVIEWPORT_END_X - gsVIEWPORT_START_X;
+	r.h = gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y;
+	SDL_RenderCopy(GameRenderer, screenTexture, &r, &r);
+
+	SDL_FreeSurface(Source);
+	SDL_DestroyTexture(screenTexture);
 }
 
 
@@ -481,8 +484,8 @@ static void WriteTGAHeader(FILE* const f)
 		0,
 		0, 0,
 		0, 0,
-		SCREEN_WIDTH  % 256, SCREEN_WIDTH  / 256,
-		SCREEN_HEIGHT % 256, SCREEN_HEIGHT / 256,
+		(UINT8) (SCREEN_WIDTH  % 256), (UINT8) (SCREEN_WIDTH  / 256),
+		(UINT8) (SCREEN_HEIGHT % 256), (UINT8) (SCREEN_HEIGHT / 256),
 		PIXEL_DEPTH,
 		0
 	};
@@ -550,29 +553,6 @@ static void TakeScreenshot()
 
 static void SnapshotSmall(void);
 
-#if EXPENSIVE_SDL_UPDATE_RECT
-static void joinInRectangle(SDL_Rect &result, const SDL_Rect &newRect)
-{
-  if((newRect.w != 0) && (newRect.h != 0))
-  {
-    if((result.w == 0) && (result.h == 0))
-    {
-      // special case: empty rectangle
-      result = newRect;
-    }
-    else
-    {
-      int16_t X2 = std::max(result.x + result.w, newRect.x + newRect.w);
-      int16_t Y2 = std::max(result.y + result.h, newRect.y + newRect.h);
-      result.x = std::min(result.x, newRect.x);
-      result.y = std::min(result.y, newRect.y);
-      result.w = X2 - result.x;
-      result.h = Y2 - result.y;
-    }
-  }
-}
-#endif
-
 void RefreshScreen(void)
 {
 	if (guiVideoManagerState != VIDEO_ON) return;
@@ -660,10 +640,6 @@ void RefreshScreen(void)
 		gfPrintFrameBuffer = FALSE;
 	}
 
-#if EXPENSIVE_SDL_UPDATE_RECT
-  SDL_Rect combinedRect = {0, 0, 0, 0};
-#endif
-
 	SGPPoint MousePos;
 	GetMousePos(&MousePos);
 	SDL_Rect src;
@@ -675,56 +651,13 @@ void RefreshScreen(void)
 	dst.x = MousePos.iX - gsMouseCursorXOffset;
 	dst.y = MousePos.iY - gsMouseCursorYOffset;
 	SDL_BlitSurface(MouseCursor, &src, ScreenBuffer, &dst);
-
-#if EXPENSIVE_SDL_UPDATE_RECT
-  joinInRectangle(combinedRect, dst);
-  joinInRectangle(combinedRect, MouseBackground);
-#else
-  SDL_UpdateRects(ScreenBuffer, 1, &dst);
-  SDL_UpdateRects(ScreenBuffer, 1, &MouseBackground);
-#endif
-
 	MouseBackground = dst;
 
-	if (gfForceFullScreenRefresh)
-	{
-		SDL_UpdateRect(ScreenBuffer, 0, 0, 0, 0);
-	}
-	else
-	{
-#if EXPENSIVE_SDL_UPDATE_RECT
-    for(int i = 0; i < guiDirtyRegionCount; i++)
-    {
-      joinInRectangle(combinedRect, DirtyRegions[i]);
-    }
-#else
-		SDL_UpdateRects(ScreenBuffer, guiDirtyRegionCount, DirtyRegions);
-#endif
-
-		for (UINT32 i = 0; i < guiDirtyRegionExCount; i++)
-		{
-			SDL_Rect* r = &DirtyRegionsEx[i];
-			if (scrolling)
-			{
-				if (r->y <= gsVIEWPORT_WINDOW_END_Y && r->y + r->h <= gsVIEWPORT_WINDOW_END_Y)
-				{
-					continue;
-				}
-			}
-#if EXPENSIVE_SDL_UPDATE_RECT
-      joinInRectangle(combinedRect, *r);
-#else
-			SDL_UpdateRects(ScreenBuffer, 1, r);
-#endif
-		}
-	}
+	SDL_UpdateTexture(ScreenTexture, NULL, ScreenBuffer->pixels, ScreenBuffer->pitch);
 
-#if EXPENSIVE_SDL_UPDATE_RECT
-  if((combinedRect.w != 0) && (combinedRect.h != 0))
-  {
-    SDL_UpdateRects(ScreenBuffer, 1, &combinedRect);
-  }
-#endif
+	SDL_RenderClear(GameRenderer);
+	SDL_RenderCopy(GameRenderer, ScreenTexture, NULL, NULL);
+	SDL_RenderPresent(GameRenderer);
 
 	gfForceFullScreenRefresh = FALSE;
 	guiDirtyRegionCount = 0;
@@ -815,33 +748,6 @@ static void SnapshotSmall(void)
 	if (giNumFrames == MAX_NUM_FRAMES) RefreshMovieCache();
 }
 
-
-void VideoCaptureToggle(void)
-{
-#ifdef JA2TESTVERSION
-	gfVideoCapture = !gfVideoCapture;
-	if (gfVideoCapture)
-	{
-		for (INT32 cnt = 0; cnt < MAX_NUM_FRAMES; cnt++)
-		{
-			gpFrameData[cnt] = MALLOCN(UINT16, SCREEN_WIDTH * SCREEN_HEIGHT);
-		}
-		guiLastFrame = GetClock();
-	}
-	else
-	{
-		RefreshMovieCache();
-
-		for (INT32 cnt = 0; cnt < MAX_NUM_FRAMES; cnt++)
-		{
-			if (gpFrameData[cnt] != NULL) MemFree(gpFrameData[cnt]);
-		}
-	}
-	giNumFrames = 0;
-#endif
-}
-
-
 static void RefreshMovieCache(void)
 {
 	static UINT32 uiPicNum = 0;
@@ -935,7 +841,7 @@ void InitializeVideoSurfaceManager(void)
 
 void ShutdownVideoSurfaceManager(void)
 {
-  DebugMsg(TOPIC_VIDEOSURFACE, DBG_LEVEL_0, "Shutting down the Video Surface manager");
+	SLOGD(DEBUG_TAG_VIDEO, "Shutting down the Video Surface manager");
 
 	// Delete primary viedeo surfaces
 	DeletePrimaryVideoSurfaces();
diff --git a/sgp/Video.h b/sgp/Video.h
index fbb980b..2158ef3 100644
--- a/sgp/Video.h
+++ b/sgp/Video.h
@@ -6,13 +6,14 @@
 
 
 #define VIDEO_NO_CURSOR 0xFFFF
+#define GAME_WINDOW g_game_window
 
+extern SDL_Window* g_game_window;
 
 void         VideoSetFullScreen(BOOLEAN enable);
 void         InitializeVideoManager(void);
 void         ShutdownVideoManager(void);
 void         SuspendVideoManager(void);
-BOOLEAN      RestoreVideoManager(void);
 void         InvalidateRegion(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom);
 void         InvalidateScreen(void);
 void         GetPrimaryRGBDistributionMasks(UINT32* RedBitMask, UINT32* GreenBitMask, UINT32* BlueBitMask);
@@ -25,8 +26,6 @@ void VideoToggleFullScreen(void);
 
 void SetMouseCursorProperties(INT16 sOffsetX, INT16 sOffsetY, UINT16 usCursorHeight, UINT16 usCursorWidth);
 
-void VideoCaptureToggle(void);
-
 void InvalidateRegionEx(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom);
 
 void RefreshScreen(void);
