Since all of the object files we're building go into the binary that
uses `-pthread` (due to SDL, where available), all of them should be
compiled with `-pthread` to ensure a consistent threading model.
This also allows all object files to use the same PCH on clang,
where threading model is part of the PCH data.
Like Amiga and 3DS, DOS does not have a system threads library. Use
CMake/platforms/threads-stub to provide the dummy Threads::Threads target
instead of special-casing DOS in CMakeLists.txt and
Source/CMakeLists.txt.
DevilutionX::SDL carried libdevilutionx_sdl2_to_1_2_backports, so
linking SDL pulled in our own compatibility shims, and with them
libdevilutionx_strings and libdevilutionx_utils_console. Anything that
only wanted the SDL headers got a chunk of devilutionX behind them.
Drop it from DevilutionX::SDL and let the targets that actually use the
shims say so. That is every target whose objects reference a symbol
defined by sdl2_to_1_2_backports.cpp, found by matching the undefined
symbols in each object library against the ones it defines. Object
libraries do not link, so a missing dependency here is invisible at
build time: the archive reached the executable through whichever target
happened to name it.
The dependency is PUBLIC where one of the target's own headers reaches
utils/sdl2_to_1_2_backports.h and PRIVATE where only its .cpp files do.
libdevilutionx_paths already declared it and is moved to PRIVATE on the
same rule: only utils/paths.cpp includes the header, utils/paths.h does
not.
Co-Authored-By: Claude Opus 5 <[email protected]>
Split several monolithic files to reduce transitive dependencies and
enable the dun_render_benchmark to link without bringing in all of
DevilutionX:
- Extract dungeon tile globals and loading functions from `gendung` into
`dun_tile_data.{hpp,cpp}`
- Extract light table data and MakeLightTable from lighting into
`engine/light_tables.{hpp,cpp}`
- Move cursor_id and SelectionRegion to cursor_defs.hpp
- Extract quest log rendering to `panels/quest_log.{hpp,cpp}`
- Extract drlg quest setpiece logic to `levels/drlg_quests.{hpp,cpp}`
- Extract quest data loading to `tables/questdat.{hpp,cpp}`
- Move `GameData` and `gbIsMultiplayer` into `game_mode.{hpp,cpp}`
- Add `parse_sfx_id.{hpp,cpp}`
- Update CMake libraries accordingly
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- `fmt::format` with literal format strings -> `std::format`.
- `fmt::format(fmt::runtime(...))` -> `FormatRuntime` in the new
`utils/format.hpp`, a std::vformat wrapper that is also safe under the
P2905 lvalue requirement of `std::make_format_args`.
- `fmt::dynamic_format_arg_store` (Lua logging) -> a small hand-rolled
per-field formatter over `std::variant`, returning `std::expected`.
- `fmt::format_int` -> `std::to_chars`.
- `fmt::join` -> manual joins at the two call sites.
- SDL_audiolib: new patch that rewrites aulib_log.h to use std::format
and drops the fmt dependency from its CMakeLists.
- The `-static-libstdc++` guard now keys off
`DEVILUTIONX_STATIC_SDL_AUDIOLIB` instead of `DEVILUTIONX_STATIC_LIBFMT`.
- libfmt removed from CMake, vcpkg, Brewfile, CI workflows,
Dockerfiles, packaging scripts, UWP project, and docs.
- macOS bumped to 13.3+, iOS to 16.3+ (required for `<format>`)
- Amiga m68k: provide `__cmpdf2`, which is missing from the hard-float
libgcc multilib but referenced via libstdc++'s long double paths.
Co-Authored-By: Claude Fable 5 <[email protected]>
Now that the project targets C++23, use the standard library's
std::expected instead of the vendored tl::expected polyfill:
- tl::expected -> std::expected, tl::unexpected/tl::make_unexpected -> std::unexpected
- .map()/.map_error() -> .transform()/.transform_error()
- #include <expected.hpp> -> #include <expected>, grouped with std headers
- Remove 3rdParty/tl/expected.hpp (the tl target remains for function_ref.hpp)
Co-Authored-By: Claude Fable 5 <[email protected]>
The loaders returned success with a null/empty buffer when a file could
not be opened in HeadlessMode, breaking every caller that probes
has_value() to pick between asset sources. With Diablo-only data the
Hellfire->Diablo town fallbacks (LoadLvlGFX / LoadMinData /
LoadLevelSOLData) never triggered: entering town dereferenced a null
town.min with uninitialized tileCount in SetDungeonMicros, a null
pMegaTiles in T_FillSector, and silently kept the previous level's
SOLData.
Files that do exist were already loaded identically in headless and
graphical builds; the special case only affected the missing-file path.
Headless code paths are a subset of the graphical ones, so reporting
the error exactly like the graphical build restores the intended
fallback behaviour with no call-site changes.
Unifies "Show Item Graphics" and "Visual Store" boolean options
into a single enum.
Also moves floating info box option next to the store UI option
as they are somewhat related.
play_movie() only early-outs for demo playback, but SVidPlayBegin
requires the SDL video subsystem that HeadlessMode intentionally never
initializes, so any real-time (non-demo) headless session crashes on
the first movie: the Lazarus in-game movie (gendata\fprst3.smk) and
the ending cinematics when Diablo dies. Mirror the existing
demo::IsRunning() early-out; the surrounding fade/redraw paths are
already headless-safe, and this one guard covers every callsite
(intro logos, Lazarus, victory/ending, credits loop).
No behavior change in graphical builds.
Same approach as the Win9x fix.
Also enables Windows XP build to run on pull request to ensure we catch
breakages in the future.
Co-Authored-By: Claude Fable 5 <[email protected]>
MinGW GCC >= 13 (Ubuntu >= 24.04) cannot target Windows 9x: its
libgcc/libstdc++ are compiled at the default _WIN32_WINNT (Windows 10),
so the win32 thread model uses Vista-only kernel32 APIs (SRWLock,
CONDITION_VARIABLE). These end up in the import table of every
statically linked C++ binary and the Windows 9x loader refuses to start
a program with unresolved kernel32 imports. It also breaks compilation
of <mutex> at _WIN32_WINNT < 0x0600.
mingw9x-prep.sh now builds a pinned i586-w64-mingw32 toolchain
(binutils 2.46.1, GCC 16.1.0, mingw-w64 v14.0.0 with msvcrt) into
/opt/mingw9x, with the target libraries built at _WIN32_WINNT=0x0400 so
that GCC's legacy pre-Vista threading path is used. i586 rather than
i686 because Windows 98-era CPUs may lack cmov.
mingw9x-gcc.patch fixes the remaining pre-Vista issues in GCC:
- libstdc++'s <chrono> tzdb assumes Vista+ APIs whenever <windows.h>
is available; gate its Windows code on _WIN32_WINNT >= 0x0600.
- libgcc's gthread runtime statically imports TryEnterCriticalSection
and GetThreadId, which do not exist on Windows 9x; resolve them
dynamically via GetProcAddress and degrade gracefully.
The prep script ends with a smoke test: a static C++ exe including
<mutex> at _WIN32_WINNT=0 must link, contain no post-9x kernel32
imports and keep PE subsystem version 4.x.
Also:
- Build zlib from source into the prefix instead of patching the
system libz-mingw-w64-dev package.
- Drop libsodium from the 9x prep; the 9x build uses NONET=ON.
- Drop Debug-only -gstabs (STABS support was removed in GCC 13).
- Cache the toolchain in CI, keyed on the prep script and patch.
Verified: the packaged devilutionx.exe imports only
SDL.dll/KERNEL32.dll/msvcrt.dll with PE subsystem 4.0, and a per-symbol
import diff against the last known-good CI build shows only Win95-era
additions (GetCPInfo, GetTempPathA, IsDBCSLeadByte, LocalFree).
Co-Authored-By: Claude Fable 5 <[email protected]>