Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 3rdParty/Lua/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ elseif(TARGET_PLATFORM STREQUAL "dos")
target_compile_definitions(lua_static PUBLIC -DLUA_USE_C89)
elseif(ANDROID AND ("${ANDROID_ABI}" STREQUAL "armeabi-v7a" OR "${ANDROID_ABI}" STREQUAL "x86"))
target_compile_definitions(lua_static PUBLIC -DLUA_USE_C89)
elseif(NINTENDO_3DS OR VITA OR NINTENDO_SWITCH OR NXDK)
elseif(NINTENDO_3DS OR VITA OR NINTENDO_SWITCH OR NXDK OR DREAMCAST)
target_compile_definitions(lua_static PUBLIC -DLUA_USE_C89)
elseif(IOS)
target_compile_definitions(lua_static PUBLIC -DLUA_USE_IOS)
Expand Down
21 changes: 17 additions & 4 deletions 3rdParty/libfmt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,23 @@ else()
set(BUILD_SHARED_LIBS ON)
endif()
include(FetchContent)
FetchContent_Declare_ExcludeFromAll(libfmt
URL https://github.com/fmtlib/fmt/releases/download/12.0.0/fmt-12.0.0.zip
URL_HASH SHA256=1c32293203449792bf8e94c7f6699c643887e826f2d66a80869b4f279fb07d25
)
if(DREAMCAST)
set(DREAMCAST_FMT_PATCH "${CMAKE_CURRENT_LIST_DIR}/fmt-12.0.0-sh4-long-double.patch")
if(NOT EXISTS "${DREAMCAST_FMT_PATCH}")
message(FATAL_ERROR "Dreamcast fmt patch not found: ${DREAMCAST_FMT_PATCH}")
endif()
find_program(DREAMCAST_PATCH_EXECUTABLE patch REQUIRED)
FetchContent_Declare_ExcludeFromAll(libfmt
URL https://github.com/fmtlib/fmt/releases/download/12.0.0/fmt-12.0.0.zip
URL_HASH SHA256=1c32293203449792bf8e94c7f6699c643887e826f2d66a80869b4f279fb07d25
PATCH_COMMAND "${DREAMCAST_PATCH_EXECUTABLE}" -p1 -N -i "${DREAMCAST_FMT_PATCH}"
)
else()
FetchContent_Declare_ExcludeFromAll(libfmt
URL https://github.com/fmtlib/fmt/releases/download/12.0.0/fmt-12.0.0.zip
URL_HASH SHA256=1c32293203449792bf8e94c7f6699c643887e826f2d66a80869b4f279fb07d25
)
endif()
FetchContent_MakeAvailable_ExcludeFromAll(libfmt)

# We do not use locale-specific features of libfmt and disabling them reduces the size.
Expand Down
31 changes: 31 additions & 0 deletions 3rdParty/libfmt/fmt-12.0.0-sh4-long-double.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
From: KOS Dreamcast Port
Subject: Fix libfmt 12.0.0 long double support on SH4

On SH4 with -m4-single, long double is IEEE 754 binary64 (same as double)
but std::numeric_limits<long double>::digits == 53, which doesn't match
fmt's expected values of 64 or 113 for extended precision types.

This causes the float_info<long double> template to be incomplete,
resulting in compilation errors:
error: invalid use of incomplete type 'struct float_info<long double>'

This patch adds an explicit specialization for long double when it has
binary64 characteristics (53 mantissa bits, 8 bytes).

--- a/include/fmt/format.h
+++ b/include/fmt/format.h
@@ -1462,6 +1462,14 @@ template <> struct float_info<double> {
static const int shorter_interval_tie_lower_threshold = -77;
static const int shorter_interval_tie_upper_threshold = -77;
};
+
+// SH4/Dreamcast fix: long double is IEEE 754 binary64 on this platform.
+// Provide explicit specialization when long double has 53 mantissa bits.
+#if defined(__LDBL_MANT_DIG__) && (__LDBL_MANT_DIG__ == 53) && \
+ defined(__SIZEOF_LONG_DOUBLE__) && (__SIZEOF_LONG_DOUBLE__ == 8)
+template <>
+struct float_info<long double> : float_info<double> {};
+#endif

// An 80- or 128-bit floating point number.
template <typename T>
4 changes: 4 additions & 0 deletions CMake/Platforms.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ if(NINTENDO_3DS)
include(platforms/n3ds)
endif()

if(DREAMCAST)
include(platforms/dreamcast)
endif()

if(VITA)
include("$ENV{VITASDK}/share/vita.cmake" REQUIRED)
include(platforms/vita)
Expand Down
3 changes: 2 additions & 1 deletion CMake/functions/devilutionx_library.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ function(add_devilutionx_library NAME)
target_compile_options(${NAME} PUBLIC -Wall -Wextra -Wno-unused-parameter)
endif()

if(NOT WIN32 AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD)
if(NOT WIN32 AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD AND NOT DREAMCAST)
# Enable POSIX extensions such as `readlink` and `ftruncate`.
# Excluded for Dreamcast/KOS which doesn't have full POSIX support.
add_definitions(-D_POSIX_C_SOURCE=200809L)
endif()

Expand Down
79 changes: 79 additions & 0 deletions CMake/platforms/dreamcast.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Not automatically defined by sh-elf-gcc
add_compile_definitions(__DREAMCAST__)

set(CMAKE_BUILD_TYPE MinSizeRel CACHE STRING "" FORCE)

set(ASAN OFF)
set(UBSAN OFF)
set(BUILD_TESTING OFF)
set(NONET ON)
set(USE_SDL1 ON)
set(NOEXIT ON)
set(PREFILL_PLAYER_NAME ON)
set(DISABLE_DEMOMODE ON)
set(DEVILUTIONX_SYSTEM_LIBSODIUM OFF)
set(DEVILUTIONX_SYSTEM_SDL_IMAGE OFF)
set(UNPACKED_MPQS OFF)

# CD reads via MPQ are too slow for real-time audio
set(DISABLE_STREAMING_SOUNDS ON)
set(DEFAULT_AUDIO_BUFFER_SIZE 2048)
set(DEFAULT_AUDIO_SAMPLE_RATE 22050)
set(DEFAULT_AUDIO_RESAMPLING_QUALITY 0)

set(DEFAULT_PER_PIXEL_LIGHTING 0)
set(DEVILUTIONX_PALETTE_TRANSPARENCY_BLACK_16_LUT ON CACHE BOOL "" FORCE)

# GPF SDL supports 8bpp with hardware palette - lets SDL handle the
# palette-to-RGB conversion internally via PVR DMA, avoiding the
# manual 8bpp->16bpp conversion in dc_video.cpp entirely.
set(SDL1_VIDEO_MODE_BPP 8)
set(SDL1_VIDEO_MODE_FLAGS SDL_DOUBLEBUF|SDL_HWSURFACE)

set(DEFAULT_WIDTH 640)
set(DEFAULT_HEIGHT 480)

# VMU saves use flat files with zlib compression
set(UNPACKED_SAVES ON)

set(DEVILUTIONX_GAMEPAD_TYPE Generic)

# GPF SDL button IDs (from SDL_dreamcast.h SDL_DC_button enum)
set(JOY_BUTTON_A 2)
set(JOY_BUTTON_B 1)
set(JOY_BUTTON_X 5)
set(JOY_BUTTON_Y 6)
set(JOY_BUTTON_START 3)

set(JOY_HAT_DPAD_UP_HAT 0)
set(JOY_HAT_DPAD_DOWN_HAT 0)
set(JOY_HAT_DPAD_LEFT_HAT 0)
set(JOY_HAT_DPAD_RIGHT_HAT 0)
set(JOY_HAT_DPAD_UP 1)
set(JOY_HAT_DPAD_DOWN 4)
set(JOY_HAT_DPAD_LEFT 8)
set(JOY_HAT_DPAD_RIGHT 2)

set(JOY_AXIS_LEFTX 0)
set(JOY_AXIS_LEFTY 1)

list(APPEND DEVILUTIONX_PLATFORM_SUBDIRECTORIES platform/dreamcast)
list(APPEND DEVILUTIONX_PLATFORM_LINK_LIBRARIES libdevilutionx_dreamcast)

# FindZLIB may not create this target during cross-compilation.
if(NOT TARGET ZLIB::ZLIB)
add_library(ZLIB::ZLIB STATIC IMPORTED)
set_target_properties(ZLIB::ZLIB PROPERTIES
IMPORTED_LOCATION "${ZLIB_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}"
)
endif()

# FindBZip2 may not create this target during cross-compilation
if(NOT TARGET BZip2::BZip2)
add_library(BZip2::BZip2 STATIC IMPORTED)
set_target_properties(BZip2::BZip2 PROPERTIES
IMPORTED_LOCATION "${BZIP2_LIBRARIES}"
INTERFACE_INCLUDE_DIRECTORIES "${BZIP2_INCLUDE_DIR}"
)
endif()
67 changes: 67 additions & 0 deletions CMake/platforms/dreamcast.toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# DevilutionX Dreamcast Toolchain
# We extend the official KOS toolchain to add project-specific settings.

if(NOT DEFINED ENV{KOS_CMAKE_TOOLCHAIN})
message(FATAL_ERROR "KOS_CMAKE_TOOLCHAIN not defined. Please source environ.sh.")
endif()

# 1. Load the official KOS toolchain
# This sets up compilers, flags (-m4-single), and system paths correctly.
include("$ENV{KOS_CMAKE_TOOLCHAIN}")

# 2. Set DevilutionX specific platform flag
# This triggers loading CMake/platforms/dreamcast.cmake
set(DREAMCAST ON)

# 3. Add our include paths
# KOS ports often hide headers in subdirectories
# GPF SDL installs to KOS_BASE/addons/include/dreamcast/SDL/
list(APPEND CMAKE_INCLUDE_PATH
"$ENV{KOS_PORTS}/include/zlib"
"$ENV{KOS_PORTS}/include/bzlib"
"$ENV{KOS_BASE}/addons/include/dreamcast/SDL"
)

# 4. Force libraries (The "Nuclear Option" for sub-projects)
# Standard find modules struggle with KOS layout, so we force them.
set(ZLIB_INCLUDE_DIR "$ENV{KOS_PORTS}/include/zlib" CACHE PATH "ZLIB Include Dir" FORCE)
set(ZLIB_LIBRARY "$ENV{KOS_PORTS}/lib/libz.a" CACHE FILEPATH "ZLIB Library" FORCE)
set(BZIP2_INCLUDE_DIR "$ENV{KOS_PORTS}/include/bzlib" CACHE PATH "BZip2 Include Dir" FORCE)
set(BZIP2_LIBRARIES "$ENV{KOS_PORTS}/lib/libbz2.a" CACHE FILEPATH "BZip2 Library" FORCE)

# 5. Force SDL1 - GPF SDL (SDL-dreamhal--GLDC) for DMA video and hardware palette
# GPF SDL installs via its Makefile.dc to KOS_BASE/addons/{lib,include}/dreamcast/
set(SDL_INCLUDE_DIR "$ENV{KOS_BASE}/addons/include/dreamcast/SDL" CACHE PATH "SDL Include Dir" FORCE)
set(SDL_LIBRARY "$ENV{KOS_BASE}/addons/lib/dreamcast/libSDL.a" CACHE FILEPATH "SDL Library" FORCE)

# 6. Fix libfmt and magic_enum compilation
# Disable long double support because sh4-gcc's long double (64-bit) confuses libfmt
# Disable magic_enum asserts because GCC 15's stricter parsing breaks them in comma expressions
add_compile_definitions(FMT_USE_LONG_DOUBLE=0 FMT_USE_FLOAT128=0 MAGIC_ENUM_NO_ASSERT)

# 7. KOS doesn't have pthreads as a separate library - threading is built into libkallisti
set(CMAKE_THREAD_LIBS_INIT "" CACHE STRING "" FORCE)
set(CMAKE_HAVE_THREADS_LIBRARY 1)
set(Threads_FOUND TRUE)

# 8. Remove host system library paths that sneak in
set(CMAKE_EXE_LINKER_FLAGS "" CACHE STRING "" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "" FORCE)

# 8b. Ignore host system paths (homebrew, etc.) to prevent finding wrong libraries
set(CMAKE_IGNORE_PATH "/opt/homebrew;/opt/homebrew/include;/opt/homebrew/lib;/usr/local;/usr/local/include;/usr/local/lib" CACHE STRING "" FORCE)

# 8c. Force using bundled magic_enum (not system/homebrew version)
set(DEVILUTIONX_SYSTEM_MAGIC_ENUM OFF CACHE BOOL "" FORCE)

# 9. Override Link Rule to use Grouping
# KOS toolchain already adds -T, -nodefaultlibs, and -L paths via <LINK_FLAGS>
# We just need to add --start-group/--end-group for circular dependency resolution
# NOTE: kos_add_romdisk() in CMakeLists.txt handles -lromdiskbase with --whole-archive
set(CMAKE_CXX_LINK_EXECUTABLE
"<CMAKE_CXX_COMPILER> <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--start-group <LINK_LIBRARIES> -lkallisti -lc -lgcc -lm -lstdc++ -Wl,--end-group")
set(CMAKE_C_LINK_EXECUTABLE
"<CMAKE_C_COMPILER> <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--start-group <LINK_LIBRARIES> -lkallisti -lc -lgcc -lm -lstdc++ -Wl,--end-group")

# 10. Skip compiler checks (saves time and avoids romdisk symbol issues)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
50 changes: 50 additions & 0 deletions Packaging/dreamcast/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Build artifacts
*.iso
*.cdi
*.bin

# Disc staging directories
cd_root/
flycast_disc/
romdisk/
objects/
towners/
items/
levels/
monsters/
music/
nlevels/
plrgfx/
sfx/
speech/
ui_art/
data/
fonts/
gendata/
arena/

# Extracted game data (copyrighted)
diabdat/
DIABDAT.MPQ
spawn.mpq

# Extracted file dumps (from MPQ tools)
File[0-9]*.pcx
File[0-9]*.wav
File[0-9]*.xxx
File[0-9]*.gif
File[0-9]*.smk

# GPF SDL source (cloned by build.sh)
SDL-gpf/

# Build tool output
*.o

# Listfiles (generated)
*_listfile*.txt

# Python / IDE
.venv/
.qodo/
__pycache__/
39 changes: 39 additions & 0 deletions Packaging/dreamcast/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Dreamcast Build

This folder contains the Dreamcast packaging flow for DevilutionX.

## Prerequisites

- [KallistiOS](https://kos-docs.dreamcast.wiki/) (KOS) with kos-ports (zlib, bzip2)
- [`mkdcdisc`](https://gitlab.com/simulant/mkdcdisc) for CDI disc image creation

SDL (GPF SDL with DMA video) and Lua are built from source automatically by `build.sh`.
No external `fmt` patch is required. The build applies a bundled SH4 `libfmt` patch automatically.

## Game Data (Required)

- You must provide your own MPQ files. Diablo data files are proprietary assets owned by Blizzard.
- Copy `DIABDAT.MPQ` to `Packaging/dreamcast/cd_root/`.
- `spawn.mpq` is optional for shareware mode.
- For extraction methods, see [Extracting MPQs from the GoG installer](https://github.com/diasurgical/devilutionX/wiki/Extracting-MPQs-from-the-GoG-installer).

## Build

From repo root:

```sh
./Packaging/dreamcast/build.sh
```

## Output

- Intermediate ELF: `build-dreamcast/devilutionx.elf`
- Bootable CDI: `Packaging/dreamcast/devilutionx-playable.cdi`

## Notes

- `build.sh` deletes and recreates `build-dreamcast/`.
- Override tool paths with env vars if needed:
- `KOS_BASE`
- `KOS_ENV`
- `MKDCDISC`
Loading
Loading