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 5 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. 62
      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. 52
      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
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

1
3rdParty/SDL3_image/CMakeLists.txt vendored

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

13
Source/appfat.cpp

@ -6,12 +6,18 @@
#include <config.h>
#ifdef USE_SDL3
#include <SDL3/SDL_thread.h>
#include <SDL3/SDL_timer.h>
#else
#include <SDL.h>
#include <fmt/format.h>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#endif
#endif
#include <fmt/format.h>
#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.

11
Source/diablo.cpp

@ -7,6 +7,17 @@
#include <cstdint>
#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 <config.h>

5
Source/diablo.h

@ -7,11 +7,16 @@
#include <cstdint>
#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
#ifdef _DEBUG
#include "monstdat.h"

1
Source/encrypt.cpp

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

49
Source/engine/assets.cpp

@ -6,6 +6,13 @@
#include <functional>
#include <vector>
#ifdef USE_SDL3
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h>
#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<std::string> 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());

88
Source/engine/assets.hpp

@ -9,7 +9,13 @@
#include <string>
#include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h>
#endif
#include <expected.hpp>
#include <fmt/format.h>
@ -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<size_t>(SDL_GetIOSize(directHandle));
#else
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 {
#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<int>(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<char[]> data;

4
Source/engine/load_pcx.cpp

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

4
Source/engine/load_pcx.hpp

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

6
Source/engine/palette.cpp

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

4
Source/engine/palette.h

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

4
Source/engine/sound_defs.hpp

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

5
Source/engine/surface.hpp

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

4
Source/engine/ticks.cpp

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

6
Source/interfac.cpp

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

5
Source/interfac.h

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

88
Source/mpq/mpq_sdl_rwops.cpp

@ -6,6 +6,12 @@
#include <string_view>
#include <vector>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h>
#endif
namespace devilution {
namespace {
@ -26,6 +32,9 @@ struct Data {
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)
{
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;
}
#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<Sint64>(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<OffsetType>(data.position + offset);
break;
#ifdef USE_SDL3
case SDL_IO_SEEK_END:
#else
case RW_SEEK_END:
#endif
newPosition = static_cast<OffsetType>(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<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) {
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<SizeType>(totalSize - remainingSize);
#else
return static_cast<SizeType>((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<SDL_RWops>();
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<Data>();
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

11
Source/mpq/mpq_sdl_rwops.hpp

@ -3,12 +3,21 @@
#include <cstdint>
#include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h>
#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

24
Source/mpq/mpq_writer.cpp

@ -6,11 +6,11 @@
#include <memory>
#include <type_traits>
#include <SDL_endian.h>
#include <libmpq/mpq.h>
#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<const char *>(&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<const char *>(offsetTable.get()), offsetTableByteSize))

62
Source/options.cpp

@ -17,7 +17,15 @@
#include <string>
#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>
#endif
#include <expected.hpp>
#include <fmt/format.h>
#include <function_ref.hpp>
@ -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<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;
#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<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++) {
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)
if (index == 0) return {}; // System Default
#if defined(USE_SDL3)
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
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, "/");

5
Source/options.h

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

12
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;
}

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 <cstring>
#include <SDL_endian.h>
#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);
}

26
Source/utils/file_util.cpp

@ -6,7 +6,11 @@
#include <cstring>
#include <limits>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#else
#include <SDL.h>
#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<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
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)

18
Source/utils/log.hpp

@ -2,7 +2,12 @@
#include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_log.h>
#else
#include <SDL.h>
#endif
#include <fmt/core.h>
#include <fmt/format.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());
}
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>
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)...);
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>
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)...);
SDL_LogDebug(static_cast<int>(category), "%s", str.c_str());
}

5
Source/utils/palette_blending.cpp

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

4
Source/utils/palette_blending.hpp

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

4
Source/utils/palette_kd_tree.cpp

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

4
Source/utils/palette_kd_tree.hpp

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

34
Source/utils/paths.cpp

@ -4,7 +4,12 @@
#include <string>
#include <string_view>
#ifdef USE_SDL3
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_filesystem.h>
#else
#include <SDL.h>
#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;

11
Source/utils/pcx_to_clx.cpp

@ -9,11 +9,16 @@
#include <optional>
#include <vector>
#include <SDL_endian.h>
#ifdef USE_SDL3
#include <SDL3/SDL_pixels.h>
#else
#include <SDL.h>
#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;
}

4
Source/utils/pcx_to_clx.hpp

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

4
Source/utils/sdl_geometry.h

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

23
Source/utils/sdl_ptrs.h

@ -6,12 +6,21 @@
#include <memory>
#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>
#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<SDL_Surface, SDLSurfaceDeleter>;
struct SDLCursorDeleter {
void operator()(SDL_Cursor *cursor) const
{
#ifdef USE_SDL3
SDL_DestroyCursor(cursor);
#else
SDL_FreeCursor(cursor);
#endif
}
};
using SDLCursorUniquePtr = std::unique_ptr<SDL_Cursor, SDLCursorDeleter>;
#endif
#ifndef USE_SDL1
struct SDLTextureDeleter {
void operator()(SDL_Texture *texture) const
{
@ -52,7 +67,11 @@ using SDLTextureUniquePtr = std::unique_ptr<SDL_Texture, SDLTextureDeleter>;
struct SDLPaletteDeleter {
void operator()(SDL_Palette *palette) const
{
#ifdef USE_SDL3
SDL_DestroyPalette(palette);
#else
SDL_FreePalette(palette);
#endif
}
};

18
Source/utils/sdl_thread.h

@ -2,18 +2,27 @@
#include <memory>
#ifdef USE_SDL3
#include <SDL3/SDL_thread.h>
#else
#include <SDL.h>
#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());
}

52
Source/utils/sdl_wrap.h

@ -1,11 +1,19 @@
#pragma once
#ifdef USE_SDL3
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_pixels.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_surface.h>
#else
#include <SDL.h>
#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"
@ -26,18 +34,36 @@ T NullErrDlg(T x, const char *file, int line)
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)
@ -45,26 +71,46 @@ inline SDLSurfaceUniquePtr ConvertSurface(SDL_Surface *src, const SDL_PixelForma
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
inline SDLSurfaceUniquePtr ConvertSurfaceFormat(SDL_Surface *src, Uint32 pixel_format, Uint32 flags)
#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, pixel_format, flags)) };
return SDLSurfaceUniquePtr { NonNull(SDL_ConvertSurfaceFormat(src, format, flags)) };
}
#endif
#ifndef USE_SDL1
#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

24
Source/utils/surface_to_png.cpp

@ -1,21 +1,39 @@
#include "utils/surface_to_png.hpp"
#include <cstdio>
#include <string>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#include <SDL3_image/SDL_image.h>
#else
#include <SDL.h>
#endif
#include <expected.hpp>
#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<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()));
SDL_ClearError();
return result;

14
Source/utils/surface_to_png.hpp

@ -1,7 +1,13 @@
#include <cstdio>
#include <string>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_surface.h>
#else
#include <SDL.h>
#endif
#include <expected.hpp>
#include "engine/surface.hpp"
@ -14,6 +20,12 @@ namespace devilution {
* Takes ownership of `dst` and closes it when done.
*/
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

14
Source/utils/ui_fwd.h

@ -1,19 +1,19 @@
#pragma once
#include <SDL.h>
#include <cstdint>
#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();

31
test/text_render_integration_test.cpp

@ -8,7 +8,13 @@
#include <string_view>
#include <variant>
#ifdef USE_SDL3
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_surface.h>
#else
#include <SDL.h>
#endif
#include <expected.hpp>
#include <function_ref.hpp>
@ -223,13 +229,28 @@ SDLPaletteUniquePtr LoadPalette()
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");
#endif
std::vector<std::byte> 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);
#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<void, std::string> result = WriteSurfaceToFilePng(out, actual);
ASSERT_TRUE(result.has_value()) << result.error();
EXPECT_THAT(actualPath, FileContentsEq(expectedPath));
}

Loading…
Cancel
Save