From 278241859bf586dd77b6cdb48d7edce6270803c2 Mon Sep 17 00:00:00 2001 From: safarp Date: Mon, 21 Mar 2022 21:51:29 +0100 Subject: Ports: Add Jagged Alliance 2 Stracciatella port --- Ports/ja2/package.sh | 20 + Ports/ja2/patches/ReadMe.md | 5 + Ports/ja2/patches/fix_SDL2_compilation.patch | 2452 ++++++++++++++++++++++++++ 3 files changed, 2477 insertions(+) create mode 100755 Ports/ja2/package.sh create mode 100644 Ports/ja2/patches/ReadMe.md create mode 100644 Ports/ja2/patches/fix_SDL2_compilation.patch (limited to 'Ports/ja2') diff --git a/Ports/ja2/package.sh b/Ports/ja2/package.sh new file mode 100755 index 0000000000..e546e823b8 --- /dev/null +++ b/Ports/ja2/package.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env -S bash ../.port_include.sh +port=ja2 +version=0.15.x +depends=("SDL2") +workdir="ja2-stracciatella-${version}" +files="https://github.com/ja2-stracciatella/ja2-stracciatella/archive/refs/heads/${version}.zip ja2-stracciatella-${version}.zip 178375de4859d16a76276c781455bf48d3fa862841387c8aa6cfa4162f4f0ca4" +auth_type=sha256 +makeopts="SERENITY=1" +launcher_name="Jagged Alliance 2" +launcher_category=Games +launcher_command="/opt/ja2/ja2" + +install() { + installdir="${SERENITY_INSTALL_ROOT}/opt/ja2" + run mkdir -p "${installdir}" + run cp -r ja2 mods externalized "${installdir}" + echo "INFO: Jagged Alliance 2 data have to be provided! Copy DATA directory located in the original Jagged Alliance 2 installation into the '${installdir}'." + echo "INFO: Boot up SerenityOS and run '/opt/ja2/ja2' executable in order to produce ja2.ini configuration file in the '/home/anon/.ja2'." + echo "INFO: Edit the configuration file and set 'data_dir' value to '/opt/ja2'." +} diff --git a/Ports/ja2/patches/ReadMe.md b/Ports/ja2/patches/ReadMe.md new file mode 100644 index 0000000000..16b537b1fd --- /dev/null +++ b/Ports/ja2/patches/ReadMe.md @@ -0,0 +1,5 @@ +# Patches for ja2 + +## `fix_SDL2_compilation.patch` + +Migrates the source code and Makefile to use SDL 2.0 instead of version 1.2. \ No newline at end of file diff --git a/Ports/ja2/patches/fix_SDL2_compilation.patch b/Ports/ja2/patches/fix_SDL2_compilation.patch new file mode 100644 index 0000000000..242a396364 --- /dev/null +++ b/Ports/ja2/patches/fix_SDL2_compilation.patch @@ -0,0 +1,2452 @@ +diff --git a/Build/Laptop/IMP_Begin_Screen.cc b/Build/Laptop/IMP_Begin_Screen.cc +index 81db08629..63c6babdb 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 1512a045a..bc0157a99 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 dbf14b827..9f485037b 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 998179af2..c036a7833 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 fc64d5813..4869ce29a 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 6db811f3f..53103d351 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 Serenity SDL2 library ++ifdef SERENITY ++CFLAGS_SDL= -I${SERENITY_BUILD_DIR}/Root/usr/local/include/SDL2 -D_REENTRANT ++LDFLAGS_SDL= -L${SERENITY_BUILD_DIR}/Root/usr/local/lib -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 0b607c213..cdfca08a2 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 +@@ -23,6 +21,7 @@ + #endif + + #include "PlatformIO.h" ++#include "Debug.h" + + #if MACOS_USE_RESOURCES_FROM_BUNDLE && defined __APPLE__ && defined __MACH__ + #include +@@ -30,11 +29,15 @@ + + #if CASE_SENSITIVE_FS + #include ++#include + #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 a7f96cbf3..d4fdf3c2a 100644 +--- a/sgp/FileMan.h ++++ b/sgp/FileMan.h +@@ -10,8 +10,6 @@ + #ifdef _WIN32 + # define WIN32_LEAN_AND_MEAN + # include +-#else +-# include + #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 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 3b64602b5..6d82a147a 100644 +--- a/sgp/Input.cc ++++ b/sgp/Input.cc +@@ -1,3 +1,5 @@ ++#include ++#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 84d7cd185..0da0517a6 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 cf1719bad..bfea6df43 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 263f9b1b6..c232813e2 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 7b9b71048..1c43c4c61 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 c5d958ec8..bc200f7da 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 e37d632e7..aff29801e 100644 +--- a/sgp/SoundMan.cc ++++ b/sgp/SoundMan.cc +@@ -14,22 +14,18 @@ + #include "Timer.h" + #include + #include ++#include + + #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 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 9147259b8..4c68143dd 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 1c6c2170b..57fab03c8 100644 +--- a/sgp/Video.cc ++++ b/sgp/Video.cc +@@ -22,13 +22,13 @@ + #include + #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 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 fbb980b63..2158ef3ee 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); -- cgit v1.2.3