diff --git a/.editorconfig b/.editorconfig
index 959e97e84ea..efd434d7897 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -67,6 +67,9 @@ end_of_line = crlf
[*.tsv]
trim_trailing_whitespace = false
+[*.plist]
+end_of_line = lf
+
[AppRun]
end_of_line = lf
diff --git a/3rdParty/Lua/CMakeLists.txt b/3rdParty/Lua/CMakeLists.txt
index 2689c093c17..7dd61770735 100644
--- a/3rdParty/Lua/CMakeLists.txt
+++ b/3rdParty/Lua/CMakeLists.txt
@@ -15,7 +15,12 @@ FetchContent_Declare_ExcludeFromAll(Lua
)
FetchContent_MakeAvailable_ExcludeFromAll(Lua)
-if(ANDROID AND ("${ANDROID_ABI}" STREQUAL "armeabi-v7a" OR "${ANDROID_ABI}" STREQUAL "x86"))
+if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND DARWIN_MAJOR_VERSION VERSION_EQUAL 8)
+ # We need legacy-support from MacPorts for:
+ # localtime_r gmtime_r
+ find_package(MacportsLegacySupport REQUIRED)
+ target_link_libraries(lua_static PRIVATE MacportsLegacySupport::MacportsLegacySupport)
+elseif(ANDROID AND ("${ANDROID_ABI}" STREQUAL "armeabi-v7a" OR "${ANDROID_ABI}" STREQUAL "x86"))
target_compile_definitions(lua_internal INTERFACE -DLUA_USE_C89)
elseif(NINTENDO_3DS OR VITA OR NINTENDO_SWITCH OR NXDK)
target_compile_definitions(lua_static PUBLIC -DLUA_USE_C89)
diff --git a/3rdParty/googletest/CMakeLists.txt b/3rdParty/googletest/CMakeLists.txt
index ed3463cf2e9..26c31d28492 100644
--- a/3rdParty/googletest/CMakeLists.txt
+++ b/3rdParty/googletest/CMakeLists.txt
@@ -2,8 +2,8 @@ include(functions/FetchContent_ExcludeFromAll_backport)
FetchContent_Declare_ExcludeFromAll(
googletest
- URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz
- URL_HASH MD5=c8340a482851ef6a3fe618a082304cfc
+ URL https://github.com/google/googletest/releases/download/v1.15.2/googletest-1.15.2.tar.gz
+ URL_HASH MD5=7e11f6cfcf6498324ac82d567dcb891e
)
set(INSTALL_GTEST OFF)
diff --git a/CMake/Platforms.cmake b/CMake/Platforms.cmake
index 76cd0d98a59..0c4f352a29e 100644
--- a/CMake/Platforms.cmake
+++ b/CMake/Platforms.cmake
@@ -88,4 +88,16 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
# to detect available APIs.
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_SYSTEM_VERSION}")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\2" DARWIN_MINOR_VERSION "${CMAKE_SYSTEM_VERSION}")
+
+ if(DARWIN_MAJOR_VERSION VERSION_EQUAL 8)
+ include(platforms/macos_tiger)
+ endif()
+
+ # For older macOS, we assume MacPorts because Homebrew only supports newer version
+ if(DARWIN_MAJOR_VERSION VERSION_LESS 11)
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/platforms/macports/finders")
+
+ # On MacPorts, libfmt is in a subdirectory:
+ list(APPEND CMAKE_MODULE_PATH "/opt/local/lib/libfmt11/cmake")
+ endif()
endif()
diff --git a/CMake/platforms/macos_tiger.cmake b/CMake/platforms/macos_tiger.cmake
new file mode 100644
index 00000000000..2367ac5a57d
--- /dev/null
+++ b/CMake/platforms/macos_tiger.cmake
@@ -0,0 +1,20 @@
+# ASAN and UBSAN are not supported by macports gcc14 on PowerPC.
+set(ASAN OFF)
+set(UBSAN OFF)
+
+# SDL2 does not build for Tiger, so we use SDL1 instead.
+set(USE_SDL1 ON)
+
+# ZeroTier is yet to be tested.
+set(DISABLE_ZERO_TIER ON)
+
+# Use vendored libfmt until this issue is resolved:
+# https://trac.macports.org/ticket/71503
+set(DEVILUTIONX_SYSTEM_LIBFMT OFF)
+set(DEVILUTIONX_STATIC_LIBFMT ON)
+
+# https://trac.macports.org/ticket/71511
+set(DEVILUTIONX_SYSTEM_GOOGLETEST OFF)
+set(DEVILUTIONX_STATIC_GOOGLETEST OFF)
+set(DEVILUTIONX_SYSTEM_BENCHMARK OFF)
+set(DEVILUTIONX_STATIC_BENCHMARK OFF)
diff --git a/CMake/platforms/macports/finders/FindMacportsLegacySupport.cmake b/CMake/platforms/macports/finders/FindMacportsLegacySupport.cmake
new file mode 100644
index 00000000000..34dbc1fe2d5
--- /dev/null
+++ b/CMake/platforms/macports/finders/FindMacportsLegacySupport.cmake
@@ -0,0 +1,30 @@
+# Provides missing functions, such as localtime_r
+if(NOT TARGET MacportsLegacySupport::MacportsLegacySupport)
+ set(MacportsLegacySupport_INCLUDE_DIR /opt/local/include/LegacySupport)
+ mark_as_advanced(MacportsLegacySupport_INCLUDE_DIR)
+
+ find_library(MacportsLegacySupport_LIBRARY NAMES MacportsLegacySupport
+ PATHS /opt/local/lib)
+ mark_as_advanced(MacportsLegacySupport_LIBRARY)
+
+ include(FindPackageHandleStandardArgs)
+ FIND_PACKAGE_HANDLE_STANDARD_ARGS(
+ MacportsLegacySupport
+ DEFAULT_MSG
+ MacportsLegacySupport_LIBRARY MacportsLegacySupport_INCLUDE_DIR)
+
+ if(MacportsLegacySupport_FOUND)
+ set(MacportsLegacySupport_LIBRARIES ${MacportsLegacySupport_LIBRARY})
+ set(MacportsLegacySupport_INCLUDE_DIRS ${MacportsLegacySupport_INCLUDE_DIR})
+ add_library(MacportsLegacySupport::MacportsLegacySupport UNKNOWN IMPORTED)
+ set_target_properties(
+ MacportsLegacySupport::MacportsLegacySupport PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${MacportsLegacySupport_INCLUDE_DIR}"
+ )
+ set_target_properties(
+ MacportsLegacySupport::MacportsLegacySupport PROPERTIES
+ IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+ IMPORTED_LOCATION "${MacportsLegacySupport_LIBRARY}"
+ )
+ endif()
+endif()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 54f8cb638fd..9eb7d73fafe 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -343,7 +343,6 @@ else()
Source/main.cpp
Packaging/windows/devilutionx.exe.manifest
Packaging/windows/devilutionx.rc
- Packaging/apple/AppIcon.icns
Packaging/apple/LaunchScreen.storyboard)
if(CMAKE_STRIP AND NOT DEVILUTIONX_DISABLE_STRIP)
@@ -382,7 +381,6 @@ include(functions/set_relative_file_macro)
set_relative_file_macro(${BIN_TARGET})
if(APPLE)
- set_source_files_properties("./Packaging/apple/AppIcon.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(MACOSX_BUNDLE_GUI_IDENTIFIER com.diasurgical.devilutionx)
set(MACOSX_BUNDLE_COPYRIGHT Unlicense)
set(MACOSX_BUNDLE_BUNDLE_NAME devilutionx)
@@ -399,7 +397,15 @@ if(APPLE)
set(MACOSX_BUNDLE_REQUIRED_PLATFORM Carbon)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.12.0")
endif()
- set_target_properties(${BIN_TARGET} PROPERTIES MACOSX_BUNDLE_ICON_FILE "AppIcon.icns")
+ if(DARWIN_MAJOR_VERSION VERSION_LESS 9)
+ # Finder on OSX Tiger can only handle icns files with up to 128x128 icons.
+ set(_icon_file AppIcon_128)
+ else()
+ set(_icon_file AppIcon)
+ endif()
+ target_sources(${BIN_TARGET} PRIVATE "Packaging/apple/${_icon_file}.icns")
+ set_source_files_properties("./Packaging/apple/${_icon_file}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+ set_target_properties(${BIN_TARGET} PROPERTIES MACOSX_BUNDLE_ICON_FILE "${_icon_file}.icns")
set_target_properties(${BIN_TARGET} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Packaging/apple/Info.plist")
install (TARGETS ${BIN_TARGET} DESTINATION ./)
diff --git a/Packaging/apple/AppIcon_128.icns b/Packaging/apple/AppIcon_128.icns
new file mode 100644
index 00000000000..76108cf0a33
Binary files /dev/null and b/Packaging/apple/AppIcon_128.icns differ
diff --git a/Packaging/apple/Info.plist b/Packaging/apple/Info.plist
index 91e32a9810c..52754cc115c 100644
--- a/Packaging/apple/Info.plist
+++ b/Packaging/apple/Info.plist
@@ -39,7 +39,7 @@
NSHumanReadableCopyright
${MACOSX_BUNDLE_COPYRIGHT}
SDL_FILESYSTEM_BASE_DIR_TYPE
- parent
+ resource
NSSupportsAutomaticGraphicsSwitching
UIApplicationSupportsIndirectInputEvents
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index f6276943606..07a4dde8cd1 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -335,6 +335,13 @@ if(USE_SDL1)
target_link_libraries(DevilutionX::SDL INTERFACE
libdevilutionx_sdl2_to_1_2_backports
)
+ if(APPLE)
+ enable_language(OBJC)
+ target_sources(libdevilutionx_sdl2_to_1_2_backports PRIVATE
+ platform/macos_sdl1/SDL_filesystem.m)
+ target_link_libraries(libdevilutionx_sdl2_to_1_2_backports PRIVATE
+ "-framework Foundation")
+ endif()
endif()
add_devilutionx_object_library(libdevilutionx_codec
diff --git a/Source/init.cpp b/Source/init.cpp
index 1e6488d58f7..02eafbc9d5c 100644
--- a/Source/init.cpp
+++ b/Source/init.cpp
@@ -130,7 +130,7 @@ std::vector GetMPQSearchPaths()
if (paths[0] == paths[1] || (paths.size() == 3 && (paths[0] == paths[2] || paths[1] == paths[2])))
paths.pop_back();
-#if defined(__unix__) && !defined(__ANDROID__)
+#if (defined(__unix__) || defined(__APPLE__)) && !defined(__ANDROID__)
// `XDG_DATA_HOME` is usually the root path of `paths::PrefPath()`, so we only
// add `XDG_DATA_DIRS`.
const char *xdgDataDirs = std::getenv("XDG_DATA_DIRS");
diff --git a/Source/interfac.cpp b/Source/interfac.cpp
index 33777b571fb..94144901f09 100644
--- a/Source/interfac.cpp
+++ b/Source/interfac.cpp
@@ -37,6 +37,23 @@ namespace devilution {
namespace {
+#if defined(__APPLE__) && defined(USE_SDL1)
+// On Tiger PPC, SDL_PushEvent from a background thread appears to do nothing.
+#define SDL_PUSH_EVENT_BG_THREAD_WORKS 0
+#else
+#define SDL_PUSH_EVENT_BG_THREAD_WORKS 1
+#endif
+
+#if !SDL_PUSH_EVENT_BG_THREAD_WORKS
+// This workaround is not completely thread-safe but the worst
+// that can happen is we miss some WM_PROGRESS events,
+// which is not a problem.
+struct {
+ std::atomic type;
+ std::string error;
+} NextCustomEvent;
+#endif
+
constexpr uint32_t MaxProgress = 534;
constexpr uint32_t ProgressStepSize = 23;
@@ -391,16 +408,31 @@ void DoLoad(interface_mode uMsg)
}
if (!loadResult.has_value()) {
+#if SDL_PUSH_EVENT_BG_THREAD_WORKS
SDL_Event event;
event.type = CustomEventToSdlEvent(WM_ERROR);
event.user.data1 = new std::string(std::move(loadResult).error());
- SDL_PushEvent(&event);
+ if (SDL_PushEvent(&event) < 0) {
+ LogError("Failed to send WM_ERROR {}", SDL_GetError());
+ SDL_ClearError();
+ }
+#else
+ NextCustomEvent.error = std::move(loadResult).error();
+ NextCustomEvent.type = static_cast(WM_ERROR);
+#endif
return;
}
+#if SDL_PUSH_EVENT_BG_THREAD_WORKS
SDL_Event event;
event.type = CustomEventToSdlEvent(WM_DONE);
- SDL_PushEvent(&event);
+ if (SDL_PushEvent(&event) < 0) {
+ LogError("Failed to send WM_DONE {}", SDL_GetError());
+ SDL_ClearError();
+ }
+#else
+ NextCustomEvent.type = static_cast(WM_DONE);
+#endif
}
struct {
@@ -561,9 +593,16 @@ void IncProgress(uint32_t steps)
if (sgdwProgress > MaxProgress)
sgdwProgress = MaxProgress;
if (!HeadlessMode && sgdwProgress != prevProgress) {
+#if SDL_PUSH_EVENT_BG_THREAD_WORKS
SDL_Event event;
event.type = CustomEventToSdlEvent(WM_PROGRESS);
- SDL_PushEvent(&event);
+ if (SDL_PushEvent(&event) < 0) {
+ LogError("Failed to send WM_PROGRESS {}", SDL_GetError());
+ SDL_ClearError();
+ }
+#else
+ NextCustomEvent.type = static_cast(WM_PROGRESS);
+#endif
}
}
@@ -589,6 +628,10 @@ void ShowProgress(interface_mode uMsg)
ProgressEventHandlerState.done = false;
ProgressEventHandlerState.drawnProgress = 0;
+#if !SDL_PUSH_EVENT_BG_THREAD_WORKS
+ NextCustomEvent.type = -1;
+#endif
+
#ifndef USE_SDL1
DeactivateVirtualGamepad();
FreeVirtualGamepadTextures();
@@ -623,21 +666,35 @@ void ShowProgress(interface_mode uMsg)
LogVerbose("Load thread finished in {}ms", SDL_GetTicks() - start);
});
+ const auto processEvent = [&](const SDL_Event &event) {
+ CheckShouldSkipRendering();
+ if (event.type != SDL_QUIT) {
+ HandleMessage(event, SDL_GetModState());
+ }
+ if (ProgressEventHandlerState.done) {
+ loadThread.join();
+ return false;
+ }
+ return true;
+ };
+
while (true) {
CheckShouldSkipRendering();
SDL_Event event;
// We use the real `SDL_PollEvent` here instead of `FetchEvent`
// to process real events rather than the recorded ones in demo mode.
while (SDL_PollEvent(&event)) {
- CheckShouldSkipRendering();
- if (event.type != SDL_QUIT) {
- HandleMessage(event, SDL_GetModState());
- }
- if (ProgressEventHandlerState.done) {
- loadThread.join();
- return;
+ if (!processEvent(event)) return;
+ }
+#if !SDL_PUSH_EVENT_BG_THREAD_WORKS
+ if (const int customEventType = NextCustomEvent.type.exchange(-1); customEventType != -1) {
+ event.type = CustomEventToSdlEvent(static_cast(customEventType));
+ if (static_cast(customEventType) == static_cast(WM_ERROR)) {
+ event.user.data1 = &NextCustomEvent.error;
}
+ if (!processEvent(event)) return;
}
+#endif
}
}
diff --git a/Source/platform/macos_sdl1/SDL_filesystem.m b/Source/platform/macos_sdl1/SDL_filesystem.m
new file mode 100644
index 00000000000..1bf4ed6e93d
--- /dev/null
+++ b/Source/platform/macos_sdl1/SDL_filesystem.m
@@ -0,0 +1,138 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2024 Sam Lantinga
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ An altered version based on:
+ https://github.com/libsdl-org/SDL/blob/3c142abcb2b0b0ad7e08b096ea8d9a1a1e1af1ef/src/filesystem/cocoa/SDL_sysfilesystem.m
+
+ Modifications:
+ 1. Changes to compile with gcc (@autoreleasepool -> NSAutoreleasePool)
+ 2. Targets SDL-1.2 rather than SDL2 (SDL_InvalidParamError -> SDL_SetError)
+*/
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/* System dependent filesystem routines */
+
+#include
+#include
+#include
+
+#include
+
+char *SDL_GetBasePath(void)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ NSBundle *bundle = [NSBundle mainBundle];
+ const char *baseType = [[[bundle infoDictionary] objectForKey:@"SDL_FILESYSTEM_BASE_DIR_TYPE"] UTF8String];
+ const char *base = NULL;
+ char *retval = NULL;
+
+ if (baseType == NULL) {
+ baseType = "resource";
+ }
+ if (SDL_strcasecmp(baseType, "bundle") == 0) {
+ base = [[bundle bundlePath] fileSystemRepresentation];
+ } else if (SDL_strcasecmp(baseType, "parent") == 0) {
+ base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation];
+ } else {
+ /* this returns the exedir for non-bundled and the resourceDir for bundled apps */
+ base = [[bundle resourcePath] fileSystemRepresentation];
+ }
+
+ if (base) {
+ const size_t len = SDL_strlen(base) + 2;
+ retval = (char *)SDL_malloc(len);
+ if (retval == NULL) {
+ SDL_OutOfMemory();
+ } else {
+ SDL_snprintf(retval, len, "%s/", base);
+ }
+ }
+
+ [pool drain];
+ return retval;
+}
+
+char *SDL_GetPrefPath(const char *org, const char *app)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ char *retval = NULL;
+ NSArray *array;
+
+ if (!app) {
+ SDL_SetError("SDL_GetPrefPath: app argument cannot be null");
+ return NULL;
+ }
+ if (!org) {
+ org = "";
+ }
+
+#if !TARGET_OS_TV
+ array = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
+#else
+ /* tvOS does not have persistent local storage!
+ * The only place on-device where we can store data is
+ * a cache directory that the OS can empty at any time.
+ *
+ * It's therefore very likely that save data will be erased
+ * between sessions. If you want your app's save data to
+ * actually stick around, you'll need to use iCloud storage.
+ */
+ {
+ static SDL_bool shown = SDL_FALSE;
+ if (!shown) {
+ shown = SDL_TRUE;
+ SDL_LogCritical(SDL_LOG_CATEGORY_SYSTEM, "tvOS does not have persistent local storage! Use iCloud storage if you want your data to persist between sessions.\n");
+ }
+ }
+
+ array = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+#endif /* !TARGET_OS_TV */
+
+ if ([array count] > 0) { /* we only want the first item in the list. */
+ NSString *str = [array objectAtIndex:0];
+ const char *base = [str fileSystemRepresentation];
+ if (base) {
+ const size_t len = SDL_strlen(base) + SDL_strlen(org) + SDL_strlen(app) + 4;
+ retval = (char *)SDL_malloc(len);
+ if (retval == NULL) {
+ SDL_OutOfMemory();
+ } else {
+ char *ptr;
+ if (*org) {
+ SDL_snprintf(retval, len, "%s/%s/%s/", base, org, app);
+ } else {
+ SDL_snprintf(retval, len, "%s/%s/", base, app);
+ }
+ for (ptr = retval + 1; *ptr; ptr++) {
+ if (*ptr == '/') {
+ *ptr = '\0';
+ mkdir(retval, 0700);
+ *ptr = '/';
+ }
+ }
+ mkdir(retval, 0700);
+ }
+ }
+ }
+
+ [pool drain];
+ return retval;
+}
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/Source/utils/paths.cpp b/Source/utils/paths.cpp
index a5bd0db8560..d0f680555b8 100644
--- a/Source/utils/paths.cpp
+++ b/Source/utils/paths.cpp
@@ -1,5 +1,9 @@
#include "utils/paths.h"
+#include
+#include
+#include
+
#include
#include "appfat.h"
@@ -130,6 +134,23 @@ const std::string &AssetsPath()
assetsPath.emplace("D:\\assets\\");
#elif defined(__3DS__) || defined(__SWITCH__)
assetsPath.emplace("romfs:/");
+#elif defined(__APPLE__) && defined(USE_SDL1)
+ // In `Info.plist` we have
+ //
+ // SDL_FILESYSTEM_BASE_DIR_TYPE
+ // resource
+ //
+ // This means `SDL_GetBasePath()` returns exedir for non-bundled
+ // and the `app_dir.app/Resources/` for bundles.
+ //
+ // Our built-in resources are directly in the `devilutionx.app/Resources` directory
+ // and are normally looked up via a relative lookup in `FindAsset`.
+ // In SDL2, this is implemented by calling `SDL_OpenFPFromBundleOrFallback`
+ // from `SDL_RWFromFile` but SDL1 doesn't do it, so we set the directory explicitly.
+ //
+ // 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()));
#else
assetsPath.emplace(FromSDL(SDL_GetBasePath()) + ("assets" DIRECTORY_SEPARATOR_STR));
#endif
diff --git a/Source/utils/paths.h b/Source/utils/paths.h
index ca4cc36003b..35edfec3052 100644
--- a/Source/utils/paths.h
+++ b/Source/utils/paths.h
@@ -1,6 +1,5 @@
#pragma once
-#include
#include
namespace devilution {
diff --git a/Source/utils/sdl2_to_1_2_backports.cpp b/Source/utils/sdl2_to_1_2_backports.cpp
index ee47b5d7260..f8de050bebd 100644
--- a/Source/utils/sdl2_to_1_2_backports.cpp
+++ b/Source/utils/sdl2_to_1_2_backports.cpp
@@ -544,7 +544,7 @@ int WIN_SetError(const char *prefix)
} // namespace
-char *SDL_GetBasePath(void)
+extern "C" char *SDL_GetBasePath(void)
{
// From sdl2-2.0.9/src/filesystem/windows/SDL_sysfilesystem.c
@@ -611,7 +611,7 @@ char *SDL_GetBasePath(void)
return retval;
}
-char *SDL_GetPrefPath(const char *org, const char *app)
+extern "C" char *SDL_GetPrefPath(const char *org, const char *app)
{
// From sdl2-2.0.9/src/filesystem/windows/SDL_sysfilesystem.c
@@ -699,7 +699,8 @@ char *SDL_GetPrefPath(const char *org, const char *app)
return retval;
}
-#else
+// For Apple, definitions are in Source/platform/macos_sdl1/SDL_filesystem.m
+#elif !defined(__APPLE__)
namespace {
#if !defined(__QNXNTO__) && !defined(__amigaos__) && !(defined(WINVER) && WINVER <= 0x0500 && (!defined(_WIN32_WINNT) || _WIN32_WINNT == 0))
@@ -736,7 +737,7 @@ char *readSymLink(const char *path)
#endif
} // namespace
-char *SDL_GetBasePath()
+extern "C" char *SDL_GetBasePath()
{
// From sdl2-2.0.9/src/filesystem/unix/SDL_sysfilesystem.c
@@ -851,7 +852,7 @@ char *SDL_GetBasePath()
return retval;
}
-char *SDL_GetPrefPath(const char *org, const char *app)
+extern "C" char *SDL_GetPrefPath(const char *org, const char *app)
{
// From sdl2-2.0.9/src/filesystem/unix/SDL_sysfilesystem.c
/*
diff --git a/Source/utils/sdl2_to_1_2_backports.h b/Source/utils/sdl2_to_1_2_backports.h
index a1b9e31d574..e089a5101d9 100644
--- a/Source/utils/sdl2_to_1_2_backports.h
+++ b/Source/utils/sdl2_to_1_2_backports.h
@@ -322,5 +322,5 @@ int SDL_BlitScaled(SDL_Surface *src, SDL_Rect *srcrect,
Sint64 SDL_RWsize(SDL_RWops *context);
-char *SDL_GetBasePath();
-char *SDL_GetPrefPath(const char *org, const char *app);
+extern "C" char *SDL_GetBasePath();
+extern "C" char *SDL_GetPrefPath(const char *org, const char *app);
diff --git a/docs/building.md b/docs/building.md
index 7904f5a9bd8..e87f099002b 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -528,6 +528,49 @@ Executing `Packaging/miyoo_mini/build.sh` will create the folder `build-miyoo-mi
OnionOS Port Collection.
+macOS 10.4 Tiger
+
+For macOS Tiger, DevilutionX can be compiled using the compiler and libraries from [MacPorts](https://www.macports.org/).
+
+For PowerPC, you can use precompiled dependencies from here:
+
+http://macports-tiger-ppc.glebm.com/
+
+After installing MacPorts, run:
+
+~~~ bash
+# Some packages may require you to manually deactivate certain ports during installation.
+# Remember to reactivate them after installing.
+sudo port install curl curl-ca-bundle gcc14 cmake \
+ libsdl12 libsdl_image libsodium bzip2 zlib lua54
+
+# Set GCC 14 as the default GCC:
+sudo port select --set gcc mp-gcc14
+~~~
+
+
+
+Then, build DevilutionX:
+
+~~~ bash
+CC=gcc cmake -S. -Bbuild-rel -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF -DCPACK=ON -DMACOSX_STANDALONE_APP_BUNDLE=ON
+cmake --build build-rel -j "$(sysctl -n hw.ncpu)"
+
+# `sudo` is required to produce a bundle with all the shared libraries.
+sudo cmake --build build-rel --target package -j "$(sysctl -n hw.ncpu)"
+~~~
+
+To run tools from the `tools/` directory (only needed for development), you also need Python:
+
+~~~ bash
+sudo port install python312
+sudo port select --set python python312
+sudo port select --set python3 python312
+~~~
+
+
+
CMake build options
### General
diff --git a/test/items_test.cpp b/test/items_test.cpp
index c829520b4c1..0a3caa2bc47 100644
--- a/test/items_test.cpp
+++ b/test/items_test.cpp
@@ -1,6 +1,8 @@
-#include
+#include
#include
+#include
+
#include "engine/random.hpp"
#include "items.h"
#include "player.h"
diff --git a/test/timedemo_test.cpp b/test/timedemo_test.cpp
index 79eb5d334e8..afb60a00a46 100644
--- a/test/timedemo_test.cpp
+++ b/test/timedemo_test.cpp
@@ -23,7 +23,14 @@ bool Dummy_GetHeroInfo(_uiheroinfo *pInfo)
void RunTimedemo(std::string timedemoFolderName)
{
- if (SDL_Init(SDL_INIT_EVENTS) <= -1) {
+ if (SDL_Init(
+#ifdef USE_SDL1
+ 0
+#else
+ SDL_INIT_EVENTS
+#endif
+ )
+ <= -1) {
ErrSdl();
}
std::string unitTestFolderCompletePath = paths::BasePath() + "/test/fixtures/timedemo/" + timedemoFolderName;