From 8f6e9810d00a58db6b23bc4b72e08810fcbafba8 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 5 Oct 2025 16:28:23 +0100 Subject: [PATCH] SDL3: Make `text_render_integration_test` build Adds support for just enough SDL3 to make `text_render_integration_test` work. --- .github/workflows/Linux_x86_64_SDL3_test.yml | 51 +++++ .github/workflows/Linux_x86_64_test.yml | 2 +- 3rdParty/SDL3_image/CMakeLists.txt | 1 + Source/appfat.cpp | 13 +- Source/diablo.cpp | 11 ++ Source/diablo.h | 5 + Source/encrypt.cpp | 1 - Source/engine/assets.cpp | 49 ++++- Source/engine/assets.hpp | 88 +++++++-- Source/engine/load_pcx.cpp | 4 + Source/engine/load_pcx.hpp | 4 + Source/engine/palette.cpp | 6 + Source/engine/palette.h | 4 + Source/engine/sound_defs.hpp | 4 + Source/engine/surface.hpp | 5 + Source/engine/ticks.cpp | 4 + Source/interfac.cpp | 6 + Source/interfac.h | 5 + Source/mpq/mpq_sdl_rwops.cpp | 88 ++++++++- Source/mpq/mpq_sdl_rwops.hpp | 11 +- Source/mpq/mpq_writer.cpp | 24 +-- Source/options.cpp | 64 ++++++- Source/options.h | 5 + Source/utils/display.cpp | 12 +- Source/utils/endian_swap.hpp | 31 +++ Source/utils/endian_write.hpp | 6 +- Source/utils/file_util.cpp | 26 ++- Source/utils/log.hpp | 18 +- Source/utils/palette_blending.cpp | 5 +- Source/utils/palette_blending.hpp | 4 + Source/utils/palette_kd_tree.cpp | 4 +- Source/utils/palette_kd_tree.hpp | 4 +- Source/utils/paths.cpp | 34 +++- Source/utils/pcx_to_clx.cpp | 11 +- Source/utils/pcx_to_clx.hpp | 4 + Source/utils/sdl_geometry.h | 4 + Source/utils/sdl_ptrs.h | 23 ++- Source/utils/sdl_thread.h | 18 ++ Source/utils/sdl_wrap.h | 190 ++++++++++++------- Source/utils/surface_to_png.cpp | 24 ++- Source/utils/surface_to_png.hpp | 14 +- Source/utils/ui_fwd.h | 14 +- test/text_render_integration_test.cpp | 31 ++- 43 files changed, 766 insertions(+), 166 deletions(-) create mode 100644 .github/workflows/Linux_x86_64_SDL3_test.yml create mode 100644 Source/utils/endian_swap.hpp diff --git a/.github/workflows/Linux_x86_64_SDL3_test.yml b/.github/workflows/Linux_x86_64_SDL3_test.yml new file mode 100644 index 000000000..fd9d2bfd9 --- /dev/null +++ b/.github/workflows/Linux_x86_64_SDL3_test.yml @@ -0,0 +1,51 @@ +name: Linux x64 SDL3 Tests + +on: + push: + branches: + - master + paths-ignore: + - '*.md' + - 'docs/**' + pull_request: + types: [ opened, synchronize ] + paths-ignore: + - '*.md' + - 'docs/**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Install dependencies + run: | + sudo apt-get update -y + sudo apt-get install -y cmake curl g++ git libgtest-dev libgmock-dev libbenchmark-dev libfmt-dev libsodium-dev libpng-dev libbz2-dev wget + + - name: Cache CMake build folder + uses: actions/cache@v4 + with: + path: build + key: ${{ github.workflow }}-v2-${{ github.sha }} + restore-keys: ${{ github.workflow }}-v2- + + # We specify `-DDEVILUTIONX_SYSTEM_BENCHMARK=OFF` to work around the following error: + # lto1: fatal error: bytecode stream in file ‘/usr/lib/x86_64-linux-gnu/libbenchmark_main.a’ generated with LTO version 11.2 instead of the expected 11.3 + - name: Build tests + run: | + cmake -S. -Bbuild -G Ninja -DUSE_SDL3=ON -DDEVILUTIONX_SYSTEM_SDL3=OFF -DDEVILUTIONX_STATIC_SDL3=ON -DDEVILUTIONX_SYSTEM_SDL_IMAGE=OFF -DNOSOUND=ON -DDEVILUTIONX_SYSTEM_BENCHMARK=OFF + wget -qnc https://github.com/diasurgical/devilutionx-assets/releases/download/v2/spawn.mpq -P build + cmake --build build -j $(nproc) --target text_render_integration_test + + - name: Run tests + run: cd build && ctest -R 'TextRenderIntegrationTest' --output-on-failure diff --git a/.github/workflows/Linux_x86_64_test.yml b/.github/workflows/Linux_x86_64_test.yml index 99afe3ff7..a3b863f0a 100644 --- a/.github/workflows/Linux_x86_64_test.yml +++ b/.github/workflows/Linux_x86_64_test.yml @@ -44,7 +44,7 @@ jobs: - name: Build tests run: | cmake -S. -Bbuild -DENABLE_CODECOVERAGE=ON -DDEVILUTIONX_SYSTEM_BENCHMARK=OFF - wget -nc https://github.com/diasurgical/devilutionx-assets/releases/download/v2/spawn.mpq -P build + wget -qnc https://github.com/diasurgical/devilutionx-assets/releases/download/v2/spawn.mpq -P build cmake --build build -j $(nproc) - name: Run tests diff --git a/3rdParty/SDL3_image/CMakeLists.txt b/3rdParty/SDL3_image/CMakeLists.txt index 9347d8c1a..ac7de522f 100644 --- a/3rdParty/SDL3_image/CMakeLists.txt +++ b/3rdParty/SDL3_image/CMakeLists.txt @@ -25,6 +25,7 @@ else() set(BUILD_SHARED_LIBS ON) endif() +set(SDLIMAGE_BACKEND_STB OFF) set(SDLIMAGE_AVIF OFF) set(SDLIMAGE_BMP OFF) set(SDLIMAGE_GIF OFF) diff --git a/Source/appfat.cpp b/Source/appfat.cpp index 637d341c0..156e9cd64 100644 --- a/Source/appfat.cpp +++ b/Source/appfat.cpp @@ -6,12 +6,18 @@ #include +#ifdef USE_SDL3 +#include +#include +#else #include -#include #ifdef USE_SDL1 #include "utils/sdl2_to_1_2_backports.h" #endif +#endif + +#include #include "diablo.h" #include "multi.h" @@ -27,8 +33,13 @@ namespace { /** Set to true when a fatal error is encountered and the application should shut down. */ bool Terminating = false; + /** Thread id of the last callee to FreeDlg(). */ +#ifdef USE_SDL3 +SDL_ThreadID CleanupThreadId; +#else SDL_threadID CleanupThreadId; +#endif /** * @brief Cleans up after a fatal application error. diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 6bf03a787..0c38014a2 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -7,6 +7,17 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else +#include + +#ifdef USE_SDL1 +#include "utils/sdl2_to_1_2_backports.h" +#endif +#endif + #include #include diff --git a/Source/diablo.h b/Source/diablo.h index e8f7d35b6..c977e252b 100644 --- a/Source/diablo.h +++ b/Source/diablo.h @@ -7,11 +7,16 @@ #include +#ifdef USE_SDL3 +#include +#include +#else #include #ifdef USE_SDL1 #include "utils/sdl2_to_1_2_backports.h" #endif +#endif #ifdef _DEBUG #include "monstdat.h" diff --git a/Source/encrypt.cpp b/Source/encrypt.cpp index f4ee72dbd..ca4b6fd41 100644 --- a/Source/encrypt.cpp +++ b/Source/encrypt.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include "encrypt.h" diff --git a/Source/engine/assets.cpp b/Source/engine/assets.cpp index dd031a109..18abffef5 100644 --- a/Source/engine/assets.cpp +++ b/Source/engine/assets.cpp @@ -6,6 +6,13 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else +#include +#endif + #include "appfat.h" #include "game_mode.hpp" #include "utils/file_util.h" @@ -45,16 +52,25 @@ char *FindUnpackedMpqFile(char *relativePath) #else bool IsDebugLogging() { - return SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION) <= SDL_LOG_PRIORITY_DEBUG; + return IsLogLevel(LogCategory::Application, SDL_LOG_PRIORITY_DEBUG); } -SDL_RWops *OpenOptionalRWops(const std::string &path) +#ifdef USE_SDL3 +SDL_IOStream * +#else +SDL_RWops * +#endif +OpenOptionalRWops(const std::string &path) { // SDL always logs an error in Debug mode. // We check the file presence in Debug mode to avoid this. if (IsDebugLogging() && !FileExists(path.c_str())) return nullptr; +#ifdef USE_SDL3 + return SDL_IOFromFile(path.c_str(), "rb"); +#else return SDL_RWFromFile(path.c_str(), "rb"); +#endif }; bool FindMpqFile(std::string_view filename, MpqArchive **archive, uint32_t *fileNumber) @@ -128,7 +144,12 @@ AssetRef FindAsset(std::string_view filename) #endif if (relativePath[0] == '/') { - result.directHandle = SDL_RWFromFile(relativePath.c_str(), "rb"); + result.directHandle = +#ifdef USE_SDL3 + SDL_IOFromFile(relativePath.c_str(), "rb"); +#else + SDL_RWFromFile(relativePath.c_str(), "rb"); +#endif if (result.directHandle != nullptr) { return result; } @@ -161,7 +182,12 @@ AssetRef FindAsset(std::string_view filename) // Fall back to the bundled assets on supported systems. // This is handled by SDL when we pass a relative path. if (!paths::AssetsPath().empty()) { - result.directHandle = SDL_RWFromFile(relativePath.c_str(), "rb"); + result.directHandle = +#ifdef USE_SDL3 + SDL_IOFromFile(relativePath.c_str(), "rb"); +#else + SDL_RWFromFile(relativePath.c_str(), "rb"); +#endif if (result.directHandle != nullptr) return result; } @@ -180,7 +206,7 @@ AssetHandle OpenAsset(AssetRef &&ref, bool threadsafe) return AssetHandle { SDL_RWops_FromMpqFile(*ref.archive, ref.fileNumber, ref.filename, threadsafe) }; if (ref.directHandle != nullptr) { // Transfer handle ownership: - SDL_RWops *handle = ref.directHandle; + auto *handle = ref.directHandle; ref.directHandle = nullptr; return AssetHandle { handle }; } @@ -205,13 +231,22 @@ AssetHandle OpenAsset(std::string_view filename, size_t &fileSize, bool threadsa return OpenAsset(std::move(ref), threadsafe); } -SDL_RWops *OpenAssetAsSdlRwOps(std::string_view filename, bool threadsafe) +#ifdef USE_SDL3 +SDL_IOStream * +#else +SDL_RWops * +#endif +OpenAssetAsSdlRwOps(std::string_view filename, bool threadsafe) { #ifdef UNPACKED_MPQS AssetRef ref = FindAsset(filename); if (!ref.ok()) return nullptr; +#ifdef USE_SDL3 + return SDL_IOFromFile(ref.path, "rb"); +#else return SDL_RWFromFile(ref.path, "rb"); +#endif #else return OpenAsset(filename, threadsafe).release(); #endif @@ -361,7 +396,7 @@ std::vector GetMPQSearchPaths() paths.emplace_back(); // PWD } - if (SDL_LOG_PRIORITY_VERBOSE >= SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION)) { + if (IsLogLevel(LogCategory::Application, SDL_LOG_PRIORITY_VERBOSE)) { LogVerbose("Paths:\n base: {}\n pref: {}\n config: {}\n assets: {}", paths::BasePath(), paths::PrefPath(), paths::ConfigPath(), paths::AssetsPath()); diff --git a/Source/engine/assets.hpp b/Source/engine/assets.hpp index 776b132eb..9deca1852 100644 --- a/Source/engine/assets.hpp +++ b/Source/engine/assets.hpp @@ -9,7 +9,13 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else #include +#endif + #include #include @@ -114,8 +120,13 @@ struct AssetRef { uint32_t fileNumber; std::string_view filename; +#ifdef USE_SDL3 + // Alternatively, a direct SDL_IOStream handle: + SDL_IOStream *directHandle = nullptr; +#else // Alternatively, a direct SDL_RWops handle: SDL_RWops *directHandle = nullptr; +#endif AssetRef() = default; @@ -130,8 +141,7 @@ struct AssetRef { AssetRef &operator=(AssetRef &&other) noexcept { - if (directHandle != nullptr) - SDL_RWclose(directHandle); + closeDirectHandle(); archive = other.archive; fileNumber = other.fileNumber; filename = other.filename; @@ -142,8 +152,7 @@ struct AssetRef { ~AssetRef() { - if (directHandle != nullptr) - SDL_RWclose(directHandle); + closeDirectHandle(); } [[nodiscard]] bool ok() const @@ -163,16 +172,42 @@ struct AssetRef { int32_t error; return archive->GetUnpackedFileSize(fileNumber, error); } +#ifdef USE_SDL3 + return static_cast(SDL_GetIOSize(directHandle)); +#else return static_cast(SDL_RWsize(directHandle)); +#endif + } + +private: + void closeDirectHandle() + { + if (directHandle != nullptr) { +#ifdef USE_SDL3 + SDL_CloseIO(directHandle); +#else + SDL_RWclose(directHandle); +#endif + } } }; struct AssetHandle { +#ifdef USE_SDL3 + SDL_IOStream *handle = nullptr; +#else SDL_RWops *handle = nullptr; +#endif AssetHandle() = default; - explicit AssetHandle(SDL_RWops *handle) + explicit AssetHandle( +#ifdef USE_SDL3 + SDL_IOStream * +#else + SDL_RWops * +#endif + handle) : handle(handle) { } @@ -185,9 +220,7 @@ struct AssetHandle { AssetHandle &operator=(AssetHandle &&other) noexcept { - if (handle != nullptr) { - SDL_RWclose(handle); - } + closeHandle(); handle = other.handle; other.handle = nullptr; return *this; @@ -195,8 +228,7 @@ struct AssetHandle { ~AssetHandle() { - if (handle != nullptr) - SDL_RWclose(handle); + closeHandle(); } [[nodiscard]] bool ok() const @@ -206,7 +238,9 @@ struct AssetHandle { bool read(void *buffer, size_t len) { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#ifdef USE_SDL3 + return SDL_ReadIO(handle, buffer, len) == len; +#elif SDL_VERSION_ATLEAST(2, 0, 0) return handle->read(handle, buffer, len, 1) == 1; #else return handle->read(handle, buffer, static_cast(len), 1) == 1; @@ -215,7 +249,11 @@ struct AssetHandle { bool seek(long pos) { +#ifdef USE_SDL3 + return SDL_SeekIO(handle, pos, SDL_IO_SEEK_SET) != -1; +#else return handle->seek(handle, pos, RW_SEEK_SET) != -1; +#endif } [[nodiscard]] const char *error() const @@ -223,12 +261,33 @@ struct AssetHandle { return SDL_GetError(); } +#ifdef USE_SDL3 + SDL_IOStream *release() && + { + SDL_IOStream *result = handle; + handle = nullptr; + return result; + } +#else SDL_RWops *release() && { SDL_RWops *result = handle; handle = nullptr; return result; } +#endif + +private: + void closeHandle() + { + if (handle != nullptr) { +#ifdef USE_SDL3 + SDL_CloseIO(handle); +#else + SDL_RWclose(handle); +#endif + } + } }; #endif @@ -265,7 +324,12 @@ AssetHandle OpenAsset(AssetRef &&ref, bool threadsafe = false); AssetHandle OpenAsset(std::string_view filename, bool threadsafe = false); AssetHandle OpenAsset(std::string_view filename, size_t &fileSize, bool threadsafe = false); -SDL_RWops *OpenAssetAsSdlRwOps(std::string_view filename, bool threadsafe = false); +#ifdef USE_SDL3 +SDL_IOStream * +#else +SDL_RWops * +#endif +OpenAssetAsSdlRwOps(std::string_view filename, bool threadsafe = false); struct AssetData { std::unique_ptr data; diff --git a/Source/engine/load_pcx.cpp b/Source/engine/load_pcx.cpp index 48b23a8b9..9d2d56537 100644 --- a/Source/engine/load_pcx.cpp +++ b/Source/engine/load_pcx.cpp @@ -10,7 +10,11 @@ #include #endif +#ifdef USE_SDL3 +#include +#else #include +#endif #include "mpq/mpq_common.hpp" #include "utils/log.hpp" diff --git a/Source/engine/load_pcx.hpp b/Source/engine/load_pcx.hpp index 6c8dcecb4..2ab6655c2 100644 --- a/Source/engine/load_pcx.hpp +++ b/Source/engine/load_pcx.hpp @@ -3,7 +3,11 @@ #include #include +#ifdef USE_SDL3 +#include +#else #include +#endif #include "engine/clx_sprite.hpp" diff --git a/Source/engine/palette.cpp b/Source/engine/palette.cpp index 3189c2b14..670c45179 100644 --- a/Source/engine/palette.cpp +++ b/Source/engine/palette.cpp @@ -11,6 +11,12 @@ #include #include +#ifdef USE_SDL3 +#include +#else +#include +#endif + #include "engine/backbuffer_state.hpp" #include "engine/demomode.h" #include "engine/dx.h" diff --git a/Source/engine/palette.h b/Source/engine/palette.h index 1936fef9f..69feb62a9 100644 --- a/Source/engine/palette.h +++ b/Source/engine/palette.h @@ -9,7 +9,11 @@ #include #include +#ifdef USE_SDL3 +#include +#else #include +#endif #include "levels/gendung_defs.hpp" diff --git a/Source/engine/sound_defs.hpp b/Source/engine/sound_defs.hpp index 8a2a425de..47a2336d4 100644 --- a/Source/engine/sound_defs.hpp +++ b/Source/engine/sound_defs.hpp @@ -1,6 +1,10 @@ #pragma once +#ifdef USE_SDL3 +#include +#else #include +#endif #define VOLUME_MIN -1600 #define VOLUME_MAX 0 diff --git a/Source/engine/surface.hpp b/Source/engine/surface.hpp index 7599b948b..7dd9641af 100644 --- a/Source/engine/surface.hpp +++ b/Source/engine/surface.hpp @@ -3,6 +3,10 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else #include #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -12,6 +16,7 @@ #include "utils/sdl2_to_1_2_backports.h" #include #endif +#endif #include "engine/point.hpp" #include "utils/sdl_geometry.h" diff --git a/Source/engine/ticks.cpp b/Source/engine/ticks.cpp index c681ae885..9e45d6081 100644 --- a/Source/engine/ticks.cpp +++ b/Source/engine/ticks.cpp @@ -2,7 +2,11 @@ #include +#ifdef USE_SDL3 +#include +#else #include +#endif namespace devilution { diff --git a/Source/interfac.cpp b/Source/interfac.cpp index 5da1738e3..3d65b1af3 100644 --- a/Source/interfac.cpp +++ b/Source/interfac.cpp @@ -8,7 +8,13 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else #include +#endif + #include #include "control.h" diff --git a/Source/interfac.h b/Source/interfac.h index 5face5d13..ee4ac6c92 100644 --- a/Source/interfac.h +++ b/Source/interfac.h @@ -7,7 +7,12 @@ #include +#ifdef USE_SDL3 +#include +#include +#else #include +#endif #include "utils/ui_fwd.h" diff --git a/Source/mpq/mpq_sdl_rwops.cpp b/Source/mpq/mpq_sdl_rwops.cpp index 6396ae179..66ea998be 100644 --- a/Source/mpq/mpq_sdl_rwops.cpp +++ b/Source/mpq/mpq_sdl_rwops.cpp @@ -6,6 +6,12 @@ #include #include +#ifdef USE_SDL3 +#include +#else +#include +#endif + namespace devilution { namespace { @@ -26,6 +32,9 @@ struct Data { std::unique_ptr blockData; }; +#ifdef USE_SDL3 +Data *GetData(void *userdata) { return reinterpret_cast(userdata); } +#else Data *GetData(struct SDL_RWops *context) { return reinterpret_cast(context->hidden.unknown.data1); @@ -35,6 +44,7 @@ void SetData(struct SDL_RWops *context, Data *data) { context->hidden.unknown.data1 = data; } +#endif #ifndef USE_SDL1 using OffsetType = Sint64; @@ -47,24 +57,46 @@ using SizeType = int; extern "C" { #ifndef USE_SDL1 -static Sint64 MpqFileRwSize(struct SDL_RWops *context) +static Sint64 MpqFileRwSize( +#ifdef USE_SDL3 + void * +#else + struct SDL_RWops * +#endif + context) { - return GetData(context)->size; + return static_cast(GetData(context)->size); } #endif +#ifdef USE_SDL3 +static Sint64 MpqFileRwSeek(void *context, Sint64 offset, SDL_IOWhence whence) +#else static OffsetType MpqFileRwSeek(struct SDL_RWops *context, OffsetType offset, int whence) +#endif { Data &data = *GetData(context); OffsetType newPosition; switch (whence) { +#ifdef USE_SDL3 + case SDL_IO_SEEK_SET: +#else case RW_SEEK_SET: +#endif newPosition = offset; break; +#ifdef USE_SDL3 + case SDL_IO_SEEK_CUR: +#else case RW_SEEK_CUR: +#endif newPosition = static_cast(data.position + offset); break; +#ifdef USE_SDL3 + case SDL_IO_SEEK_END: +#else case RW_SEEK_END: +#endif newPosition = static_cast(data.size + offset); break; default: @@ -92,8 +124,15 @@ static OffsetType MpqFileRwSeek(struct SDL_RWops *context, OffsetType offset, in return newPosition; } +#ifdef USE_SDL3 +static SizeType MpqFileRwRead(void *context, void *ptr, size_t size, SDL_IOStatus *status) +#else static SizeType MpqFileRwRead(struct SDL_RWops *context, void *ptr, SizeType size, SizeType maxnum) +#endif { +#ifdef USE_SDL3 + const size_t maxnum = 1; +#endif Data &data = *GetData(context); const size_t totalSize = size * maxnum; size_t remainingSize = totalSize; @@ -104,9 +143,12 @@ static SizeType MpqFileRwRead(struct SDL_RWops *context, void *ptr, SizeType siz data.blockData = std::unique_ptr { new uint8_t[data.blockSize] }; } - uint32_t blockNumber = static_cast(data.position / data.blockSize); + auto blockNumber = static_cast(data.position / data.blockSize); while (remainingSize > 0) { if (data.position == data.size) { +#ifdef USE_SDL3 + *status = SDL_IO_STATUS_EOF; +#endif break; } @@ -121,13 +163,17 @@ static SizeType MpqFileRwRead(struct SDL_RWops *context, void *ptr, SizeType siz data.blockRead = true; } - const size_t blockPosition = data.position - blockNumber * data.blockSize; + const size_t blockPosition = data.position - (blockNumber * data.blockSize); const size_t remainingBlockSize = currentBlockSize - blockPosition; if (remainingSize < remainingBlockSize) { std::memcpy(out, data.blockData.get() + blockPosition, remainingSize); data.position += remainingSize; +#ifdef USE_SDL3 + return size; +#else return maxnum; +#endif } std::memcpy(out, data.blockData.get() + blockPosition, remainingBlockSize); @@ -138,30 +184,55 @@ static SizeType MpqFileRwRead(struct SDL_RWops *context, void *ptr, SizeType siz data.blockRead = false; } +#ifdef USE_SDL3 + return static_cast(totalSize - remainingSize); +#else return static_cast((totalSize - remainingSize) / size); +#endif } +#ifdef USE_SDL3 +static bool MpqFileRwClose(void *context) +#else static int MpqFileRwClose(struct SDL_RWops *context) +#endif { Data *data = GetData(context); data->mpqArchive->CloseBlockOffsetTable(data->fileNumber); delete data; +#ifdef USE_SDL3 + return true; +#else delete context; return 0; +#endif } } // extern "C" } // namespace -SDL_RWops *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, std::string_view filename, bool threadsafe) +#ifdef USE_SDL3 +SDL_IOStream * +#else +SDL_RWops * +#endif +SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, std::string_view filename, bool threadsafe) { +#ifdef USE_SDL3 + SDL_IOStreamInterface interface; + SDL_INIT_INTERFACE(&interface); + SDL_IOStreamInterface *result = &interface; +#else auto result = std::make_unique(); std::memset(result.get(), 0, sizeof(*result)); +#endif #ifndef USE_SDL1 result->size = &MpqFileRwSize; +#ifndef USE_SDL3 result->type = SDL_RWOPS_UNKNOWN; +#endif #else result->type = 0; #endif @@ -170,6 +241,9 @@ SDL_RWops *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, st result->read = &MpqFileRwRead; result->write = nullptr; result->close = &MpqFileRwClose; +#ifdef USE_SDL3 + result->flush = nullptr; +#endif auto data = std::make_unique(); int32_t error = 0; @@ -226,8 +300,12 @@ SDL_RWops *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, st data->position = 0; data->blockRead = false; +#ifdef USE_SDL3 + return SDL_OpenIO(&interface, data.release()); +#else SetData(result.get(), data.release()); return result.release(); +#endif } } // namespace devilution diff --git a/Source/mpq/mpq_sdl_rwops.hpp b/Source/mpq/mpq_sdl_rwops.hpp index ea57da27b..6bc2f897a 100644 --- a/Source/mpq/mpq_sdl_rwops.hpp +++ b/Source/mpq/mpq_sdl_rwops.hpp @@ -3,12 +3,21 @@ #include #include +#ifdef USE_SDL3 +#include +#else #include +#endif #include "mpq/mpq_reader.hpp" namespace devilution { -SDL_RWops *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, std::string_view filename, bool threadsafe); +#ifdef USE_SDL3 +SDL_IOStream * +#else +SDL_RWops * +#endif +SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, std::string_view filename, bool threadsafe); } // namespace devilution diff --git a/Source/mpq/mpq_writer.cpp b/Source/mpq/mpq_writer.cpp index c3909c852..1ba4c5cb2 100644 --- a/Source/mpq/mpq_writer.cpp +++ b/Source/mpq/mpq_writer.cpp @@ -6,11 +6,11 @@ #include #include -#include #include #include "appfat.h" #include "encrypt.h" +#include "utils/endian_swap.hpp" #include "utils/file_util.h" #include "utils/language.h" #include "utils/log.hpp" @@ -63,15 +63,15 @@ constexpr uint32_t MinBlockSize = 1024; void ByteSwapHdr(MpqFileHeader *hdr) { - hdr->signature = SDL_SwapLE32(hdr->signature); - hdr->headerSize = SDL_SwapLE32(hdr->headerSize); - hdr->fileSize = SDL_SwapLE32(hdr->fileSize); - hdr->version = SDL_SwapLE16(hdr->version); - hdr->blockSizeFactor = SDL_SwapLE16(hdr->blockSizeFactor); - hdr->hashEntriesOffset = SDL_SwapLE32(hdr->hashEntriesOffset); - hdr->blockEntriesOffset = SDL_SwapLE32(hdr->blockEntriesOffset); - hdr->hashEntriesCount = SDL_SwapLE32(hdr->hashEntriesCount); - hdr->blockEntriesCount = SDL_SwapLE32(hdr->blockEntriesCount); + hdr->signature = Swap32LE(hdr->signature); + hdr->headerSize = Swap32LE(hdr->headerSize); + hdr->fileSize = Swap32LE(hdr->fileSize); + hdr->version = Swap16LE(hdr->version); + hdr->blockSizeFactor = Swap16LE(hdr->blockSizeFactor); + hdr->hashEntriesOffset = Swap32LE(hdr->hashEntriesOffset); + hdr->blockEntriesOffset = Swap32LE(hdr->blockEntriesOffset); + hdr->hashEntriesCount = Swap32LE(hdr->hashEntriesCount); + hdr->blockEntriesCount = Swap32LE(hdr->blockEntriesCount); } bool IsAllocatedUnusedBlock(const MpqBlockEntry *block) @@ -421,7 +421,7 @@ bool MpqWriter::WriteFileContents(const std::byte *fileData, uint32_t fileSize, len = PkwareCompress(mpqBuf, len); if (!stream_.Write(reinterpret_cast(&mpqBuf[0]), len)) return false; - offsetTable[curSector++] = SDL_SwapLE32(destSize); + offsetTable[curSector++] = Swap32LE(destSize); destSize += len; // compressed length if (fileSize <= BlockSize) break; @@ -429,7 +429,7 @@ bool MpqWriter::WriteFileContents(const std::byte *fileData, uint32_t fileSize, fileSize -= BlockSize; } - offsetTable[numSectors] = SDL_SwapLE32(destSize); + offsetTable[numSectors] = Swap32LE(destSize); if (!stream_.Seekp(block->offset, SEEK_SET)) return false; if (!stream_.Write(reinterpret_cast(offsetTable.get()), offsetTableByteSize)) diff --git a/Source/options.cpp b/Source/options.cpp index 2bfe3fa73..0144df66f 100644 --- a/Source/options.cpp +++ b/Source/options.cpp @@ -17,7 +17,15 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#include +#include +#else #include +#endif + #include #include #include @@ -36,6 +44,7 @@ #include "utils/log.hpp" #include "utils/logged_fstream.hpp" #include "utils/paths.h" +#include "utils/sdl_ptrs.h" #include "utils/str_cat.hpp" #include "utils/str_split.hpp" #include "utils/utf8.hpp" @@ -198,6 +207,8 @@ bool HardwareCursorSupported() { #if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) || __DJGPP__ return false; +#elif USE_SDL3 + return true; #else SDL_version v; SDL_GetVersion(&v); @@ -645,7 +656,11 @@ void OptionEntryAudioDevice::SaveToIni(std::string_view category) const size_t OptionEntryAudioDevice::GetListSize() const { -#if SDL_VERSION_ATLEAST(2, 0, 0) +#if defined(USE_SDL3) + int numDevices = 0; + SDLUniquePtr devices { SDL_GetAudioPlaybackDevices(&numDevices) }; + return static_cast(numDevices) + 1; +#elif SDL_VERSION_ATLEAST(2, 0, 0) return SDL_GetNumAudioDevices(false) + 1; #else return 1; @@ -661,12 +676,22 @@ std::string_view OptionEntryAudioDevice::GetListDescription(size_t index) const size_t OptionEntryAudioDevice::GetActiveListIndex() const { +#ifdef USE_SDL3 + int numDevices; + SDLUniquePtr devices { SDL_GetAudioPlaybackDevices(&numDevices) }; + if (devices == nullptr) return 0; + for (int i = 0; i < numDevices; ++i) { + const char *deviceName = SDL_GetAudioDeviceName(devices.get()[i]); + if (deviceName_ == deviceName) return i; + } + return 0; +#else for (size_t i = 0; i < GetListSize(); i++) { const std::string_view deviceName = GetDeviceName(i); - if (deviceName == deviceName_) - return i; + if (deviceName_ == deviceName) return i; } return 0; +#endif } void OptionEntryAudioDevice::SetActiveListIndex(size_t index) @@ -677,11 +702,18 @@ void OptionEntryAudioDevice::SetActiveListIndex(size_t index) std::string_view OptionEntryAudioDevice::GetDeviceName(size_t index) const { -#if SDL_VERSION_ATLEAST(2, 0, 0) - if (index != 0) - return SDL_GetAudioDeviceName(static_cast(index) - 1, false); + if (index == 0) return {}; // System Default +#if defined(USE_SDL3) + int numDevices = 0; + SDLUniquePtr devices { SDL_GetAudioPlaybackDevices(&numDevices) }; + if (devices == nullptr || static_cast(index) > numDevices) return "Unknown"; + const char *deviceName = SDL_GetAudioDeviceName(devices.get()[index - 1]); + if (deviceName == nullptr) return "Unknown"; + return deviceName; +#elif SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_GetAudioDeviceName(static_cast(index) - 1, false); #endif - return ""; + return {}; } GraphicsOptions::GraphicsOptions() @@ -1078,12 +1110,26 @@ KeymapperOptions::KeymapperOptions() keyIDToKeyName.emplace(MouseScrollLeftButton, "SCROLLLEFTMOUSE"); keyIDToKeyName.emplace(MouseScrollRightButton, "SCROLLRIGHTMOUSE"); - keyIDToKeyName.emplace(SDLK_BACKQUOTE, "`"); + keyIDToKeyName.emplace( +#ifdef USE_SDL3 + SDLK_GRAVE +#else + SDLK_BACKQUOTE +#endif + , + "`"); keyIDToKeyName.emplace(SDLK_LEFTBRACKET, "["); keyIDToKeyName.emplace(SDLK_RIGHTBRACKET, "]"); keyIDToKeyName.emplace(SDLK_BACKSLASH, "\\"); keyIDToKeyName.emplace(SDLK_SEMICOLON, ";"); - keyIDToKeyName.emplace(SDLK_QUOTE, "'"); + keyIDToKeyName.emplace( +#ifdef USE_SDL3 + SDLK_APOSTROPHE +#else + SDLK_QUOTE +#endif + , + "'"); keyIDToKeyName.emplace(SDLK_COMMA, ","); keyIDToKeyName.emplace(SDLK_PERIOD, "."); keyIDToKeyName.emplace(SDLK_SLASH, "/"); diff --git a/Source/options.h b/Source/options.h index b18ed5c9d..f4157bc9f 100644 --- a/Source/options.h +++ b/Source/options.h @@ -12,7 +12,12 @@ #include #include +#ifdef USE_SDL3 +#include +#else #include +#endif + #include #include diff --git a/Source/utils/display.cpp b/Source/utils/display.cpp index b6f19b2e5..0150ee9c5 100644 --- a/Source/utils/display.cpp +++ b/Source/utils/display.cpp @@ -52,21 +52,21 @@ SDL_Window *ghMainWnd; Size forceResolution; -Uint16 gnScreenWidth; -Uint16 gnScreenHeight; -Uint16 gnViewportHeight; +uint16_t gnScreenWidth; +uint16_t gnScreenHeight; +uint16_t gnViewportHeight; -Uint16 GetScreenWidth() +uint16_t GetScreenWidth() { return gnScreenWidth; } -Uint16 GetScreenHeight() +uint16_t GetScreenHeight() { return gnScreenHeight; } -Uint16 GetViewportHeight() +uint16_t GetViewportHeight() { return gnViewportHeight; } diff --git a/Source/utils/endian_swap.hpp b/Source/utils/endian_swap.hpp new file mode 100644 index 000000000..3aafc669c --- /dev/null +++ b/Source/utils/endian_swap.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#ifdef USE_SDL3 +#include +#else +#include +#endif + +namespace devilution { + +constexpr uint16_t Swap16LE(uint16_t val) +{ +#ifdef USE_SDL3 + return SDL_Swap16LE(val); +#else + return SDL_SwapLE16(val); +#endif +} + +constexpr uint32_t Swap32LE(uint32_t val) +{ +#ifdef USE_SDL3 + return SDL_Swap32LE(val); +#else + return SDL_SwapLE32(val); +#endif +} + +} // namespace devilution diff --git a/Source/utils/endian_write.hpp b/Source/utils/endian_write.hpp index 7354bdf0e..b6adf11ba 100644 --- a/Source/utils/endian_write.hpp +++ b/Source/utils/endian_write.hpp @@ -3,19 +3,19 @@ #include #include -#include +#include "utils/endian_swap.hpp" namespace devilution { inline void WriteLE16(void *out, uint16_t val) { - const uint16_t littleEndian = SDL_SwapLE16(val); + const uint16_t littleEndian = Swap16LE(val); memcpy(out, &littleEndian, 2); } inline void WriteLE32(void *out, uint32_t val) { - const uint32_t littleEndian = SDL_SwapLE32(val); + const uint32_t littleEndian = Swap32LE(val); memcpy(out, &littleEndian, 4); } diff --git a/Source/utils/file_util.cpp b/Source/utils/file_util.cpp index f2b9569a3..421c0970a 100644 --- a/Source/utils/file_util.cpp +++ b/Source/utils/file_util.cpp @@ -6,7 +6,11 @@ #include #include +#ifdef USE_SDL3 +#include +#else #include +#endif #include "utils/log.hpp" #include "utils/stdcompat/filesystem.hpp" @@ -112,10 +116,14 @@ bool FileExists(const char *path) #elif defined(DVL_HAS_FILESYSTEM) std::error_code ec; return std::filesystem::exists(reinterpret_cast(path), ec); +#elif USE_SDL3 + SDL_IOStream *file = SDL_IOFromFile(path, "rb"); + if (file == nullptr) return false; + SDL_CloseIO(file); + return true; #else - SDL_RWops *file = SDL_RWFromFile(path, "r+b"); - if (file == nullptr) - return false; + SDL_RWops *file = SDL_RWFromFile(path, "rb"); + if (file == nullptr) return false; SDL_RWclose(file); return true; #endif @@ -176,12 +184,18 @@ bool FileExistsAndIsWriteable(const char *path) #else if (!FileExists(path)) return false; - SDL_RWops *file = SDL_RWFromFile(path, "a+b"); - if (file == nullptr) - return false; +#ifdef USE_SDL3 + SDL_IOStream *file = SDL_IOFromFile(path, "ab"); + if (file == nullptr) return false; + SDL_CloseIO(file); + return true; +#else + SDL_RWops *file = SDL_RWFromFile(path, "ab"); + if (file == nullptr) return false; SDL_RWclose(file); return true; #endif +#endif } bool GetFileSize(const char *path, std::uintmax_t *size) diff --git a/Source/utils/log.hpp b/Source/utils/log.hpp index f50f2f129..cfacf1a7a 100644 --- a/Source/utils/log.hpp +++ b/Source/utils/log.hpp @@ -2,7 +2,12 @@ #include +#ifdef USE_SDL3 +#include +#else #include +#endif + #include #include #include @@ -85,10 +90,19 @@ inline void LogVerbose(LogCategory category, std::string_view str) SDL_LogVerbose(static_cast(category), "%.*s", static_cast(str.size()), str.data()); } +inline bool IsLogLevel(LogCategory category, SDL_LogPriority priority) +{ +#ifdef USE_SDL3 + return SDL_GetLogPriority(static_cast(category)) <= priority; +#else + return SDL_LogGetPriority(static_cast(category)) <= priority; +#endif +} + template void LogVerbose(LogCategory category, std::string_view fmt, Args &&...args) { - if (SDL_LogGetPriority(static_cast(category)) > SDL_LOG_PRIORITY_VERBOSE) return; + if (!IsLogLevel(category, SDL_LOG_PRIORITY_VERBOSE)) return; auto str = detail::format(fmt, std::forward(args)...); SDL_LogVerbose(static_cast(category), "%s", str.c_str()); } @@ -107,7 +121,7 @@ inline void LogDebug(LogCategory category, std::string_view str) template void LogDebug(LogCategory category, std::string_view fmt, Args &&...args) { - if (SDL_LogGetPriority(static_cast(category)) > SDL_LOG_PRIORITY_DEBUG) return; + if (!IsLogLevel(category, SDL_LOG_PRIORITY_DEBUG)) return; auto str = detail::format(fmt, std::forward(args)...); SDL_LogDebug(static_cast(category), "%s", str.c_str()); } diff --git a/Source/utils/palette_blending.cpp b/Source/utils/palette_blending.cpp index 94cdbb4c4..8facad672 100644 --- a/Source/utils/palette_blending.cpp +++ b/Source/utils/palette_blending.cpp @@ -2,9 +2,12 @@ #include #include -#include +#ifdef USE_SDL3 +#include +#else #include +#endif #include "utils/palette_kd_tree.hpp" diff --git a/Source/utils/palette_blending.hpp b/Source/utils/palette_blending.hpp index a5185007c..5a0e84647 100644 --- a/Source/utils/palette_blending.hpp +++ b/Source/utils/palette_blending.hpp @@ -2,7 +2,11 @@ #include +#ifdef USE_SDL3 +#include +#else #include +#endif namespace devilution { diff --git a/Source/utils/palette_kd_tree.cpp b/Source/utils/palette_kd_tree.cpp index aa137220f..15dec07d7 100644 --- a/Source/utils/palette_kd_tree.cpp +++ b/Source/utils/palette_kd_tree.cpp @@ -7,7 +7,9 @@ #include #include -#ifdef USE_SDL1 +#ifdef USE_SDL3 +#include +#elif defined(USE_SDL1) #include #else #include diff --git a/Source/utils/palette_kd_tree.hpp b/Source/utils/palette_kd_tree.hpp index af9208b27..85e139176 100644 --- a/Source/utils/palette_kd_tree.hpp +++ b/Source/utils/palette_kd_tree.hpp @@ -8,7 +8,9 @@ #include #include -#ifdef USE_SDL1 +#ifdef USE_SDL3 +#include +#elif defined(USE_SDL1) #include #else #include diff --git a/Source/utils/paths.cpp b/Source/utils/paths.cpp index 6b300742e..aec13ebcb 100644 --- a/Source/utils/paths.cpp +++ b/Source/utils/paths.cpp @@ -4,7 +4,12 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else #include +#endif #include "appfat.h" #include "utils/file_util.h" @@ -67,16 +72,31 @@ const std::string &NxdkGetPrefPath() } #endif +std::string GetSdlBasePath() +{ + std::string result; +#if defined(__DJGPP__) + // In DOS, use an empty base path. +#elif defined(USE_SDL3) + const char *s = SDL_GetBasePath(); + if (s == nullptr) { + LogError("{}", SDL_GetError()); + SDL_ClearError(); + } else { + result = s; + } +#else + result = FromSDL(SDL_GetBasePath()); +#endif + return result; +} + } // namespace const std::string &BasePath() { if (!basePath) { -#if defined(__DJGPP__) - basePath = std::string(); -#else - basePath = FromSDL(SDL_GetBasePath()); -#endif + basePath = GetSdlBasePath(); } return *basePath; } @@ -148,9 +168,9 @@ const std::string &AssetsPath() // // Note that SDL3 reverts to SDL1 behaviour! // https://github.com/libsdl-org/SDL/blob/962268ca21ed10b9cee31198c22681099293f20a/docs/README-migration.md?plain=1#L1623 - assetsPath.emplace(FromSDL(SDL_GetBasePath())); + assetsPath.emplace(GetSdlBasePath()); #else - assetsPath.emplace(FromSDL(SDL_GetBasePath()) + ("assets" DIRECTORY_SEPARATOR_STR)); + assetsPath.emplace(GetSdlBasePath() + ("assets" DIRECTORY_SEPARATOR_STR)); #endif } return *assetsPath; diff --git a/Source/utils/pcx_to_clx.cpp b/Source/utils/pcx_to_clx.cpp index d00c74832..82c07970d 100644 --- a/Source/utils/pcx_to_clx.cpp +++ b/Source/utils/pcx_to_clx.cpp @@ -9,11 +9,16 @@ #include #include -#include +#ifdef USE_SDL3 +#include +#else +#include +#endif #include "appfat.h" #include "utils/clx_encode.hpp" #include "utils/endian_read.hpp" +#include "utils/endian_swap.hpp" #include "utils/endian_write.hpp" #include "utils/pcx.hpp" @@ -47,8 +52,8 @@ bool LoadPcxMeta(AssetHandle &handle, int &width, int &height, uint8_t &bpp) if (!handle.read(&pcxhdr, PcxHeaderSize)) { return false; } - width = SDL_SwapLE16(pcxhdr.Xmax) - SDL_SwapLE16(pcxhdr.Xmin) + 1; - height = SDL_SwapLE16(pcxhdr.Ymax) - SDL_SwapLE16(pcxhdr.Ymin) + 1; + width = Swap16LE(pcxhdr.Xmax) - Swap16LE(pcxhdr.Xmin) + 1; + height = Swap16LE(pcxhdr.Ymax) - Swap16LE(pcxhdr.Ymin) + 1; bpp = pcxhdr.BitsPerPixel; return true; } diff --git a/Source/utils/pcx_to_clx.hpp b/Source/utils/pcx_to_clx.hpp index b29198b58..7504cca2a 100644 --- a/Source/utils/pcx_to_clx.hpp +++ b/Source/utils/pcx_to_clx.hpp @@ -3,7 +3,11 @@ #include #include +#ifdef USE_SDL3 +#include +#else #include +#endif #include "engine/assets.hpp" #include "engine/clx_sprite.hpp" diff --git a/Source/utils/sdl_geometry.h b/Source/utils/sdl_geometry.h index 07a9d64c0..ce0ab1bf3 100644 --- a/Source/utils/sdl_geometry.h +++ b/Source/utils/sdl_geometry.h @@ -4,6 +4,9 @@ */ #pragma once +#ifdef USE_SDL3 +#include +#else #include #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -11,6 +14,7 @@ #else #include #endif +#endif #include "engine/rectangle.hpp" diff --git a/Source/utils/sdl_ptrs.h b/Source/utils/sdl_ptrs.h index 9336399ee..6d70a743e 100644 --- a/Source/utils/sdl_ptrs.h +++ b/Source/utils/sdl_ptrs.h @@ -6,12 +6,21 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#include +#include +#include +#include +#else #include #ifdef USE_SDL1 #include "utils/sdl2_to_1_2_backports.h" #else #include "utils/sdl2_backports.h" #endif +#endif namespace devilution { @@ -21,7 +30,11 @@ namespace devilution { struct SDLSurfaceDeleter { void operator()(SDL_Surface *surface) const { +#ifdef USE_SDL3 + SDL_DestroySurface(surface); +#else SDL_FreeSurface(surface); +#endif } }; @@ -31,14 +44,16 @@ using SDLSurfaceUniquePtr = std::unique_ptr; struct SDLCursorDeleter { void operator()(SDL_Cursor *cursor) const { +#ifdef USE_SDL3 + SDL_DestroyCursor(cursor); +#else SDL_FreeCursor(cursor); +#endif } }; using SDLCursorUniquePtr = std::unique_ptr; -#endif -#ifndef USE_SDL1 struct SDLTextureDeleter { void operator()(SDL_Texture *texture) const { @@ -52,7 +67,11 @@ using SDLTextureUniquePtr = std::unique_ptr; struct SDLPaletteDeleter { void operator()(SDL_Palette *palette) const { +#ifdef USE_SDL3 + SDL_DestroyPalette(palette); +#else SDL_FreePalette(palette); +#endif } }; diff --git a/Source/utils/sdl_thread.h b/Source/utils/sdl_thread.h index 6a78ea70d..f9e0f8a21 100644 --- a/Source/utils/sdl_thread.h +++ b/Source/utils/sdl_thread.h @@ -2,18 +2,27 @@ #include +#ifdef USE_SDL3 +#include +#else #include #ifdef USE_SDL1 #include "utils/sdl2_to_1_2_backports.h" #endif +#endif + #include "appfat.h" #include "utils/attributes.h" namespace devilution { namespace this_sdl_thread { +#ifdef USE_SDL3 +inline SDL_ThreadID get_id() +#else inline SDL_threadID get_id() +#endif { #if defined(__DJGPP__) return 1; @@ -39,7 +48,12 @@ public: { return false; } + +#ifdef USE_SDL3 + SDL_ThreadID get_id() const +#else SDL_threadID get_id() const +#endif { return this_sdl_thread::get_id(); } @@ -79,7 +93,11 @@ public: return thread != nullptr; } +#ifdef USE_SDL3 + SDL_ThreadID get_id() const +#else SDL_threadID get_id() const +#endif { return SDL_GetThreadID(thread.get()); } diff --git a/Source/utils/sdl_wrap.h b/Source/utils/sdl_wrap.h index 3fc22d8f8..59607beb5 100644 --- a/Source/utils/sdl_wrap.h +++ b/Source/utils/sdl_wrap.h @@ -1,72 +1,118 @@ -#pragma once - -#include -#ifdef USE_SDL1 -#include "utils/sdl2_to_1_2_backports.h" -#else -#include "utils/sdl2_backports.h" -#endif - -#include "appfat.h" -#include "utils/sdl_ptrs.h" - -#define NonNull(x) NullErrDlg(x, __FILE__, __LINE__) - -namespace devilution { - -namespace SDLWrap { - -template -T NullErrDlg(T x, const char *file, int line) -{ - if (x == nullptr) - ErrDlg("SDL Error", SDL_GetError(), file, line); - return x; -} - -inline SDLSurfaceUniquePtr CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) -{ - return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask)) }; -} - -inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format) -{ - return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format)) }; -} - -inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 format) -{ - return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormatFrom(pixels, width, height, depth, pitch, format)) }; -} - -#ifndef USE_SDL1 -inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, const SDL_PixelFormat *fmt, Uint32 flags) -#else -inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags) -#endif -{ - return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurface(src, fmt, flags)) }; -} - -#ifndef USE_SDL1 -inline SDLSurfaceUniquePtr ConvertSurfaceFormat(SDL_Surface *src, Uint32 pixel_format, Uint32 flags) -{ - return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurfaceFormat(src, pixel_format, flags)) }; -} -#endif - -#ifndef USE_SDL1 -inline SDLTextureUniquePtr CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h) -{ - return SDLTextureUniquePtr { NonNull(SDL_CreateTexture(renderer, format, access, w, h)) }; -} -#endif - -inline SDLPaletteUniquePtr AllocPalette(int ncolors = 256) -{ - return SDLPaletteUniquePtr { NonNull(SDL_AllocPalette(ncolors)) }; -} - -} // namespace SDLWrap - -} // namespace devilution +#pragma once + +#ifdef USE_SDL3 +#include +#include +#include +#include +#include +#else +#include +#ifdef USE_SDL1 +#include "utils/sdl2_to_1_2_backports.h" +#else +#include "utils/sdl2_backports.h" +#endif +#endif + +#include "appfat.h" +#include "utils/sdl_ptrs.h" + +#define NonNull(x) NullErrDlg(x, __FILE__, __LINE__) + +namespace devilution { + +namespace SDLWrap { + +template +T NullErrDlg(T x, const char *file, int line) +{ + if (x == nullptr) + ErrDlg("SDL Error", SDL_GetError(), file, line); + return x; +} + +inline SDLSurfaceUniquePtr CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) +{ +#ifdef USE_SDL3 + return SDLSurfaceUniquePtr { NonNull(SDL_CreateSurface(width, height, SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask))) }; +#else + return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask)) }; +#endif +} + +#ifdef USE_SDL3 +inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, SDL_PixelFormat format) +{ + return SDLSurfaceUniquePtr { NonNull(SDL_CreateSurface(width, height, format)) }; +} +#else +inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format) +{ + return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format)) }; +} +#endif + +#ifdef USE_SDL3 +inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, SDL_PixelFormat format) +{ + return SDLSurfaceUniquePtr { NonNull(SDL_CreateSurfaceFrom(width, height, format, pixels, pitch)) }; +} +#else +inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 format) +{ + return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormatFrom(pixels, width, height, depth, pitch, format)) }; +} +#endif + +#ifndef USE_SDL1 +inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, const SDL_PixelFormat *fmt, Uint32 flags) +#else +inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags) +#endif +{ +#ifdef USE_SDL3 + return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurface(src, *fmt)) }; +#else + return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurface(src, fmt, flags)) }; +#endif +} + +#ifndef USE_SDL1 +#ifdef USE_SDL3 +inline SDLSurfaceUniquePtr ConvertSurfaceFormat(SDL_Surface *src, SDL_PixelFormat format, Uint32 flags) +{ + return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurface(src, format)) }; +} +#else +inline SDLSurfaceUniquePtr ConvertSurfaceFormat(SDL_Surface *src, Uint32 format, Uint32 flags) +{ + return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurfaceFormat(src, format, flags)) }; +} +#endif + +#ifdef USE_SDL3 +inline SDLTextureUniquePtr CreateTexture(SDL_Renderer *renderer, SDL_PixelFormat format, SDL_TextureAccess access, int w, int h) +{ + return SDLTextureUniquePtr { NonNull(SDL_CreateTexture(renderer, format, access, w, h)) }; +} +#else +inline SDLTextureUniquePtr CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h) +{ + return SDLTextureUniquePtr { NonNull(SDL_CreateTexture(renderer, format, access, w, h)) }; +} +#endif +#endif + +inline SDLPaletteUniquePtr AllocPalette(int ncolors = 256) +{ +#ifdef USE_SDL3 + return SDLPaletteUniquePtr { NonNull(SDL_CreatePalette(ncolors)) }; +#else + return SDLPaletteUniquePtr { NonNull(SDL_AllocPalette(ncolors)) }; +#endif +} + +} // namespace SDLWrap + +} // namespace devilution diff --git a/Source/utils/surface_to_png.cpp b/Source/utils/surface_to_png.cpp index e64abf246..7d2b8d4e4 100644 --- a/Source/utils/surface_to_png.cpp +++ b/Source/utils/surface_to_png.cpp @@ -1,21 +1,39 @@ #include "utils/surface_to_png.hpp" -#include #include +#ifdef USE_SDL3 +#include +#include +#else #include +#endif + #include #include "engine/surface.hpp" namespace devilution { +#ifndef USE_SDL3 extern "C" int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst); +#endif tl::expected -WriteSurfaceToFilePng(const Surface &buf, SDL_RWops *dst) +WriteSurfaceToFilePng(const Surface &buf, +#ifdef USE_SDL3 + SDL_IOStream * +#else + SDL_RWops * +#endif + dst) { - if (IMG_SavePNG_RW(buf.surface, dst, /*freedst=*/1) != 0) { +#ifdef USE_SDL3 + const bool ok = IMG_SavePNG_IO(buf.surface, dst, /*closeio=*/true); +#else + const bool ok = IMG_SavePNG_RW(buf.surface, dst, /*freedst=*/1) == 0; +#endif + if (!ok) { tl::expected result = tl::make_unexpected(std::string(SDL_GetError())); SDL_ClearError(); return result; diff --git a/Source/utils/surface_to_png.hpp b/Source/utils/surface_to_png.hpp index eeeed4ea0..14aede4b6 100644 --- a/Source/utils/surface_to_png.hpp +++ b/Source/utils/surface_to_png.hpp @@ -1,7 +1,13 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else #include +#endif + #include #include "engine/surface.hpp" @@ -14,6 +20,12 @@ namespace devilution { * Takes ownership of `dst` and closes it when done. */ tl::expected -WriteSurfaceToFilePng(const Surface &buf, SDL_RWops *dst); +WriteSurfaceToFilePng(const Surface &buf, +#ifdef USE_SDL3 + SDL_IOStream * +#else + SDL_RWops * +#endif + dst); } // namespace devilution diff --git a/Source/utils/ui_fwd.h b/Source/utils/ui_fwd.h index 60ec87454..ea1d3035e 100644 --- a/Source/utils/ui_fwd.h +++ b/Source/utils/ui_fwd.h @@ -1,19 +1,19 @@ #pragma once -#include +#include #include "engine/rectangle.hpp" #include "utils/attributes.h" namespace devilution { -extern DVL_API_FOR_TEST Uint16 gnScreenWidth; -extern DVL_API_FOR_TEST Uint16 gnScreenHeight; -extern DVL_API_FOR_TEST Uint16 gnViewportHeight; +extern DVL_API_FOR_TEST uint16_t gnScreenWidth; +extern DVL_API_FOR_TEST uint16_t gnScreenHeight; +extern DVL_API_FOR_TEST uint16_t gnViewportHeight; -Uint16 GetScreenWidth(); -Uint16 GetScreenHeight(); -Uint16 GetViewportHeight(); +uint16_t GetScreenWidth(); +uint16_t GetScreenHeight(); +uint16_t GetViewportHeight(); /** @brief Returns the UI (Menus, Messages, Help) can use. Currently this is 640x480 like vanilla. */ const Rectangle &GetUIRectangle(); diff --git a/test/text_render_integration_test.cpp b/test/text_render_integration_test.cpp index c3d2a7c55..07debf471 100644 --- a/test/text_render_integration_test.cpp +++ b/test/text_render_integration_test.cpp @@ -8,7 +8,13 @@ #include #include +#ifdef USE_SDL3 +#include +#include +#else #include +#endif + #include #include @@ -223,13 +229,28 @@ SDLPaletteUniquePtr LoadPalette() std::vector ReadFile(const std::string &path) { +#ifdef USE_SDL3 + SDL_IOStream *rwops = SDL_IOFromFile(path.c_str(), "rb"); +#else SDL_RWops *rwops = SDL_RWFromFile(path.c_str(), "rb"); +#endif std::vector result; if (rwops == nullptr) return result; - const size_t size = static_cast(SDL_RWsize(rwops)); + const size_t size = static_cast( +#ifdef USE_SDL3 + SDL_GetIOSize(rwops) +#else + SDL_RWsize(rwops) +#endif + ); result.resize(size); +#ifdef USE_SDL3 + SDL_ReadIO(rwops, result.data(), size); + SDL_CloseIO(rwops); +#else SDL_RWread(rwops, result.data(), size, 1); SDL_RWclose(rwops); +#endif return result; } @@ -294,9 +315,15 @@ TEST_P(TextRenderIntegrationTest, RenderAndCompareTest) const std::string actualPath = StrCat(paths::BasePath(), FixturesPath, GetParam().name, "-Actual.png"); const std::string expectedPath = StrCat(paths::BasePath(), FixturesPath, GetParam().name, ".png"); +#ifdef USE_SDL3 + SDL_IOStream *actual = SDL_IOFromFile(actualPath.c_str(), "wb"); +#else SDL_RWops *actual = SDL_RWFromFile(actualPath.c_str(), "wb"); +#endif ASSERT_NE(actual, nullptr) << SDL_GetError(); - ASSERT_TRUE(WriteSurfaceToFilePng(out, actual).has_value()); + + const tl::expected result = WriteSurfaceToFilePng(out, actual); + ASSERT_TRUE(result.has_value()) << result.error(); EXPECT_THAT(actualPath, FileContentsEq(expectedPath)); }