From 01507d532cdf6c3187742d1d1770f9a172b68350 Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Sun, 31 Aug 2025 17:34:11 +0200 Subject: [PATCH] Add basic DOS port (#8155) --- 3rdParty/Lua/CMakeLists.txt | 2 ++ 3rdParty/SDL_image/CMakeLists.txt | 2 +- 3rdParty/libsmackerdec/CMakeLists.txt | 2 +- CMake/Assets.cmake | 6 +++- CMake/Dependencies.cmake | 2 +- CMake/Platforms.cmake | 2 ++ CMake/platforms/djcpp.toolchain.cmake | 27 ++++++++++++++ CMake/platforms/dos.cmake | 29 +++++++++++++++ CMakeLists.txt | 21 +++++++++-- Packaging/windows/dos-prep.sh | 49 ++++++++++++++++++++++++++ Source/CMakeLists.txt | 4 ++- Source/effects_stubs.cpp | 12 +++++++ Source/engine/assets.cpp | 6 +++- Source/mpq/mpq_writer.cpp | 4 ++- Source/options.cpp | 16 ++++++--- Source/panels/console.cpp | 2 +- Source/pfile.cpp | 4 ++- Source/utils/display.cpp | 1 + Source/utils/file_util.cpp | 12 ++++--- Source/utils/sdl2_to_1_2_backports.cpp | 2 +- Source/utils/sdl_mutex.h | 18 ++++++++++ Source/utils/sdl_thread.cpp | 2 ++ Source/utils/sdl_thread.h | 32 +++++++++++++++++ docs/building.md | 24 +++++++++++-- 24 files changed, 259 insertions(+), 22 deletions(-) create mode 100644 CMake/platforms/djcpp.toolchain.cmake create mode 100644 CMake/platforms/dos.cmake create mode 100755 Packaging/windows/dos-prep.sh diff --git a/3rdParty/Lua/CMakeLists.txt b/3rdParty/Lua/CMakeLists.txt index fffed7ca8..04147a54d 100644 --- a/3rdParty/Lua/CMakeLists.txt +++ b/3rdParty/Lua/CMakeLists.txt @@ -26,6 +26,8 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND DARWIN_MAJOR_VERSION VERSION_EQUAL 8) # localtime_r gmtime_r find_package(MacportsLegacySupport REQUIRED) target_link_libraries(lua_static PRIVATE MacportsLegacySupport::MacportsLegacySupport) +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) diff --git a/3rdParty/SDL_image/CMakeLists.txt b/3rdParty/SDL_image/CMakeLists.txt index 722f5d853..2e676d637 100644 --- a/3rdParty/SDL_image/CMakeLists.txt +++ b/3rdParty/SDL_image/CMakeLists.txt @@ -35,7 +35,7 @@ target_include_directories(SDL_image PRIVATE ${sdl_image_SOURCE_DIR}) target_compile_definitions(SDL_image PRIVATE LOAD_PNG SDL_IMAGE_USE_COMMON_BACKEND) target_link_libraries(SDL_image PNG::PNG) -if(TARGET SDL2::SDL2) +if(TARGET SDL2::SDL2 AND NOT (DEVILUTIONX_STATIC_SDL2 AND TARGET SDL2::SDL2-static)) target_link_libraries(SDL_image SDL2::SDL2) add_library(SDL2::SDL2_image ALIAS SDL_image) elseif(TARGET SDL2::SDL2-static) diff --git a/3rdParty/libsmackerdec/CMakeLists.txt b/3rdParty/libsmackerdec/CMakeLists.txt index 4a8c18e8e..86915b5b5 100644 --- a/3rdParty/libsmackerdec/CMakeLists.txt +++ b/3rdParty/libsmackerdec/CMakeLists.txt @@ -17,7 +17,7 @@ target_include_directories(libsmackerdec PUBLIC ${libsmackerdec_SOURCE_DIR}/incl if(USE_SDL1) target_link_libraries(libsmackerdec PUBLIC ${SDL_LIBRARY}) -elseif(TARGET SDL2::SDL2) +elseif(TARGET SDL2::SDL2 AND NOT (DEVILUTIONX_STATIC_SDL2 AND TARGET SDL2::SDL2-static)) target_link_libraries(libsmackerdec PUBLIC SDL2::SDL2) elseif(TARGET SDL2::SDL2-static) target_link_libraries(libsmackerdec PUBLIC SDL2::SDL2-static) diff --git a/CMake/Assets.cmake b/CMake/Assets.cmake index 7fd418377..d09a4f28d 100644 --- a/CMake/Assets.cmake +++ b/CMake/Assets.cmake @@ -236,7 +236,11 @@ else() endif() if(BUILD_ASSETS_MPQ) - set(DEVILUTIONX_MPQ "${CMAKE_CURRENT_BINARY_DIR}/devilutionx.mpq") + if(TARGET_PLATFORM STREQUAL "dos") + set(DEVILUTIONX_MPQ "${CMAKE_CURRENT_BINARY_DIR}/devx.mpq") + else() + set(DEVILUTIONX_MPQ "${CMAKE_CURRENT_BINARY_DIR}/devilutionx.mpq") + endif() add_custom_command( COMMENT "Building devilutionx.mpq" OUTPUT "${DEVILUTIONX_MPQ}" diff --git a/CMake/Dependencies.cmake b/CMake/Dependencies.cmake index 788bcc6c0..118a0a9bd 100644 --- a/CMake/Dependencies.cmake +++ b/CMake/Dependencies.cmake @@ -104,7 +104,7 @@ if(USE_SDL1) target_link_libraries(DevilutionX::SDL INTERFACE ${SDL_LIBRARY}) target_compile_definitions(DevilutionX::SDL INTERFACE USE_SDL1) else() - if(TARGET SDL2::SDL2) + if(TARGET SDL2::SDL2 AND NOT (DEVILUTIONX_STATIC_SDL2 AND TARGET SDL2::SDL2-static)) target_link_libraries(DevilutionX::SDL INTERFACE SDL2::SDL2) elseif(TARGET SDL2::SDL2-static) target_link_libraries(DevilutionX::SDL INTERFACE SDL2::SDL2-static) diff --git a/CMake/Platforms.cmake b/CMake/Platforms.cmake index ebd1b4783..44f6826e8 100644 --- a/CMake/Platforms.cmake +++ b/CMake/Platforms.cmake @@ -33,6 +33,8 @@ elseif(TARGET_PLATFORM STREQUAL "windows9x") include(platforms/windows9x) elseif(TARGET_PLATFORM STREQUAL "windowsXP") include(platforms/windowsXP) +elseif(TARGET_PLATFORM STREQUAL "dos") + include(platforms/dos) elseif(WIN32) include(platforms/windows) endif() diff --git a/CMake/platforms/djcpp.toolchain.cmake b/CMake/platforms/djcpp.toolchain.cmake new file mode 100644 index 000000000..ebe31647e --- /dev/null +++ b/CMake/platforms/djcpp.toolchain.cmake @@ -0,0 +1,27 @@ +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_VERSION 1) + +if($ENV{DJGPP_PREFIX}) + set(DJGPP_PREFIX "$ENV{DJGPP_PREFIX}") +else() + set(DJGPP_PREFIX "/opt/i386-pc-msdosdjgpp-toolchain") +endif() + +set(CMAKE_C_COMPILER "${DJGPP_PREFIX}/bin/i386-pc-msdosdjgpp-gcc") +set(CMAKE_CXX_COMPILER "${DJGPP_PREFIX}/bin/i386-pc-msdosdjgpp-g++") +set(CMAKE_STRIP "${DJGPP_PREFIX}/bin/i386-pc-msdosdjgpp-strip") +set(PKG_CONFIG_EXECUTABLE "${DJGPP_PREFIX}/bin/i386-pc-msdosdjgpp-pkg-config" CACHE STRING "Path to pkg-config") + +set(CMAKE_EXE_LINKER_FLAGS_INIT "-static") + +set(DJGPP_ROOT "${DJGPP_PREFIX}/i386-pc-msdosdjgpp") + +set(CMAKE_FIND_ROOT_PATH "${DJGPP_ROOT}") +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +link_directories("${DJGPP_ROOT}/lib") +include_directories(BEFORE SYSTEM "${DJGPP_ROOT}/sys-include" "${DJGPP_ROOT}/include") + diff --git a/CMake/platforms/dos.cmake b/CMake/platforms/dos.cmake new file mode 100644 index 000000000..e68ae23c4 --- /dev/null +++ b/CMake/platforms/dos.cmake @@ -0,0 +1,29 @@ +set(ASAN OFF) +set(UBSAN OFF) +set(DIST ON) + +set(NONET ON) +set(NOSOUND ON) + +set(PREFILL_PLAYER_NAME ON) + +set(DEVILUTIONX_SYSTEM_BZIP2 OFF) +set(DEVILUTIONX_SYSTEM_LIBFMT OFF) +set(DEVILUTIONX_SYSTEM_ZLIB OFF) +set(DEVILUTIONX_STATIC_ZLIB ON) +set(DEVILUTIONX_SYSTEM_SDL2 ON) +set(DEVILUTIONX_STATIC_SDL2 ON) +set(DEVILUTIONX_SYSTEM_SDL_IMAGE OFF) +set(DEVILUTIONX_SYSTEM_LIBPNG OFF) + +set(DEVILUTIONX_PLATFORM_FILE_UTIL_LINK_LIBRARIES "") + +list(APPEND DEVILUTIONX_PLATFORM_COMPILE_OPTIONS $<$:-gstabs>) + +add_compile_definitions( + SDL_DISABLE_IMMINTRIN_H + SDL_DISABLE_XMMINTRIN_H + SDL_DISABLE_EMMINTRIN_H + SDL_DISABLE_PMMINTRIN_H + SDL_DISABLE_MMINTRIN_H +) diff --git a/CMakeLists.txt b/CMakeLists.txt index d750b0a3e..4f34809f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,12 +320,19 @@ if(GPERF) endif() set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_EXTENSIONS OFF) + +# On some platforms, such as DJGPP, +# `-std=c++20` defines `__STRICT_ANSI__` which disables +# all POSIX extensions, so we need to use `-std=gnu++20` instead. +set(CMAKE_CXX_EXTENSIONS ON) + set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # for clang-tidy set(CMAKE_THREAD_PREFER_PTHREAD ON) set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) +if(NOT TARGET_PLATFORM STREQUAL "dos") + find_package(Threads REQUIRED) +endif() # Dependencies must be included after Platforms. include(Dependencies) @@ -335,6 +342,8 @@ add_subdirectory(Source) set(BIN_TARGET devilutionx) if(NINTENDO_3DS) set(BIN_TARGET ${BIN_TARGET}.elf) +elseif(TARGET_PLATFORM STREQUAL "dos") + set(BIN_TARGET devx) endif() if(ANDROID) @@ -358,6 +367,14 @@ else() endif() endif() +if(TARGET_PLATFORM STREQUAL "dos") + # Allow multiple definitions for math stubs in DJGPP + set_target_properties(${BIN_TARGET} PROPERTIES + LINK_FLAGS "-Wl,--allow-multiple-definition -static" + ) + target_link_libraries(${BIN_TARGET} PRIVATE m) +endif() + if(NOT UWP_LIB) target_link_dependencies(${BIN_TARGET} PRIVATE libdevilutionx) endif() diff --git a/Packaging/windows/dos-prep.sh b/Packaging/windows/dos-prep.sh new file mode 100755 index 000000000..9dea48806 --- /dev/null +++ b/Packaging/windows/dos-prep.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# only use sudo when necessary +if [ `id -u` -ne 0 ]; then + SUDO=sudo +else + SUDO="" +fi + +# Install dependencies on Debian / Ubuntu: +if which apt-get 2>/dev/null; then + set -x + $SUDO apt-get update + $SUDO apt-get install bison flex curl gcc g++ make texinfo zlib1g-dev tar bzip2 \ + gzip xz-utils unzip python3-dev m4 dos2unix nasm cmake + { set +x; } 2>/dev/null +fi + +INSTALL_PREFIX=/opt/i386-pc-msdosdjgpp-toolchain + +set -x +mkdir -p tmp/dos-prep +cd tmp/dos-prep + +# Build and install DJGPP +git clone https://github.com/jwt27/build-gcc.git +cd build-gcc +$SUDO ./build-djgpp.sh --prefix="$INSTALL_PREFIX" --batch binutils gcc-14.2.0 djgpp-cvs +cd - +$SUDO rm -rf build-gcc + +# Activate DJGPP environment +{ set +x; } 2>/dev/null +set +eu +source "${INSTALL_PREFIX}/bin/i386-pc-msdosdjgpp-setenv" +set -eu +set -x + +# Build and install SDL2 for DOS +git clone --branch=dos-vbe https://github.com/diasurgical/SDL.git +cd SDL +autoreconf -fi +./configure --host=i386-pc-msdosdjgpp --prefix="${INSTALL_PREFIX}/i386-pc-msdosdjgpp" --disable-shared --enable-static --enable-video-svga --enable-timer-dos --enable-uclock +make -j$(nproc) +$SUDO "${INSTALL_PREFIX}/bin/i386-pc-msdosdjgpp-setenv" make install +cd - +rm -rf SDL diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 119215e11..935758f59 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -877,7 +877,6 @@ endif() add_devilutionx_object_library(libdevilutionx ${libdevilutionx_SRCS}) target_include_directories(libdevilutionx PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) target_link_dependencies(libdevilutionx PUBLIC - Threads::Threads DevilutionX::SDL fmt::fmt libsmackerdec @@ -931,6 +930,9 @@ target_link_dependencies(libdevilutionx PUBLIC libdevilutionx_utf8 libdevilutionx_utils_console ) +if(NOT TARGET_PLATFORM STREQUAL "dos") + target_link_dependencies(libdevilutionx PUBLIC Threads::Threads) +endif() if(DEVILUTIONX_SCREENSHOT_FORMAT STREQUAL DEVILUTIONX_SCREENSHOT_FORMAT_PNG) target_link_dependencies(libdevilutionx PUBLIC libdevilutionx_surface_to_png) endif() diff --git a/Source/effects_stubs.cpp b/Source/effects_stubs.cpp index 12b588af4..b424dc3ae 100644 --- a/Source/effects_stubs.cpp +++ b/Source/effects_stubs.cpp @@ -1,6 +1,9 @@ // Stubbed implementations of effects for the NOSOUND mode. #include "effects.h" +#include +#include + #include "engine/random.hpp" namespace devilution { @@ -47,4 +50,13 @@ void ui_sound_init() { } void effects_play_sound(SfxID id) { } int GetSFXLength(SfxID nSFX) { return 0; } +tl::expected ParseSfxId(std::string_view value) +{ + const std::optional enumValueOpt = magic_enum::enum_cast(value); + if (enumValueOpt.has_value()) { + return enumValueOpt.value(); + } + return tl::make_unexpected("Unknown enum value."); +} + } // namespace devilution diff --git a/Source/engine/assets.cpp b/Source/engine/assets.cpp index 169ebf994..98d2fb14f 100644 --- a/Source/engine/assets.cpp +++ b/Source/engine/assets.cpp @@ -88,7 +88,7 @@ AssetRef FindAsset(std::string_view filename) char *const relativePath = &pathBuf[AssetRef::PathBufSize - filename.size() - 1]; *BufCopy(relativePath, filename) = '\0'; -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__DJGPP__) std::replace(relativePath, pathEnd, '\\', '/'); #endif // Absolute path: @@ -383,7 +383,11 @@ void LoadCoreArchives() #if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(__3DS__) && !defined(__SWITCH__) // Load devilutionx.mpq first to get the font file for error messages +#ifdef __DJGPP__ + LoadMPQ(paths, "devx", DevilutionXMpqPriority); +#else LoadMPQ(paths, "devilutionx", DevilutionXMpqPriority); +#endif #endif LoadMPQ(paths, "fonts", FontMpqPriority); // Extra fonts HasHellfireMpq = FindMPQ(paths, "hellfire"); diff --git a/Source/mpq/mpq_writer.cpp b/Source/mpq/mpq_writer.cpp index 511d12be7..c3909c852 100644 --- a/Source/mpq/mpq_writer.cpp +++ b/Source/mpq/mpq_writer.cpp @@ -89,7 +89,9 @@ bool IsUnallocatedBlock(const MpqBlockEntry *block) MpqWriter::MpqWriter(const char *path) { const std::string dir = std::string(Dirname(path)); - RecursivelyCreateDir(dir.c_str()); + if (!dir.empty()) { + RecursivelyCreateDir(dir.c_str()); + } LogVerbose("Opening {}", path); bool isNewFile = false; std::string error; diff --git a/Source/options.cpp b/Source/options.cpp index c3c6a44d3..2bfe3fa73 100644 --- a/Source/options.cpp +++ b/Source/options.cpp @@ -157,7 +157,9 @@ void SaveIni() { if (!ini.has_value()) return; if (!ini->changed()) return; - RecursivelyCreateDir(paths::ConfigPath().c_str()); + if (!paths::ConfigPath().empty()) { + RecursivelyCreateDir(paths::ConfigPath().c_str()); + } const std::string iniPath = GetIniPath(); LoggedFStream out; if (!out.Open(iniPath.c_str(), "wb")) { @@ -194,7 +196,7 @@ Options &GetOptions() #if SDL_VERSION_ATLEAST(2, 0, 0) bool HardwareCursorSupported() { -#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) || __DJGPP__ return false; #else SDL_version v; @@ -686,11 +688,17 @@ GraphicsOptions::GraphicsOptions() : OptionCategoryBase("Graphics", N_("Graphics"), N_("Graphics Settings")) , fullscreen("Fullscreen", OnlyIfSupportsWindowed | OptionEntryFlags::CantChangeInGame | OptionEntryFlags::RecreateUI, N_("Fullscreen"), N_("Display the game in windowed or fullscreen mode."), true) #if !defined(USE_SDL1) || defined(__3DS__) - , fitToScreen("Fit to Screen", OptionEntryFlags::CantChangeInGame | OptionEntryFlags::RecreateUI, N_("Fit to Screen"), N_("Automatically adjust the game window to your current desktop screen aspect ratio and resolution."), true) + , fitToScreen("Fit to Screen", OptionEntryFlags::CantChangeInGame | OptionEntryFlags::RecreateUI, N_("Fit to Screen"), N_("Automatically adjust the game window to your current desktop screen aspect ratio and resolution."), +#ifdef __DJGPP__ + false +#else + true +#endif + ) #endif #ifndef USE_SDL1 , upscale("Upscale", OptionEntryFlags::Invisible | OptionEntryFlags::CantChangeInGame | OptionEntryFlags::RecreateUI, N_("Upscale"), N_("Enables image scaling from the game resolution to your monitor resolution. Prevents changing the monitor resolution and allows window resizing."), -#ifdef NXDK +#if defined(NXDK) || defined(__DJGPP__) false #else true diff --git a/Source/panels/console.cpp b/Source/panels/console.cpp index d922e6396..eb1169cfb 100644 --- a/Source/panels/console.cpp +++ b/Source/panels/console.cpp @@ -579,7 +579,7 @@ void DrawConsole(const Surface &out) }; if (FirstRender) { - const SDL_Rect sdlInputRect = MakeSdlRect(InputRect); + SDL_Rect sdlInputRect = MakeSdlRect(InputRect); SDL_SetTextInputRect(&sdlInputRect); SDL_StartTextInput(); FirstRender = false; diff --git a/Source/pfile.cpp b/Source/pfile.cpp index 158f4fc6e..67e6d483e 100644 --- a/Source/pfile.cpp +++ b/Source/pfile.cpp @@ -170,7 +170,9 @@ void CopySaveFile(uint32_t saveNum, std::string targetPath) #ifdef DVL_NO_FILESYSTEM #error "UNPACKED_SAVES requires either DISABLE_DEMOMODE or C++17 " #endif - CreateDir(targetPath.c_str()); + if (!targetPath.empty()) { + CreateDir(targetPath.c_str()); + } for (const std::filesystem::directory_entry &entry : std::filesystem::directory_iterator(savePath)) { CopyFileOverwrite(entry.path().string().c_str(), (targetPath + entry.path().filename().string()).c_str()); } diff --git a/Source/utils/display.cpp b/Source/utils/display.cpp index fcc73f001..0773eabee 100644 --- a/Source/utils/display.cpp +++ b/Source/utils/display.cpp @@ -242,6 +242,7 @@ void UpdateAvailableResolutions() ErrSdl(); } for (auto &size : sizes) { + if (mode.h == 0) continue; // Ensure that the ini specified resolution remains present in the resolution list if (size.height == configuredSize.height) size.width = configuredSize.width; diff --git a/Source/utils/file_util.cpp b/Source/utils/file_util.cpp index 5b377e551..f2b9569a3 100644 --- a/Source/utils/file_util.cpp +++ b/Source/utils/file_util.cpp @@ -28,7 +28,11 @@ #endif #endif -#if (_POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__)) && !defined(DEVILUTIONX_WINDOWS_NO_WCHAR) +#if _POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__) || defined(__DJGPP__) +#define DVL_HAS_POSIX_2001 +#endif + +#if defined(DVL_HAS_POSIX_2001) && !defined(DEVILUTIONX_WINDOWS_NO_WCHAR) #include #include @@ -103,7 +107,7 @@ bool FileExists(const char *path) return false; } return true; -#elif (_POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__)) && !defined(__ANDROID__) +#elif defined(DVL_HAS_POSIX_2001) && !defined(__ANDROID__) return ::access(path, F_OK) == 0; #elif defined(DVL_HAS_FILESYSTEM) std::error_code ec; @@ -167,7 +171,7 @@ bool FileExistsAndIsWriteable(const char *path) #ifdef _WIN32 const DWORD attr = WindowsGetFileAttributes(path); return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_READONLY) == 0; -#elif (_POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__)) && !defined(__ANDROID__) +#elif defined(DVL_HAS_POSIX_2001) && !defined(__ANDROID__) return ::access(path, W_OK) == 0; #else if (!FileExists(path)) @@ -351,7 +355,7 @@ bool ResizeFile(const char *path, std::uintmax_t size) } ::CloseHandle(file); return true; -#elif _POSIX_C_SOURCE >= 200112L || defined(_BSD_SOURCE) || defined(__APPLE__) +#elif defined(DVL_HAS_POSIX_2001) return ::truncate(path, static_cast(size)) == 0; #else static_assert(false, "truncate not implemented for the current platform"); diff --git a/Source/utils/sdl2_to_1_2_backports.cpp b/Source/utils/sdl2_to_1_2_backports.cpp index f8de050be..6befb7911 100644 --- a/Source/utils/sdl2_to_1_2_backports.cpp +++ b/Source/utils/sdl2_to_1_2_backports.cpp @@ -862,7 +862,7 @@ extern "C" char *SDL_GetPrefPath(const char *org, const char *app) * * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html */ -#if defined(WINVER) && WINVER <= 0x0500 && (!defined(_WIN32_WINNT) || _WIN32_WINNT == 0) +#if (defined(WINVER) && WINVER <= 0x0500 && (!defined(_WIN32_WINNT) || _WIN32_WINNT == 0)) || defined(__DJGPP__) // On Windows9x there is no such thing as PrefPath. Simply use the current directory. char *result = (char *)SDL_malloc(1); *result = '\0'; diff --git a/Source/utils/sdl_mutex.h b/Source/utils/sdl_mutex.h index 95286e480..4284de78b 100644 --- a/Source/utils/sdl_mutex.h +++ b/Source/utils/sdl_mutex.h @@ -13,6 +13,23 @@ namespace devilution { * RAII wrapper for SDL_mutex. Satisfies std's "Lockable" (SDL 2) or "BasicLockable" (SDL 1) * requirements so it can be used with std::lock_guard and friends. */ +#ifdef __DJGPP__ +class SdlMutex final { +public: + SdlMutex() noexcept { } + ~SdlMutex() noexcept { } + + SdlMutex(const SdlMutex &) = delete; + SdlMutex(SdlMutex &&) = delete; + SdlMutex &operator=(const SdlMutex &) = delete; + SdlMutex &operator=(SdlMutex &&) = delete; + + void lock() noexcept { } + void unlock() noexcept { } + + void *get() noexcept { return nullptr; } // Dummy +}; +#else class SdlMutex final { public: SdlMutex() @@ -64,5 +81,6 @@ public: private: SDL_mutex *mutex_; }; +#endif } // namespace devilution diff --git a/Source/utils/sdl_thread.cpp b/Source/utils/sdl_thread.cpp index 5aa7ad014..0279f23dc 100644 --- a/Source/utils/sdl_thread.cpp +++ b/Source/utils/sdl_thread.cpp @@ -2,6 +2,7 @@ namespace devilution { +#ifndef __DJGPP__ int SDLCALL SdlThread::ThreadTranslate(void *ptr) { auto handler = (void (*)())ptr; @@ -16,5 +17,6 @@ void SdlThread::ThreadDeleter(SDL_Thread *thread) if (thread != nullptr) app_fatal("Joinable thread destroyed"); } +#endif } // namespace devilution diff --git a/Source/utils/sdl_thread.h b/Source/utils/sdl_thread.h index 336a8ec5e..6a78ea70d 100644 --- a/Source/utils/sdl_thread.h +++ b/Source/utils/sdl_thread.h @@ -15,10 +15,40 @@ namespace devilution { namespace this_sdl_thread { inline SDL_threadID get_id() { +#if defined(__DJGPP__) + return 1; +#else return SDL_GetThreadID(nullptr); +#endif } } // namespace this_sdl_thread +#if defined(__DJGPP__) +class SdlThread final { +public: + SdlThread(int(SDLCALL *handler)(void *), void *data) + { + if (handler != nullptr) handler(data); + } + explicit SdlThread(void (*handler)(void)) + { + if (handler != nullptr) handler(); + } + SdlThread() = default; + bool joinable() const + { + return false; + } + SDL_threadID get_id() const + { + return this_sdl_thread::get_id(); + } + void join() + { + } +}; +#else + class SdlThread final { static int SDLCALL ThreadTranslate(void *ptr); static void ThreadDeleter(SDL_Thread *thread); @@ -66,4 +96,6 @@ public: } }; +#endif + } // namespace devilution diff --git a/docs/building.md b/docs/building.md index bca6070d2..61cd3c42f 100644 --- a/docs/building.md +++ b/docs/building.md @@ -234,14 +234,14 @@ These can be done automatically by running [`Packaging/windows/mingw-prep64.sh`] Note: If your `x86_64-w64-mingw32` directory is not in `/usr` (e.g. when on Debian), the mingw-prep scripts and the CMake command won't work. You need adjust the mingw-prep scripts and pass `-DCROSS_PREFIX=/path` to CMake to set -the path to the parent of the `x86_64-w64-mingw32` directory. +the path to the parent of the `x86_64-w64-mingw32` directory. ```bash # Download the 64-bit development libraries for SDL2 and libsodium # as well as the headers for zlib and place them in subfolders under # /usr/x86_64-w64-mingw32 Packaging/windows/mingw-prep64.sh -``` +``` ### Compiling @@ -623,6 +623,26 @@ sudo port select --set python3 python312 +
DOS + +You can build for DOS from Linux using DJGPP. + +First, install / compile the dependencies (only needs to be done once): + +~~~ bash +Packaging/windows/dos-prep.sh +~~~ + +Then, build DevilutionX: + +~~~ bash +cmake -S. -Bbuild-dos -DCMAKE_TOOLCHAIN_FILE=CMake/platforms/djcpp.toolchain.cmake -DTARGET_PLATFORM="dos" \ + -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF +cmake --build build-dos -j $(getconf _NPROCESSORS_ONLN) +~~~ + +
+
CMake build options ### General