Browse Source

SDL3: Make `text_render_integration_test` build

Adds support for just enough SDL3 to make `text_render_integration_test`
work.
pull/8212/head
Gleb Mazovetskiy 6 months ago
parent
commit
8f6e9810d0
  1. 51
      .github/workflows/Linux_x86_64_SDL3_test.yml
  2. 2
      .github/workflows/Linux_x86_64_test.yml
  3. 1
      3rdParty/SDL3_image/CMakeLists.txt
  4. 13
      Source/appfat.cpp
  5. 11
      Source/diablo.cpp
  6. 5
      Source/diablo.h
  7. 1
      Source/encrypt.cpp
  8. 49
      Source/engine/assets.cpp
  9. 88
      Source/engine/assets.hpp
  10. 4
      Source/engine/load_pcx.cpp
  11. 4
      Source/engine/load_pcx.hpp
  12. 6
      Source/engine/palette.cpp
  13. 4
      Source/engine/palette.h
  14. 4
      Source/engine/sound_defs.hpp
  15. 5
      Source/engine/surface.hpp
  16. 4
      Source/engine/ticks.cpp
  17. 6
      Source/interfac.cpp
  18. 5
      Source/interfac.h
  19. 88
      Source/mpq/mpq_sdl_rwops.cpp
  20. 11
      Source/mpq/mpq_sdl_rwops.hpp
  21. 24
      Source/mpq/mpq_writer.cpp
  22. 64
      Source/options.cpp
  23. 5
      Source/options.h
  24. 12
      Source/utils/display.cpp
  25. 31
      Source/utils/endian_swap.hpp
  26. 6
      Source/utils/endian_write.hpp
  27. 26
      Source/utils/file_util.cpp
  28. 18
      Source/utils/log.hpp
  29. 5
      Source/utils/palette_blending.cpp
  30. 4
      Source/utils/palette_blending.hpp
  31. 4
      Source/utils/palette_kd_tree.cpp
  32. 4
      Source/utils/palette_kd_tree.hpp
  33. 34
      Source/utils/paths.cpp
  34. 11
      Source/utils/pcx_to_clx.cpp
  35. 4
      Source/utils/pcx_to_clx.hpp
  36. 4
      Source/utils/sdl_geometry.h
  37. 23
      Source/utils/sdl_ptrs.h
  38. 18
      Source/utils/sdl_thread.h
  39. 190
      Source/utils/sdl_wrap.h
  40. 24
      Source/utils/surface_to_png.cpp
  41. 14
      Source/utils/surface_to_png.hpp
  42. 14
      Source/utils/ui_fwd.h
  43. 31
      test/text_render_integration_test.cpp

51
.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

2
.github/workflows/Linux_x86_64_test.yml

@ -44,7 +44,7 @@ jobs:
- name: Build tests - name: Build tests
run: | run: |
cmake -S. -Bbuild -DENABLE_CODECOVERAGE=ON -DDEVILUTIONX_SYSTEM_BENCHMARK=OFF 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) cmake --build build -j $(nproc)
- name: Run tests - name: Run tests

1
3rdParty/SDL3_image/CMakeLists.txt vendored

@ -25,6 +25,7 @@ else()
set(BUILD_SHARED_LIBS ON) set(BUILD_SHARED_LIBS ON)
endif() endif()
set(SDLIMAGE_BACKEND_STB OFF)
set(SDLIMAGE_AVIF OFF) set(SDLIMAGE_AVIF OFF)
set(SDLIMAGE_BMP OFF) set(SDLIMAGE_BMP OFF)
set(SDLIMAGE_GIF OFF) set(SDLIMAGE_GIF OFF)

13
Source/appfat.cpp

@ -6,12 +6,18 @@
#include <config.h> #include <config.h>
#ifdef USE_SDL3
#include <SDL3/SDL_thread.h>
#include <SDL3/SDL_timer.h>
#else
#include <SDL.h> #include <SDL.h>
#include <fmt/format.h>
#ifdef USE_SDL1 #ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h" #include "utils/sdl2_to_1_2_backports.h"
#endif #endif
#endif
#include <fmt/format.h>
#include "diablo.h" #include "diablo.h"
#include "multi.h" #include "multi.h"
@ -27,8 +33,13 @@ namespace {
/** Set to true when a fatal error is encountered and the application should shut down. */ /** Set to true when a fatal error is encountered and the application should shut down. */
bool Terminating = false; bool Terminating = false;
/** Thread id of the last callee to FreeDlg(). */ /** Thread id of the last callee to FreeDlg(). */
#ifdef USE_SDL3
SDL_ThreadID CleanupThreadId;
#else
SDL_threadID CleanupThreadId; SDL_threadID CleanupThreadId;
#endif
/** /**
* @brief Cleans up after a fatal application error. * @brief Cleans up after a fatal application error.

11
Source/diablo.cpp

@ -7,6 +7,17 @@
#include <cstdint> #include <cstdint>
#include <string_view> #include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_keycode.h>
#else
#include <SDL.h>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#endif
#endif
#include <fmt/format.h> #include <fmt/format.h>
#include <config.h> #include <config.h>

5
Source/diablo.h

@ -7,11 +7,16 @@
#include <cstdint> #include <cstdint>
#ifdef USE_SDL3
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_keycode.h>
#else
#include <SDL.h> #include <SDL.h>
#ifdef USE_SDL1 #ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h" #include "utils/sdl2_to_1_2_backports.h"
#endif #endif
#endif
#ifdef _DEBUG #ifdef _DEBUG
#include "monstdat.h" #include "monstdat.h"

1
Source/encrypt.cpp

@ -9,7 +9,6 @@
#include <cstring> #include <cstring>
#include <memory> #include <memory>
#include <SDL.h>
#include <pkware.h> #include <pkware.h>
#include "encrypt.h" #include "encrypt.h"

49
Source/engine/assets.cpp

@ -6,6 +6,13 @@
#include <functional> #include <functional>
#include <vector> #include <vector>
#ifdef USE_SDL3
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h>
#endif
#include "appfat.h" #include "appfat.h"
#include "game_mode.hpp" #include "game_mode.hpp"
#include "utils/file_util.h" #include "utils/file_util.h"
@ -45,16 +52,25 @@ char *FindUnpackedMpqFile(char *relativePath)
#else #else
bool IsDebugLogging() 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. // SDL always logs an error in Debug mode.
// We check the file presence in Debug mode to avoid this. // We check the file presence in Debug mode to avoid this.
if (IsDebugLogging() && !FileExists(path.c_str())) if (IsDebugLogging() && !FileExists(path.c_str()))
return nullptr; return nullptr;
#ifdef USE_SDL3
return SDL_IOFromFile(path.c_str(), "rb");
#else
return SDL_RWFromFile(path.c_str(), "rb"); return SDL_RWFromFile(path.c_str(), "rb");
#endif
}; };
bool FindMpqFile(std::string_view filename, MpqArchive **archive, uint32_t *fileNumber) bool FindMpqFile(std::string_view filename, MpqArchive **archive, uint32_t *fileNumber)
@ -128,7 +144,12 @@ AssetRef FindAsset(std::string_view filename)
#endif #endif
if (relativePath[0] == '/') { 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) { if (result.directHandle != nullptr) {
return result; return result;
} }
@ -161,7 +182,12 @@ AssetRef FindAsset(std::string_view filename)
// Fall back to the bundled assets on supported systems. // Fall back to the bundled assets on supported systems.
// This is handled by SDL when we pass a relative path. // This is handled by SDL when we pass a relative path.
if (!paths::AssetsPath().empty()) { 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) if (result.directHandle != nullptr)
return result; return result;
} }
@ -180,7 +206,7 @@ AssetHandle OpenAsset(AssetRef &&ref, bool threadsafe)
return AssetHandle { SDL_RWops_FromMpqFile(*ref.archive, ref.fileNumber, ref.filename, threadsafe) }; return AssetHandle { SDL_RWops_FromMpqFile(*ref.archive, ref.fileNumber, ref.filename, threadsafe) };
if (ref.directHandle != nullptr) { if (ref.directHandle != nullptr) {
// Transfer handle ownership: // Transfer handle ownership:
SDL_RWops *handle = ref.directHandle; auto *handle = ref.directHandle;
ref.directHandle = nullptr; ref.directHandle = nullptr;
return AssetHandle { handle }; return AssetHandle { handle };
} }
@ -205,13 +231,22 @@ AssetHandle OpenAsset(std::string_view filename, size_t &fileSize, bool threadsa
return OpenAsset(std::move(ref), threadsafe); 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 #ifdef UNPACKED_MPQS
AssetRef ref = FindAsset(filename); AssetRef ref = FindAsset(filename);
if (!ref.ok()) if (!ref.ok())
return nullptr; return nullptr;
#ifdef USE_SDL3
return SDL_IOFromFile(ref.path, "rb");
#else
return SDL_RWFromFile(ref.path, "rb"); return SDL_RWFromFile(ref.path, "rb");
#endif
#else #else
return OpenAsset(filename, threadsafe).release(); return OpenAsset(filename, threadsafe).release();
#endif #endif
@ -361,7 +396,7 @@ std::vector<std::string> GetMPQSearchPaths()
paths.emplace_back(); // PWD 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: {}", LogVerbose("Paths:\n base: {}\n pref: {}\n config: {}\n assets: {}",
paths::BasePath(), paths::PrefPath(), paths::ConfigPath(), paths::AssetsPath()); paths::BasePath(), paths::PrefPath(), paths::ConfigPath(), paths::AssetsPath());

88
Source/engine/assets.hpp

@ -9,7 +9,13 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include <expected.hpp> #include <expected.hpp>
#include <fmt/format.h> #include <fmt/format.h>
@ -114,8 +120,13 @@ struct AssetRef {
uint32_t fileNumber; uint32_t fileNumber;
std::string_view filename; std::string_view filename;
#ifdef USE_SDL3
// Alternatively, a direct SDL_IOStream handle:
SDL_IOStream *directHandle = nullptr;
#else
// Alternatively, a direct SDL_RWops handle: // Alternatively, a direct SDL_RWops handle:
SDL_RWops *directHandle = nullptr; SDL_RWops *directHandle = nullptr;
#endif
AssetRef() = default; AssetRef() = default;
@ -130,8 +141,7 @@ struct AssetRef {
AssetRef &operator=(AssetRef &&other) noexcept AssetRef &operator=(AssetRef &&other) noexcept
{ {
if (directHandle != nullptr) closeDirectHandle();
SDL_RWclose(directHandle);
archive = other.archive; archive = other.archive;
fileNumber = other.fileNumber; fileNumber = other.fileNumber;
filename = other.filename; filename = other.filename;
@ -142,8 +152,7 @@ struct AssetRef {
~AssetRef() ~AssetRef()
{ {
if (directHandle != nullptr) closeDirectHandle();
SDL_RWclose(directHandle);
} }
[[nodiscard]] bool ok() const [[nodiscard]] bool ok() const
@ -163,16 +172,42 @@ struct AssetRef {
int32_t error; int32_t error;
return archive->GetUnpackedFileSize(fileNumber, error); return archive->GetUnpackedFileSize(fileNumber, error);
} }
#ifdef USE_SDL3
return static_cast<size_t>(SDL_GetIOSize(directHandle));
#else
return static_cast<size_t>(SDL_RWsize(directHandle)); return static_cast<size_t>(SDL_RWsize(directHandle));
#endif
}
private:
void closeDirectHandle()
{
if (directHandle != nullptr) {
#ifdef USE_SDL3
SDL_CloseIO(directHandle);
#else
SDL_RWclose(directHandle);
#endif
}
} }
}; };
struct AssetHandle { struct AssetHandle {
#ifdef USE_SDL3
SDL_IOStream *handle = nullptr;
#else
SDL_RWops *handle = nullptr; SDL_RWops *handle = nullptr;
#endif
AssetHandle() = default; AssetHandle() = default;
explicit AssetHandle(SDL_RWops *handle) explicit AssetHandle(
#ifdef USE_SDL3
SDL_IOStream *
#else
SDL_RWops *
#endif
handle)
: handle(handle) : handle(handle)
{ {
} }
@ -185,9 +220,7 @@ struct AssetHandle {
AssetHandle &operator=(AssetHandle &&other) noexcept AssetHandle &operator=(AssetHandle &&other) noexcept
{ {
if (handle != nullptr) { closeHandle();
SDL_RWclose(handle);
}
handle = other.handle; handle = other.handle;
other.handle = nullptr; other.handle = nullptr;
return *this; return *this;
@ -195,8 +228,7 @@ struct AssetHandle {
~AssetHandle() ~AssetHandle()
{ {
if (handle != nullptr) closeHandle();
SDL_RWclose(handle);
} }
[[nodiscard]] bool ok() const [[nodiscard]] bool ok() const
@ -206,7 +238,9 @@ struct AssetHandle {
bool read(void *buffer, size_t len) 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; return handle->read(handle, buffer, len, 1) == 1;
#else #else
return handle->read(handle, buffer, static_cast<int>(len), 1) == 1; return handle->read(handle, buffer, static_cast<int>(len), 1) == 1;
@ -215,7 +249,11 @@ struct AssetHandle {
bool seek(long pos) 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; return handle->seek(handle, pos, RW_SEEK_SET) != -1;
#endif
} }
[[nodiscard]] const char *error() const [[nodiscard]] const char *error() const
@ -223,12 +261,33 @@ struct AssetHandle {
return SDL_GetError(); return SDL_GetError();
} }
#ifdef USE_SDL3
SDL_IOStream *release() &&
{
SDL_IOStream *result = handle;
handle = nullptr;
return result;
}
#else
SDL_RWops *release() && SDL_RWops *release() &&
{ {
SDL_RWops *result = handle; SDL_RWops *result = handle;
handle = nullptr; handle = nullptr;
return result; return result;
} }
#endif
private:
void closeHandle()
{
if (handle != nullptr) {
#ifdef USE_SDL3
SDL_CloseIO(handle);
#else
SDL_RWclose(handle);
#endif
}
}
}; };
#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, bool threadsafe = false);
AssetHandle OpenAsset(std::string_view filename, size_t &fileSize, 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 { struct AssetData {
std::unique_ptr<char[]> data; std::unique_ptr<char[]> data;

4
Source/engine/load_pcx.cpp

@ -10,7 +10,11 @@
#include <iostream> #include <iostream>
#endif #endif
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "mpq/mpq_common.hpp" #include "mpq/mpq_common.hpp"
#include "utils/log.hpp" #include "utils/log.hpp"

4
Source/engine/load_pcx.hpp

@ -3,7 +3,11 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "engine/clx_sprite.hpp" #include "engine/clx_sprite.hpp"

6
Source/engine/palette.cpp

@ -11,6 +11,12 @@
#include <cstring> #include <cstring>
#include <span> #include <span>
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h>
#endif
#include "engine/backbuffer_state.hpp" #include "engine/backbuffer_state.hpp"
#include "engine/demomode.h" #include "engine/demomode.h"
#include "engine/dx.h" #include "engine/dx.h"

4
Source/engine/palette.h

@ -9,7 +9,11 @@
#include <cstdint> #include <cstdint>
#include <span> #include <span>
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "levels/gendung_defs.hpp" #include "levels/gendung_defs.hpp"

4
Source/engine/sound_defs.hpp

@ -1,6 +1,10 @@
#pragma once #pragma once
#ifdef USE_SDL3
#include <SDL3/SDL_version.h>
#else
#include <SDL_version.h> #include <SDL_version.h>
#endif
#define VOLUME_MIN -1600 #define VOLUME_MIN -1600
#define VOLUME_MAX 0 #define VOLUME_MAX 0

5
Source/engine/surface.hpp

@ -3,6 +3,10 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#ifdef USE_SDL3
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_surface.h>
#else
#include <SDL_version.h> #include <SDL_version.h>
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
@ -12,6 +16,7 @@
#include "utils/sdl2_to_1_2_backports.h" #include "utils/sdl2_to_1_2_backports.h"
#include <SDL_video.h> #include <SDL_video.h>
#endif #endif
#endif
#include "engine/point.hpp" #include "engine/point.hpp"
#include "utils/sdl_geometry.h" #include "utils/sdl_geometry.h"

4
Source/engine/ticks.cpp

@ -2,7 +2,11 @@
#include <cstdint> #include <cstdint>
#ifdef USE_SDL3
#include <SDL3/SDL_timer.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
namespace devilution { namespace devilution {

6
Source/interfac.cpp

@ -8,7 +8,13 @@
#include <optional> #include <optional>
#include <string> #include <string>
#ifdef USE_SDL3
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_version.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include <expected.hpp> #include <expected.hpp>
#include "control.h" #include "control.h"

5
Source/interfac.h

@ -7,7 +7,12 @@
#include <cstdint> #include <cstdint>
#ifdef USE_SDL3
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_version.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "utils/ui_fwd.h" #include "utils/ui_fwd.h"

88
Source/mpq/mpq_sdl_rwops.cpp

@ -6,6 +6,12 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h>
#endif
namespace devilution { namespace devilution {
namespace { namespace {
@ -26,6 +32,9 @@ struct Data {
std::unique_ptr<uint8_t[]> blockData; std::unique_ptr<uint8_t[]> blockData;
}; };
#ifdef USE_SDL3
Data *GetData(void *userdata) { return reinterpret_cast<Data *>(userdata); }
#else
Data *GetData(struct SDL_RWops *context) Data *GetData(struct SDL_RWops *context)
{ {
return reinterpret_cast<Data *>(context->hidden.unknown.data1); return reinterpret_cast<Data *>(context->hidden.unknown.data1);
@ -35,6 +44,7 @@ void SetData(struct SDL_RWops *context, Data *data)
{ {
context->hidden.unknown.data1 = data; context->hidden.unknown.data1 = data;
} }
#endif
#ifndef USE_SDL1 #ifndef USE_SDL1
using OffsetType = Sint64; using OffsetType = Sint64;
@ -47,24 +57,46 @@ using SizeType = int;
extern "C" { extern "C" {
#ifndef USE_SDL1 #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<Sint64>(GetData(context)->size);
} }
#endif #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) static OffsetType MpqFileRwSeek(struct SDL_RWops *context, OffsetType offset, int whence)
#endif
{ {
Data &data = *GetData(context); Data &data = *GetData(context);
OffsetType newPosition; OffsetType newPosition;
switch (whence) { switch (whence) {
#ifdef USE_SDL3
case SDL_IO_SEEK_SET:
#else
case RW_SEEK_SET: case RW_SEEK_SET:
#endif
newPosition = offset; newPosition = offset;
break; break;
#ifdef USE_SDL3
case SDL_IO_SEEK_CUR:
#else
case RW_SEEK_CUR: case RW_SEEK_CUR:
#endif
newPosition = static_cast<OffsetType>(data.position + offset); newPosition = static_cast<OffsetType>(data.position + offset);
break; break;
#ifdef USE_SDL3
case SDL_IO_SEEK_END:
#else
case RW_SEEK_END: case RW_SEEK_END:
#endif
newPosition = static_cast<OffsetType>(data.size + offset); newPosition = static_cast<OffsetType>(data.size + offset);
break; break;
default: default:
@ -92,8 +124,15 @@ static OffsetType MpqFileRwSeek(struct SDL_RWops *context, OffsetType offset, in
return newPosition; 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) 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); Data &data = *GetData(context);
const size_t totalSize = size * maxnum; const size_t totalSize = size * maxnum;
size_t remainingSize = totalSize; size_t remainingSize = totalSize;
@ -104,9 +143,12 @@ static SizeType MpqFileRwRead(struct SDL_RWops *context, void *ptr, SizeType siz
data.blockData = std::unique_ptr<uint8_t[]> { new uint8_t[data.blockSize] }; data.blockData = std::unique_ptr<uint8_t[]> { new uint8_t[data.blockSize] };
} }
uint32_t blockNumber = static_cast<uint32_t>(data.position / data.blockSize); auto blockNumber = static_cast<uint32_t>(data.position / data.blockSize);
while (remainingSize > 0) { while (remainingSize > 0) {
if (data.position == data.size) { if (data.position == data.size) {
#ifdef USE_SDL3
*status = SDL_IO_STATUS_EOF;
#endif
break; break;
} }
@ -121,13 +163,17 @@ static SizeType MpqFileRwRead(struct SDL_RWops *context, void *ptr, SizeType siz
data.blockRead = true; 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; const size_t remainingBlockSize = currentBlockSize - blockPosition;
if (remainingSize < remainingBlockSize) { if (remainingSize < remainingBlockSize) {
std::memcpy(out, data.blockData.get() + blockPosition, remainingSize); std::memcpy(out, data.blockData.get() + blockPosition, remainingSize);
data.position += remainingSize; data.position += remainingSize;
#ifdef USE_SDL3
return size;
#else
return maxnum; return maxnum;
#endif
} }
std::memcpy(out, data.blockData.get() + blockPosition, remainingBlockSize); 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; data.blockRead = false;
} }
#ifdef USE_SDL3
return static_cast<SizeType>(totalSize - remainingSize);
#else
return static_cast<SizeType>((totalSize - remainingSize) / size); return static_cast<SizeType>((totalSize - remainingSize) / size);
#endif
} }
#ifdef USE_SDL3
static bool MpqFileRwClose(void *context)
#else
static int MpqFileRwClose(struct SDL_RWops *context) static int MpqFileRwClose(struct SDL_RWops *context)
#endif
{ {
Data *data = GetData(context); Data *data = GetData(context);
data->mpqArchive->CloseBlockOffsetTable(data->fileNumber); data->mpqArchive->CloseBlockOffsetTable(data->fileNumber);
delete data; delete data;
#ifdef USE_SDL3
return true;
#else
delete context; delete context;
return 0; return 0;
#endif
} }
} // extern "C" } // extern "C"
} // namespace } // 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<SDL_RWops>(); auto result = std::make_unique<SDL_RWops>();
std::memset(result.get(), 0, sizeof(*result)); std::memset(result.get(), 0, sizeof(*result));
#endif
#ifndef USE_SDL1 #ifndef USE_SDL1
result->size = &MpqFileRwSize; result->size = &MpqFileRwSize;
#ifndef USE_SDL3
result->type = SDL_RWOPS_UNKNOWN; result->type = SDL_RWOPS_UNKNOWN;
#endif
#else #else
result->type = 0; result->type = 0;
#endif #endif
@ -170,6 +241,9 @@ SDL_RWops *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, st
result->read = &MpqFileRwRead; result->read = &MpqFileRwRead;
result->write = nullptr; result->write = nullptr;
result->close = &MpqFileRwClose; result->close = &MpqFileRwClose;
#ifdef USE_SDL3
result->flush = nullptr;
#endif
auto data = std::make_unique<Data>(); auto data = std::make_unique<Data>();
int32_t error = 0; int32_t error = 0;
@ -226,8 +300,12 @@ SDL_RWops *SDL_RWops_FromMpqFile(MpqArchive &mpqArchive, uint32_t fileNumber, st
data->position = 0; data->position = 0;
data->blockRead = false; data->blockRead = false;
#ifdef USE_SDL3
return SDL_OpenIO(&interface, data.release());
#else
SetData(result.get(), data.release()); SetData(result.get(), data.release());
return result.release(); return result.release();
#endif
} }
} // namespace devilution } // namespace devilution

11
Source/mpq/mpq_sdl_rwops.hpp

@ -3,12 +3,21 @@
#include <cstdint> #include <cstdint>
#include <string_view> #include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "mpq/mpq_reader.hpp" #include "mpq/mpq_reader.hpp"
namespace devilution { 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 } // namespace devilution

24
Source/mpq/mpq_writer.cpp

@ -6,11 +6,11 @@
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include <SDL_endian.h>
#include <libmpq/mpq.h> #include <libmpq/mpq.h>
#include "appfat.h" #include "appfat.h"
#include "encrypt.h" #include "encrypt.h"
#include "utils/endian_swap.hpp"
#include "utils/file_util.h" #include "utils/file_util.h"
#include "utils/language.h" #include "utils/language.h"
#include "utils/log.hpp" #include "utils/log.hpp"
@ -63,15 +63,15 @@ constexpr uint32_t MinBlockSize = 1024;
void ByteSwapHdr(MpqFileHeader *hdr) void ByteSwapHdr(MpqFileHeader *hdr)
{ {
hdr->signature = SDL_SwapLE32(hdr->signature); hdr->signature = Swap32LE(hdr->signature);
hdr->headerSize = SDL_SwapLE32(hdr->headerSize); hdr->headerSize = Swap32LE(hdr->headerSize);
hdr->fileSize = SDL_SwapLE32(hdr->fileSize); hdr->fileSize = Swap32LE(hdr->fileSize);
hdr->version = SDL_SwapLE16(hdr->version); hdr->version = Swap16LE(hdr->version);
hdr->blockSizeFactor = SDL_SwapLE16(hdr->blockSizeFactor); hdr->blockSizeFactor = Swap16LE(hdr->blockSizeFactor);
hdr->hashEntriesOffset = SDL_SwapLE32(hdr->hashEntriesOffset); hdr->hashEntriesOffset = Swap32LE(hdr->hashEntriesOffset);
hdr->blockEntriesOffset = SDL_SwapLE32(hdr->blockEntriesOffset); hdr->blockEntriesOffset = Swap32LE(hdr->blockEntriesOffset);
hdr->hashEntriesCount = SDL_SwapLE32(hdr->hashEntriesCount); hdr->hashEntriesCount = Swap32LE(hdr->hashEntriesCount);
hdr->blockEntriesCount = SDL_SwapLE32(hdr->blockEntriesCount); hdr->blockEntriesCount = Swap32LE(hdr->blockEntriesCount);
} }
bool IsAllocatedUnusedBlock(const MpqBlockEntry *block) bool IsAllocatedUnusedBlock(const MpqBlockEntry *block)
@ -421,7 +421,7 @@ bool MpqWriter::WriteFileContents(const std::byte *fileData, uint32_t fileSize,
len = PkwareCompress(mpqBuf, len); len = PkwareCompress(mpqBuf, len);
if (!stream_.Write(reinterpret_cast<const char *>(&mpqBuf[0]), len)) if (!stream_.Write(reinterpret_cast<const char *>(&mpqBuf[0]), len))
return false; return false;
offsetTable[curSector++] = SDL_SwapLE32(destSize); offsetTable[curSector++] = Swap32LE(destSize);
destSize += len; // compressed length destSize += len; // compressed length
if (fileSize <= BlockSize) if (fileSize <= BlockSize)
break; break;
@ -429,7 +429,7 @@ bool MpqWriter::WriteFileContents(const std::byte *fileData, uint32_t fileSize,
fileSize -= BlockSize; fileSize -= BlockSize;
} }
offsetTable[numSectors] = SDL_SwapLE32(destSize); offsetTable[numSectors] = Swap32LE(destSize);
if (!stream_.Seekp(block->offset, SEEK_SET)) if (!stream_.Seekp(block->offset, SEEK_SET))
return false; return false;
if (!stream_.Write(reinterpret_cast<const char *>(offsetTable.get()), offsetTableByteSize)) if (!stream_.Write(reinterpret_cast<const char *>(offsetTable.get()), offsetTableByteSize))

64
Source/options.cpp

@ -17,7 +17,15 @@
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
#ifdef USE_SDL3
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_keycode.h>
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_version.h>
#else
#include <SDL_version.h> #include <SDL_version.h>
#endif
#include <expected.hpp> #include <expected.hpp>
#include <fmt/format.h> #include <fmt/format.h>
#include <function_ref.hpp> #include <function_ref.hpp>
@ -36,6 +44,7 @@
#include "utils/log.hpp" #include "utils/log.hpp"
#include "utils/logged_fstream.hpp" #include "utils/logged_fstream.hpp"
#include "utils/paths.h" #include "utils/paths.h"
#include "utils/sdl_ptrs.h"
#include "utils/str_cat.hpp" #include "utils/str_cat.hpp"
#include "utils/str_split.hpp" #include "utils/str_split.hpp"
#include "utils/utf8.hpp" #include "utils/utf8.hpp"
@ -198,6 +207,8 @@ bool HardwareCursorSupported()
{ {
#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) || __DJGPP__ #if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) || __DJGPP__
return false; return false;
#elif USE_SDL3
return true;
#else #else
SDL_version v; SDL_version v;
SDL_GetVersion(&v); SDL_GetVersion(&v);
@ -645,7 +656,11 @@ void OptionEntryAudioDevice::SaveToIni(std::string_view category) const
size_t OptionEntryAudioDevice::GetListSize() const size_t OptionEntryAudioDevice::GetListSize() const
{ {
#if SDL_VERSION_ATLEAST(2, 0, 0) #if defined(USE_SDL3)
int numDevices = 0;
SDLUniquePtr<SDL_AudioDeviceID> devices { SDL_GetAudioPlaybackDevices(&numDevices) };
return static_cast<size_t>(numDevices) + 1;
#elif SDL_VERSION_ATLEAST(2, 0, 0)
return SDL_GetNumAudioDevices(false) + 1; return SDL_GetNumAudioDevices(false) + 1;
#else #else
return 1; return 1;
@ -661,12 +676,22 @@ std::string_view OptionEntryAudioDevice::GetListDescription(size_t index) const
size_t OptionEntryAudioDevice::GetActiveListIndex() const size_t OptionEntryAudioDevice::GetActiveListIndex() const
{ {
#ifdef USE_SDL3
int numDevices;
SDLUniquePtr<SDL_AudioDeviceID> 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++) { for (size_t i = 0; i < GetListSize(); i++) {
const std::string_view deviceName = GetDeviceName(i); const std::string_view deviceName = GetDeviceName(i);
if (deviceName == deviceName_) if (deviceName_ == deviceName) return i;
return i;
} }
return 0; return 0;
#endif
} }
void OptionEntryAudioDevice::SetActiveListIndex(size_t index) 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 std::string_view OptionEntryAudioDevice::GetDeviceName(size_t index) const
{ {
#if SDL_VERSION_ATLEAST(2, 0, 0) if (index == 0) return {}; // System Default
if (index != 0) #if defined(USE_SDL3)
return SDL_GetAudioDeviceName(static_cast<int>(index) - 1, false); int numDevices = 0;
SDLUniquePtr<SDL_AudioDeviceID> devices { SDL_GetAudioPlaybackDevices(&numDevices) };
if (devices == nullptr || static_cast<int>(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<int>(index) - 1, false);
#endif #endif
return ""; return {};
} }
GraphicsOptions::GraphicsOptions() GraphicsOptions::GraphicsOptions()
@ -1078,12 +1110,26 @@ KeymapperOptions::KeymapperOptions()
keyIDToKeyName.emplace(MouseScrollLeftButton, "SCROLLLEFTMOUSE"); keyIDToKeyName.emplace(MouseScrollLeftButton, "SCROLLLEFTMOUSE");
keyIDToKeyName.emplace(MouseScrollRightButton, "SCROLLRIGHTMOUSE"); 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_LEFTBRACKET, "[");
keyIDToKeyName.emplace(SDLK_RIGHTBRACKET, "]"); keyIDToKeyName.emplace(SDLK_RIGHTBRACKET, "]");
keyIDToKeyName.emplace(SDLK_BACKSLASH, "\\"); keyIDToKeyName.emplace(SDLK_BACKSLASH, "\\");
keyIDToKeyName.emplace(SDLK_SEMICOLON, ";"); 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_COMMA, ",");
keyIDToKeyName.emplace(SDLK_PERIOD, "."); keyIDToKeyName.emplace(SDLK_PERIOD, ".");
keyIDToKeyName.emplace(SDLK_SLASH, "/"); keyIDToKeyName.emplace(SDLK_SLASH, "/");

5
Source/options.h

@ -12,7 +12,12 @@
#include <string_view> #include <string_view>
#include <utility> #include <utility>
#ifdef USE_SDL3
#include <SDL3/SDL_version.h>
#else
#include <SDL_version.h> #include <SDL_version.h>
#endif
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include <function_ref.hpp> #include <function_ref.hpp>

12
Source/utils/display.cpp

@ -52,21 +52,21 @@ SDL_Window *ghMainWnd;
Size forceResolution; Size forceResolution;
Uint16 gnScreenWidth; uint16_t gnScreenWidth;
Uint16 gnScreenHeight; uint16_t gnScreenHeight;
Uint16 gnViewportHeight; uint16_t gnViewportHeight;
Uint16 GetScreenWidth() uint16_t GetScreenWidth()
{ {
return gnScreenWidth; return gnScreenWidth;
} }
Uint16 GetScreenHeight() uint16_t GetScreenHeight()
{ {
return gnScreenHeight; return gnScreenHeight;
} }
Uint16 GetViewportHeight() uint16_t GetViewportHeight()
{ {
return gnViewportHeight; return gnViewportHeight;
} }

31
Source/utils/endian_swap.hpp

@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#ifdef USE_SDL3
#include <SDL3/SDL_endian.h>
#else
#include <SDL_endian.h>
#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

6
Source/utils/endian_write.hpp

@ -3,19 +3,19 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <SDL_endian.h> #include "utils/endian_swap.hpp"
namespace devilution { namespace devilution {
inline void WriteLE16(void *out, uint16_t val) 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); memcpy(out, &littleEndian, 2);
} }
inline void WriteLE32(void *out, uint32_t val) 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); memcpy(out, &littleEndian, 4);
} }

26
Source/utils/file_util.cpp

@ -6,7 +6,11 @@
#include <cstring> #include <cstring>
#include <limits> #include <limits>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "utils/log.hpp" #include "utils/log.hpp"
#include "utils/stdcompat/filesystem.hpp" #include "utils/stdcompat/filesystem.hpp"
@ -112,10 +116,14 @@ bool FileExists(const char *path)
#elif defined(DVL_HAS_FILESYSTEM) #elif defined(DVL_HAS_FILESYSTEM)
std::error_code ec; std::error_code ec;
return std::filesystem::exists(reinterpret_cast<const char8_t *>(path), ec); return std::filesystem::exists(reinterpret_cast<const char8_t *>(path), ec);
#elif USE_SDL3
SDL_IOStream *file = SDL_IOFromFile(path, "rb");
if (file == nullptr) return false;
SDL_CloseIO(file);
return true;
#else #else
SDL_RWops *file = SDL_RWFromFile(path, "r+b"); SDL_RWops *file = SDL_RWFromFile(path, "rb");
if (file == nullptr) if (file == nullptr) return false;
return false;
SDL_RWclose(file); SDL_RWclose(file);
return true; return true;
#endif #endif
@ -176,12 +184,18 @@ bool FileExistsAndIsWriteable(const char *path)
#else #else
if (!FileExists(path)) if (!FileExists(path))
return false; return false;
SDL_RWops *file = SDL_RWFromFile(path, "a+b"); #ifdef USE_SDL3
if (file == nullptr) SDL_IOStream *file = SDL_IOFromFile(path, "ab");
return false; 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); SDL_RWclose(file);
return true; return true;
#endif #endif
#endif
} }
bool GetFileSize(const char *path, std::uintmax_t *size) bool GetFileSize(const char *path, std::uintmax_t *size)

18
Source/utils/log.hpp

@ -2,7 +2,12 @@
#include <string_view> #include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_log.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include <fmt/core.h> #include <fmt/core.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <fmt/ranges.h> #include <fmt/ranges.h>
@ -85,10 +90,19 @@ inline void LogVerbose(LogCategory category, std::string_view str)
SDL_LogVerbose(static_cast<int>(category), "%.*s", static_cast<int>(str.size()), str.data()); SDL_LogVerbose(static_cast<int>(category), "%.*s", static_cast<int>(str.size()), str.data());
} }
inline bool IsLogLevel(LogCategory category, SDL_LogPriority priority)
{
#ifdef USE_SDL3
return SDL_GetLogPriority(static_cast<int>(category)) <= priority;
#else
return SDL_LogGetPriority(static_cast<int>(category)) <= priority;
#endif
}
template <typename... Args> template <typename... Args>
void LogVerbose(LogCategory category, std::string_view fmt, Args &&...args) void LogVerbose(LogCategory category, std::string_view fmt, Args &&...args)
{ {
if (SDL_LogGetPriority(static_cast<int>(category)) > SDL_LOG_PRIORITY_VERBOSE) return; if (!IsLogLevel(category, SDL_LOG_PRIORITY_VERBOSE)) return;
auto str = detail::format(fmt, std::forward<Args>(args)...); auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogVerbose(static_cast<int>(category), "%s", str.c_str()); SDL_LogVerbose(static_cast<int>(category), "%s", str.c_str());
} }
@ -107,7 +121,7 @@ inline void LogDebug(LogCategory category, std::string_view str)
template <typename... Args> template <typename... Args>
void LogDebug(LogCategory category, std::string_view fmt, Args &&...args) void LogDebug(LogCategory category, std::string_view fmt, Args &&...args)
{ {
if (SDL_LogGetPriority(static_cast<int>(category)) > SDL_LOG_PRIORITY_DEBUG) return; if (!IsLogLevel(category, SDL_LOG_PRIORITY_DEBUG)) return;
auto str = detail::format(fmt, std::forward<Args>(args)...); auto str = detail::format(fmt, std::forward<Args>(args)...);
SDL_LogDebug(static_cast<int>(category), "%s", str.c_str()); SDL_LogDebug(static_cast<int>(category), "%s", str.c_str());
} }

5
Source/utils/palette_blending.cpp

@ -2,9 +2,12 @@
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <limits>
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "utils/palette_kd_tree.hpp" #include "utils/palette_kd_tree.hpp"

4
Source/utils/palette_blending.hpp

@ -2,7 +2,11 @@
#include <cstdint> #include <cstdint>
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
namespace devilution { namespace devilution {

4
Source/utils/palette_kd_tree.cpp

@ -7,7 +7,9 @@
#include <string> #include <string>
#include <utility> #include <utility>
#ifdef USE_SDL1 #ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#elif defined(USE_SDL1)
#include <SDL_video.h> #include <SDL_video.h>
#else #else
#include <SDL_pixels.h> #include <SDL_pixels.h>

4
Source/utils/palette_kd_tree.hpp

@ -8,7 +8,9 @@
#include <string> #include <string>
#include <utility> #include <utility>
#ifdef USE_SDL1 #ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#elif defined(USE_SDL1)
#include <SDL_video.h> #include <SDL_video.h>
#else #else
#include <SDL_pixels.h> #include <SDL_pixels.h>

34
Source/utils/paths.cpp

@ -4,7 +4,12 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_filesystem.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "appfat.h" #include "appfat.h"
#include "utils/file_util.h" #include "utils/file_util.h"
@ -67,16 +72,31 @@ const std::string &NxdkGetPrefPath()
} }
#endif #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 } // namespace
const std::string &BasePath() const std::string &BasePath()
{ {
if (!basePath) { if (!basePath) {
#if defined(__DJGPP__) basePath = GetSdlBasePath();
basePath = std::string();
#else
basePath = FromSDL(SDL_GetBasePath());
#endif
} }
return *basePath; return *basePath;
} }
@ -148,9 +168,9 @@ const std::string &AssetsPath()
// //
// Note that SDL3 reverts to SDL1 behaviour! // Note that SDL3 reverts to SDL1 behaviour!
// https://github.com/libsdl-org/SDL/blob/962268ca21ed10b9cee31198c22681099293f20a/docs/README-migration.md?plain=1#L1623 // https://github.com/libsdl-org/SDL/blob/962268ca21ed10b9cee31198c22681099293f20a/docs/README-migration.md?plain=1#L1623
assetsPath.emplace(FromSDL(SDL_GetBasePath())); assetsPath.emplace(GetSdlBasePath());
#else #else
assetsPath.emplace(FromSDL(SDL_GetBasePath()) + ("assets" DIRECTORY_SEPARATOR_STR)); assetsPath.emplace(GetSdlBasePath() + ("assets" DIRECTORY_SEPARATOR_STR));
#endif #endif
} }
return *assetsPath; return *assetsPath;

11
Source/utils/pcx_to_clx.cpp

@ -9,11 +9,16 @@
#include <optional> #include <optional>
#include <vector> #include <vector>
#include <SDL_endian.h> #ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h>
#endif
#include "appfat.h" #include "appfat.h"
#include "utils/clx_encode.hpp" #include "utils/clx_encode.hpp"
#include "utils/endian_read.hpp" #include "utils/endian_read.hpp"
#include "utils/endian_swap.hpp"
#include "utils/endian_write.hpp" #include "utils/endian_write.hpp"
#include "utils/pcx.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)) { if (!handle.read(&pcxhdr, PcxHeaderSize)) {
return false; return false;
} }
width = SDL_SwapLE16(pcxhdr.Xmax) - SDL_SwapLE16(pcxhdr.Xmin) + 1; width = Swap16LE(pcxhdr.Xmax) - Swap16LE(pcxhdr.Xmin) + 1;
height = SDL_SwapLE16(pcxhdr.Ymax) - SDL_SwapLE16(pcxhdr.Ymin) + 1; height = Swap16LE(pcxhdr.Ymax) - Swap16LE(pcxhdr.Ymin) + 1;
bpp = pcxhdr.BitsPerPixel; bpp = pcxhdr.BitsPerPixel;
return true; return true;
} }

4
Source/utils/pcx_to_clx.hpp

@ -3,7 +3,11 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include "engine/assets.hpp" #include "engine/assets.hpp"
#include "engine/clx_sprite.hpp" #include "engine/clx_sprite.hpp"

4
Source/utils/sdl_geometry.h

@ -4,6 +4,9 @@
*/ */
#pragma once #pragma once
#ifdef USE_SDL3
#include <SDL3/SDL_rect.h>
#else
#include <SDL_version.h> #include <SDL_version.h>
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
@ -11,6 +14,7 @@
#else #else
#include <SDL_video.h> #include <SDL_video.h>
#endif #endif
#endif
#include "engine/rectangle.hpp" #include "engine/rectangle.hpp"

23
Source/utils/sdl_ptrs.h

@ -6,12 +6,21 @@
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#ifdef USE_SDL3
#include <SDL3/SDL_mouse.h>
#include <SDL3/SDL_pixels.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_surface.h>
#include <SDL3/SDL_version.h>
#else
#include <SDL.h> #include <SDL.h>
#ifdef USE_SDL1 #ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h" #include "utils/sdl2_to_1_2_backports.h"
#else #else
#include "utils/sdl2_backports.h" #include "utils/sdl2_backports.h"
#endif #endif
#endif
namespace devilution { namespace devilution {
@ -21,7 +30,11 @@ namespace devilution {
struct SDLSurfaceDeleter { struct SDLSurfaceDeleter {
void operator()(SDL_Surface *surface) const void operator()(SDL_Surface *surface) const
{ {
#ifdef USE_SDL3
SDL_DestroySurface(surface);
#else
SDL_FreeSurface(surface); SDL_FreeSurface(surface);
#endif
} }
}; };
@ -31,14 +44,16 @@ using SDLSurfaceUniquePtr = std::unique_ptr<SDL_Surface, SDLSurfaceDeleter>;
struct SDLCursorDeleter { struct SDLCursorDeleter {
void operator()(SDL_Cursor *cursor) const void operator()(SDL_Cursor *cursor) const
{ {
#ifdef USE_SDL3
SDL_DestroyCursor(cursor);
#else
SDL_FreeCursor(cursor); SDL_FreeCursor(cursor);
#endif
} }
}; };
using SDLCursorUniquePtr = std::unique_ptr<SDL_Cursor, SDLCursorDeleter>; using SDLCursorUniquePtr = std::unique_ptr<SDL_Cursor, SDLCursorDeleter>;
#endif
#ifndef USE_SDL1
struct SDLTextureDeleter { struct SDLTextureDeleter {
void operator()(SDL_Texture *texture) const void operator()(SDL_Texture *texture) const
{ {
@ -52,7 +67,11 @@ using SDLTextureUniquePtr = std::unique_ptr<SDL_Texture, SDLTextureDeleter>;
struct SDLPaletteDeleter { struct SDLPaletteDeleter {
void operator()(SDL_Palette *palette) const void operator()(SDL_Palette *palette) const
{ {
#ifdef USE_SDL3
SDL_DestroyPalette(palette);
#else
SDL_FreePalette(palette); SDL_FreePalette(palette);
#endif
} }
}; };

18
Source/utils/sdl_thread.h

@ -2,18 +2,27 @@
#include <memory> #include <memory>
#ifdef USE_SDL3
#include <SDL3/SDL_thread.h>
#else
#include <SDL.h> #include <SDL.h>
#ifdef USE_SDL1 #ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h" #include "utils/sdl2_to_1_2_backports.h"
#endif #endif
#endif
#include "appfat.h" #include "appfat.h"
#include "utils/attributes.h" #include "utils/attributes.h"
namespace devilution { namespace devilution {
namespace this_sdl_thread { namespace this_sdl_thread {
#ifdef USE_SDL3
inline SDL_ThreadID get_id()
#else
inline SDL_threadID get_id() inline SDL_threadID get_id()
#endif
{ {
#if defined(__DJGPP__) #if defined(__DJGPP__)
return 1; return 1;
@ -39,7 +48,12 @@ public:
{ {
return false; return false;
} }
#ifdef USE_SDL3
SDL_ThreadID get_id() const
#else
SDL_threadID get_id() const SDL_threadID get_id() const
#endif
{ {
return this_sdl_thread::get_id(); return this_sdl_thread::get_id();
} }
@ -79,7 +93,11 @@ public:
return thread != nullptr; return thread != nullptr;
} }
#ifdef USE_SDL3
SDL_ThreadID get_id() const
#else
SDL_threadID get_id() const SDL_threadID get_id() const
#endif
{ {
return SDL_GetThreadID(thread.get()); return SDL_GetThreadID(thread.get());
} }

190
Source/utils/sdl_wrap.h

@ -1,72 +1,118 @@
#pragma once #pragma once
#include <SDL.h> #ifdef USE_SDL3
#ifdef USE_SDL1 #include <SDL3/SDL_error.h>
#include "utils/sdl2_to_1_2_backports.h" #include <SDL3/SDL_pixels.h>
#else #include <SDL3/SDL_render.h>
#include "utils/sdl2_backports.h" #include <SDL3/SDL_stdinc.h>
#endif #include <SDL3/SDL_surface.h>
#else
#include "appfat.h" #include <SDL.h>
#include "utils/sdl_ptrs.h" #ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#define NonNull(x) NullErrDlg(x, __FILE__, __LINE__) #else
#include "utils/sdl2_backports.h"
namespace devilution { #endif
#endif
namespace SDLWrap {
#include "appfat.h"
template <typename T> #include "utils/sdl_ptrs.h"
T NullErrDlg(T x, const char *file, int line)
{ #define NonNull(x) NullErrDlg(x, __FILE__, __LINE__)
if (x == nullptr)
ErrDlg("SDL Error", SDL_GetError(), file, line); namespace devilution {
return x;
} namespace SDLWrap {
inline SDLSurfaceUniquePtr CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) template <typename T>
{ T NullErrDlg(T x, const char *file, int line)
return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask)) }; {
} if (x == nullptr)
ErrDlg("SDL Error", SDL_GetError(), file, line);
inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format) return x;
{ }
return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format)) };
} inline SDLSurfaceUniquePtr CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
{
inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 format) #ifdef USE_SDL3
{ return SDLSurfaceUniquePtr { NonNull(SDL_CreateSurface(width, height, SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask))) };
return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormatFrom(pixels, width, height, depth, pitch, format)) }; #else
} return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurface(flags, width, height, depth, Rmask, Gmask, Bmask, Amask)) };
#endif
#ifndef USE_SDL1 }
inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, const SDL_PixelFormat *fmt, Uint32 flags)
#else #ifdef USE_SDL3
inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags) inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, SDL_PixelFormat format)
#endif {
{ return SDLSurfaceUniquePtr { NonNull(SDL_CreateSurface(width, height, format)) };
return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurface(src, fmt, flags)) }; }
} #else
inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format)
#ifndef USE_SDL1 {
inline SDLSurfaceUniquePtr ConvertSurfaceFormat(SDL_Surface *src, Uint32 pixel_format, Uint32 flags) return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format)) };
{ }
return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurfaceFormat(src, pixel_format, flags)) }; #endif
}
#endif #ifdef USE_SDL3
inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, SDL_PixelFormat format)
#ifndef USE_SDL1 {
inline SDLTextureUniquePtr CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h) return SDLSurfaceUniquePtr { NonNull(SDL_CreateSurfaceFrom(width, height, format, pixels, pitch)) };
{ }
return SDLTextureUniquePtr { NonNull(SDL_CreateTexture(renderer, format, access, w, h)) }; #else
} inline SDLSurfaceUniquePtr CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 format)
#endif {
return SDLSurfaceUniquePtr { NonNull(SDL_CreateRGBSurfaceWithFormatFrom(pixels, width, height, depth, pitch, format)) };
inline SDLPaletteUniquePtr AllocPalette(int ncolors = 256) }
{ #endif
return SDLPaletteUniquePtr { NonNull(SDL_AllocPalette(ncolors)) };
} #ifndef USE_SDL1
inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, const SDL_PixelFormat *fmt, Uint32 flags)
} // namespace SDLWrap #else
inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, SDL_PixelFormat *fmt, Uint32 flags)
} // namespace devilution #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

24
Source/utils/surface_to_png.cpp

@ -1,21 +1,39 @@
#include "utils/surface_to_png.hpp" #include "utils/surface_to_png.hpp"
#include <cstdio>
#include <string> #include <string>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#include <SDL3_image/SDL_image.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include <expected.hpp> #include <expected.hpp>
#include "engine/surface.hpp" #include "engine/surface.hpp"
namespace devilution { namespace devilution {
#ifndef USE_SDL3
extern "C" int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst); extern "C" int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst);
#endif
tl::expected<void, std::string> tl::expected<void, std::string>
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<void, std::string> result = tl::make_unexpected(std::string(SDL_GetError())); tl::expected<void, std::string> result = tl::make_unexpected(std::string(SDL_GetError()));
SDL_ClearError(); SDL_ClearError();
return result; return result;

14
Source/utils/surface_to_png.hpp

@ -1,7 +1,13 @@
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_surface.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include <expected.hpp> #include <expected.hpp>
#include "engine/surface.hpp" #include "engine/surface.hpp"
@ -14,6 +20,12 @@ namespace devilution {
* Takes ownership of `dst` and closes it when done. * Takes ownership of `dst` and closes it when done.
*/ */
tl::expected<void, std::string> tl::expected<void, std::string>
WriteSurfaceToFilePng(const Surface &buf, SDL_RWops *dst); WriteSurfaceToFilePng(const Surface &buf,
#ifdef USE_SDL3
SDL_IOStream *
#else
SDL_RWops *
#endif
dst);
} // namespace devilution } // namespace devilution

14
Source/utils/ui_fwd.h

@ -1,19 +1,19 @@
#pragma once #pragma once
#include <SDL.h> #include <cstdint>
#include "engine/rectangle.hpp" #include "engine/rectangle.hpp"
#include "utils/attributes.h" #include "utils/attributes.h"
namespace devilution { namespace devilution {
extern DVL_API_FOR_TEST Uint16 gnScreenWidth; extern DVL_API_FOR_TEST uint16_t gnScreenWidth;
extern DVL_API_FOR_TEST Uint16 gnScreenHeight; extern DVL_API_FOR_TEST uint16_t gnScreenHeight;
extern DVL_API_FOR_TEST Uint16 gnViewportHeight; extern DVL_API_FOR_TEST uint16_t gnViewportHeight;
Uint16 GetScreenWidth(); uint16_t GetScreenWidth();
Uint16 GetScreenHeight(); uint16_t GetScreenHeight();
Uint16 GetViewportHeight(); uint16_t GetViewportHeight();
/** @brief Returns the UI (Menus, Messages, Help) can use. Currently this is 640x480 like vanilla. */ /** @brief Returns the UI (Menus, Messages, Help) can use. Currently this is 640x480 like vanilla. */
const Rectangle &GetUIRectangle(); const Rectangle &GetUIRectangle();

31
test/text_render_integration_test.cpp

@ -8,7 +8,13 @@
#include <string_view> #include <string_view>
#include <variant> #include <variant>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_surface.h>
#else
#include <SDL.h> #include <SDL.h>
#endif
#include <expected.hpp> #include <expected.hpp>
#include <function_ref.hpp> #include <function_ref.hpp>
@ -223,13 +229,28 @@ SDLPaletteUniquePtr LoadPalette()
std::vector<std::byte> ReadFile(const std::string &path) std::vector<std::byte> 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"); SDL_RWops *rwops = SDL_RWFromFile(path.c_str(), "rb");
#endif
std::vector<std::byte> result; std::vector<std::byte> result;
if (rwops == nullptr) return result; if (rwops == nullptr) return result;
const size_t size = static_cast<size_t>(SDL_RWsize(rwops)); const size_t size = static_cast<size_t>(
#ifdef USE_SDL3
SDL_GetIOSize(rwops)
#else
SDL_RWsize(rwops)
#endif
);
result.resize(size); result.resize(size);
#ifdef USE_SDL3
SDL_ReadIO(rwops, result.data(), size);
SDL_CloseIO(rwops);
#else
SDL_RWread(rwops, result.data(), size, 1); SDL_RWread(rwops, result.data(), size, 1);
SDL_RWclose(rwops); SDL_RWclose(rwops);
#endif
return result; 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 actualPath = StrCat(paths::BasePath(), FixturesPath, GetParam().name, "-Actual.png");
const std::string expectedPath = StrCat(paths::BasePath(), FixturesPath, GetParam().name, ".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"); SDL_RWops *actual = SDL_RWFromFile(actualPath.c_str(), "wb");
#endif
ASSERT_NE(actual, nullptr) << SDL_GetError(); ASSERT_NE(actual, nullptr) << SDL_GetError();
ASSERT_TRUE(WriteSurfaceToFilePng(out, actual).has_value());
const tl::expected<void, std::string> result = WriteSurfaceToFilePng(out, actual);
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(actualPath, FileContentsEq(expectedPath)); EXPECT_THAT(actualPath, FileContentsEq(expectedPath));
} }

Loading…
Cancel
Save