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);