From 4cf6cccbaf3ca80dcc85fe1b639bae4764b3d9de Mon Sep 17 00:00:00 2001 From: Rafael Kitover Date: Thu, 24 Apr 2025 16:11:58 +0000 Subject: [PATCH] build: bundle SFML 3.x sys/network in third_party Remove the SFML external dependency, include the SFML 3.0.1 system and network libraries in third_party and adjust the build code, tools and documentation accordingly. Signed-off-by: Rafael Kitover --- CMakeLists.txt | 1 - README.md | 4 +- cmake/Dependencies.cmake | 6 + cmake/FindSFML.cmake | 365 -------- cmake/Options.cmake | 11 +- installdeps | 45 +- snapcraft.yaml | 3 - src/core/gba/gbaLink.cpp | 2 +- src/core/gba/internal/gbaSockClient.h | 2 +- third_party/sfml/include/SFML/Config.hpp | 164 ++++ third_party/sfml/include/SFML/Network.hpp | 51 ++ .../sfml/include/SFML/Network/Export.hpp | 44 + third_party/sfml/include/SFML/Network/Ftp.hpp | 629 +++++++++++++ .../sfml/include/SFML/Network/Http.hpp | 480 ++++++++++ .../sfml/include/SFML/Network/IpAddress.hpp | 297 +++++++ .../sfml/include/SFML/Network/Packet.hpp | 552 ++++++++++++ .../sfml/include/SFML/Network/Socket.hpp | 229 +++++ .../include/SFML/Network/SocketHandle.hpp | 52 ++ .../include/SFML/Network/SocketSelector.hpp | 273 ++++++ .../sfml/include/SFML/Network/TcpListener.hpp | 166 ++++ .../sfml/include/SFML/Network/TcpSocket.hpp | 317 +++++++ .../sfml/include/SFML/Network/UdpSocket.hpp | 293 +++++++ third_party/sfml/include/SFML/System.hpp | 55 ++ .../sfml/include/SFML/System/Angle.hpp | 511 +++++++++++ .../sfml/include/SFML/System/Angle.inl | 274 ++++++ .../sfml/include/SFML/System/Clock.hpp | 196 +++++ third_party/sfml/include/SFML/System/Err.hpp | 77 ++ .../sfml/include/SFML/System/Exception.hpp | 46 + .../sfml/include/SFML/System/Export.hpp | 44 + .../include/SFML/System/FileInputStream.hpp | 212 +++++ .../sfml/include/SFML/System/InputStream.hpp | 166 ++++ .../include/SFML/System/MemoryInputStream.hpp | 139 +++ .../include/SFML/System/NativeActivity.hpp | 58 ++ .../sfml/include/SFML/System/Sleep.hpp | 57 ++ .../sfml/include/SFML/System/String.hpp | 700 +++++++++++++++ .../sfml/include/SFML/System/String.inl | 64 ++ .../include/SFML/System/SuspendAwareClock.hpp | 76 ++ third_party/sfml/include/SFML/System/Time.hpp | 500 +++++++++++ third_party/sfml/include/SFML/System/Time.inl | 284 ++++++ third_party/sfml/include/SFML/System/Utf.hpp | 758 ++++++++++++++++ third_party/sfml/include/SFML/System/Utf.inl | 829 ++++++++++++++++++ .../sfml/include/SFML/System/Vector2.hpp | 435 +++++++++ .../sfml/include/SFML/System/Vector2.inl | 217 +++++ .../sfml/include/SFML/System/Vector3.hpp | 356 ++++++++ .../sfml/include/SFML/System/Vector3.inl | 214 +++++ third_party/sfml/license.md | 9 + .../sfml/src/SFML/Network/CMakeLists.txt | 59 ++ third_party/sfml/src/SFML/Network/Ftp.cpp | 643 ++++++++++++++ third_party/sfml/src/SFML/Network/Http.cpp | 399 +++++++++ .../sfml/src/SFML/Network/IpAddress.cpp | 253 ++++++ third_party/sfml/src/SFML/Network/Packet.cpp | 596 +++++++++++++ third_party/sfml/src/SFML/Network/Socket.cpp | 175 ++++ .../sfml/src/SFML/Network/SocketImpl.hpp | 123 +++ .../sfml/src/SFML/Network/SocketSelector.cpp | 209 +++++ .../sfml/src/SFML/Network/TcpListener.cpp | 132 +++ .../sfml/src/SFML/Network/TcpSocket.cpp | 429 +++++++++ .../sfml/src/SFML/Network/UdpSocket.cpp | 223 +++++ .../sfml/src/SFML/Network/Unix/SocketImpl.cpp | 111 +++ .../src/SFML/Network/Win32/SocketImpl.cpp | 110 +++ .../sfml/src/SFML/System/Android/Activity.cpp | 74 ++ .../sfml/src/SFML/System/Android/Activity.hpp | 103 +++ .../SFML/System/Android/NativeActivity.cpp | 39 + .../SFML/System/Android/ResourceStream.cpp | 91 ++ .../SFML/System/Android/ResourceStream.hpp | 119 +++ .../SFML/System/Android/SuspendAwareClock.cpp | 46 + .../sfml/src/SFML/System/CMakeLists.txt | 96 ++ third_party/sfml/src/SFML/System/Clock.cpp | 88 ++ .../src/SFML/System/Dependencies.cmake.in | 13 + .../sfml/src/SFML/System/EnumArray.hpp | 75 ++ third_party/sfml/src/SFML/System/Err.cpp | 109 +++ .../sfml/src/SFML/System/FileInputStream.cpp | 167 ++++ .../src/SFML/System/MemoryInputStream.cpp | 92 ++ third_party/sfml/src/SFML/System/Sleep.cpp | 50 ++ third_party/sfml/src/SFML/System/String.cpp | 477 ++++++++++ .../sfml/src/SFML/System/Unix/SleepImpl.cpp | 57 ++ .../sfml/src/SFML/System/Unix/SleepImpl.hpp | 48 + third_party/sfml/src/SFML/System/Utils.cpp | 64 ++ third_party/sfml/src/SFML/System/Utils.hpp | 58 ++ third_party/sfml/src/SFML/System/Vector2.cpp | 124 +++ third_party/sfml/src/SFML/System/Vector3.cpp | 65 ++ .../sfml/src/SFML/System/Win32/SleepImpl.cpp | 57 ++ .../sfml/src/SFML/System/Win32/SleepImpl.hpp | 48 + .../src/SFML/System/Win32/WindowsHeader.hpp | 55 ++ tools/Windows/msys2-builder | 1 - tools/builder/MINGW.sh | 2 +- tools/builder/UNIX.sh | 8 +- tools/builder/core.sh | 3 - tools/macOS/builder | 11 - 88 files changed, 15529 insertions(+), 436 deletions(-) delete mode 100644 cmake/FindSFML.cmake create mode 100644 third_party/sfml/include/SFML/Config.hpp create mode 100644 third_party/sfml/include/SFML/Network.hpp create mode 100644 third_party/sfml/include/SFML/Network/Export.hpp create mode 100644 third_party/sfml/include/SFML/Network/Ftp.hpp create mode 100644 third_party/sfml/include/SFML/Network/Http.hpp create mode 100644 third_party/sfml/include/SFML/Network/IpAddress.hpp create mode 100644 third_party/sfml/include/SFML/Network/Packet.hpp create mode 100644 third_party/sfml/include/SFML/Network/Socket.hpp create mode 100644 third_party/sfml/include/SFML/Network/SocketHandle.hpp create mode 100644 third_party/sfml/include/SFML/Network/SocketSelector.hpp create mode 100644 third_party/sfml/include/SFML/Network/TcpListener.hpp create mode 100644 third_party/sfml/include/SFML/Network/TcpSocket.hpp create mode 100644 third_party/sfml/include/SFML/Network/UdpSocket.hpp create mode 100644 third_party/sfml/include/SFML/System.hpp create mode 100644 third_party/sfml/include/SFML/System/Angle.hpp create mode 100644 third_party/sfml/include/SFML/System/Angle.inl create mode 100644 third_party/sfml/include/SFML/System/Clock.hpp create mode 100644 third_party/sfml/include/SFML/System/Err.hpp create mode 100644 third_party/sfml/include/SFML/System/Exception.hpp create mode 100644 third_party/sfml/include/SFML/System/Export.hpp create mode 100644 third_party/sfml/include/SFML/System/FileInputStream.hpp create mode 100644 third_party/sfml/include/SFML/System/InputStream.hpp create mode 100644 third_party/sfml/include/SFML/System/MemoryInputStream.hpp create mode 100644 third_party/sfml/include/SFML/System/NativeActivity.hpp create mode 100644 third_party/sfml/include/SFML/System/Sleep.hpp create mode 100644 third_party/sfml/include/SFML/System/String.hpp create mode 100644 third_party/sfml/include/SFML/System/String.inl create mode 100644 third_party/sfml/include/SFML/System/SuspendAwareClock.hpp create mode 100644 third_party/sfml/include/SFML/System/Time.hpp create mode 100644 third_party/sfml/include/SFML/System/Time.inl create mode 100644 third_party/sfml/include/SFML/System/Utf.hpp create mode 100644 third_party/sfml/include/SFML/System/Utf.inl create mode 100644 third_party/sfml/include/SFML/System/Vector2.hpp create mode 100644 third_party/sfml/include/SFML/System/Vector2.inl create mode 100644 third_party/sfml/include/SFML/System/Vector3.hpp create mode 100644 third_party/sfml/include/SFML/System/Vector3.inl create mode 100644 third_party/sfml/license.md create mode 100644 third_party/sfml/src/SFML/Network/CMakeLists.txt create mode 100644 third_party/sfml/src/SFML/Network/Ftp.cpp create mode 100644 third_party/sfml/src/SFML/Network/Http.cpp create mode 100644 third_party/sfml/src/SFML/Network/IpAddress.cpp create mode 100644 third_party/sfml/src/SFML/Network/Packet.cpp create mode 100644 third_party/sfml/src/SFML/Network/Socket.cpp create mode 100644 third_party/sfml/src/SFML/Network/SocketImpl.hpp create mode 100644 third_party/sfml/src/SFML/Network/SocketSelector.cpp create mode 100644 third_party/sfml/src/SFML/Network/TcpListener.cpp create mode 100644 third_party/sfml/src/SFML/Network/TcpSocket.cpp create mode 100644 third_party/sfml/src/SFML/Network/UdpSocket.cpp create mode 100644 third_party/sfml/src/SFML/Network/Unix/SocketImpl.cpp create mode 100644 third_party/sfml/src/SFML/Network/Win32/SocketImpl.cpp create mode 100644 third_party/sfml/src/SFML/System/Android/Activity.cpp create mode 100644 third_party/sfml/src/SFML/System/Android/Activity.hpp create mode 100644 third_party/sfml/src/SFML/System/Android/NativeActivity.cpp create mode 100644 third_party/sfml/src/SFML/System/Android/ResourceStream.cpp create mode 100644 third_party/sfml/src/SFML/System/Android/ResourceStream.hpp create mode 100644 third_party/sfml/src/SFML/System/Android/SuspendAwareClock.cpp create mode 100644 third_party/sfml/src/SFML/System/CMakeLists.txt create mode 100644 third_party/sfml/src/SFML/System/Clock.cpp create mode 100644 third_party/sfml/src/SFML/System/Dependencies.cmake.in create mode 100644 third_party/sfml/src/SFML/System/EnumArray.hpp create mode 100644 third_party/sfml/src/SFML/System/Err.cpp create mode 100644 third_party/sfml/src/SFML/System/FileInputStream.cpp create mode 100644 third_party/sfml/src/SFML/System/MemoryInputStream.cpp create mode 100644 third_party/sfml/src/SFML/System/Sleep.cpp create mode 100644 third_party/sfml/src/SFML/System/String.cpp create mode 100644 third_party/sfml/src/SFML/System/Unix/SleepImpl.cpp create mode 100644 third_party/sfml/src/SFML/System/Unix/SleepImpl.hpp create mode 100644 third_party/sfml/src/SFML/System/Utils.cpp create mode 100644 third_party/sfml/src/SFML/System/Utils.hpp create mode 100644 third_party/sfml/src/SFML/System/Vector2.cpp create mode 100644 third_party/sfml/src/SFML/System/Vector3.cpp create mode 100644 third_party/sfml/src/SFML/System/Win32/SleepImpl.cpp create mode 100644 third_party/sfml/src/SFML/System/Win32/SleepImpl.hpp create mode 100644 third_party/sfml/src/SFML/System/Win32/WindowsHeader.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2df6d218..6337498d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,6 @@ endif() set(VCPKG_DEPS pkgconf zlib pthreads "sdl2[samplerate]" gettext wxwidgets) set(VCPKG_DEPS_OPTIONAL - sfml ENABLE_LINK ffmpeg ENABLE_FFMPEG faudio ENABLE_FAUDIO ) diff --git a/README.md b/README.md index bf98f02f..b5fa70d2 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,6 @@ And the following development libraries: - [ffmpeg](https://ffmpeg.org/) (optional, at least version `4.0.4`, for game recording) - [gettext](https://www.gnu.org/software/gettext/) and gettext-tools - [SDL2](https://www.libsdl.org/) (required) -- [SFML](https://www.sfml-dev.org/) (optional, for link) - [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface) - [wxWidgets](https://wxwidgets.org/) (required for GUI, 2.8 and non-stl builds are no longer supported) @@ -211,7 +210,7 @@ cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LINK=NO -G Ninja | `ENABLE_ASM` | Enable the following two ASM options | ON for 32 bit builds | | `ENABLE_ASM_SCALERS` | Enable x86 ASM graphic filters | ON for 32 bit builds | | `ENABLE_MMX` | Enable MMX | ON for 32 bit builds | -| `ENABLE_LINK` | Enable GBA linking functionality (requires SFML) | AUTO | +| `ENABLE_LINK` | Enable GBA linking functionality | AUTO | | `ENABLE_LIRC` | Enable LIRC support | OFF | | `ENABLE_FFMPEG` | Enable ffmpeg A/V recording | AUTO | | `ENABLE_ONLINEUPDATES` | Enable online update checks | ON | @@ -224,7 +223,6 @@ cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LINK=NO -G Ninja | `BUILD_TESTING` | Build the tests and enable ctest support. | ON | | `VBAM_STATIC` | Try link all libs statically (the following are set to ON if ON) | OFF | | `SDL2_STATIC` | Try to link static SDL2 libraries | OFF | -| `SFML_STATIC_LIBRARIES` | Try to link static SFML libraries | OFF | | `FFMPEG_STATIC` | Try to link static ffmpeg libraries | OFF | | `OPENAL_STATIC` | Try to link static OpenAL libraries | OFF | | `TRANSLATIONS_ONLY` | Build only the translations.zip and nothing else | OFF | diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 4934f60b..b8af9341 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -120,3 +120,9 @@ if(ENABLE_LINK OR ENABLE_WX) endif() endif() +if(ENABLE_LINK) + add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/sfml/src/SFML/System EXCLUDE_FROM_ALL) + add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/sfml/src/SFML/Network EXCLUDE_FROM_ALL) + set(SFML_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/sfml/include) + set(SFML_LIBRARIES sfml-system sfml-network) +endif() diff --git a/cmake/FindSFML.cmake b/cmake/FindSFML.cmake deleted file mode 100644 index c8ee5295..00000000 --- a/cmake/FindSFML.cmake +++ /dev/null @@ -1,365 +0,0 @@ -# This script locates the SFML library -# ------------------------------------ -# -# Usage -# ----- -# -# When you try to locate the SFML libraries, you must specify which modules you want to use (system, window, graphics, network, audio, main). -# If none is given, the SFML_LIBRARIES variable will be empty and you'll end up linking to nothing. -# example: -# find_package(SFML COMPONENTS graphics window system) # find the graphics, window and system modules -# -# You can enforce a specific version, either MAJOR.MINOR or only MAJOR. -# If nothing is specified, the version won't be checked (i.e. any version will be accepted). -# example: -# find_package(SFML COMPONENTS ...) # no specific version required -# find_package(SFML 2 COMPONENTS ...) # any 2.x version -# find_package(SFML 2.4 COMPONENTS ...) # version 2.4 or greater -# -# By default, the dynamic libraries of SFML will be found. To find the static ones instead, -# you must set the SFML_STATIC_LIBRARIES variable to TRUE before calling find_package(SFML ...). -# Since you have to link yourself all the SFML dependencies when you link it statically, the following -# additional variables are defined: SFML_XXX_DEPENDENCIES and SFML_DEPENDENCIES (see their detailed -# description below). -# In case of static linking, the SFML_STATIC macro will also be defined by this script. -# example: -# set(SFML_STATIC_LIBRARIES TRUE) -# find_package(SFML 2 COMPONENTS network system) -# -# On Mac OS X if SFML_STATIC_LIBRARIES is not set to TRUE then by default CMake will search for frameworks unless -# CMAKE_FIND_FRAMEWORK is set to "NEVER" for example. Please refer to CMake documentation for more details. -# Moreover, keep in mind that SFML frameworks are only available as release libraries unlike dylibs which -# are available for both release and debug modes. -# -# If SFML is not installed in a standard path, you can use the SFML_ROOT CMake (or environment) variable -# to tell CMake where SFML is. -# -# Output -# ------ -# -# This script defines the following variables: -# - For each specified module XXX (system, window, graphics, network, audio, main): -# - SFML_XXX_LIBRARY_DEBUG: the name of the debug library of the xxx module (set to SFML_XXX_LIBRARY_RELEASE is no debug version is found) -# - SFML_XXX_LIBRARY_RELEASE: the name of the release library of the xxx module (set to SFML_XXX_LIBRARY_DEBUG is no release version is found) -# - SFML_XXX_LIBRARY: the name of the library to link to for the xxx module (includes both debug and optimized names if necessary) -# - SFML_XXX_FOUND: true if either the debug or release library of the xxx module is found -# - SFML_XXX_DEPENDENCIES: the list of libraries the module depends on, in case of static linking -# - SFML_LIBRARIES: the list of all libraries corresponding to the required modules -# - SFML_FOUND: true if all the required modules are found -# - SFML_INCLUDE_DIR: the path where SFML headers are located (the directory containing the SFML/Config.hpp file) -# - SFML_DEPENDENCIES: the list of libraries SFML depends on, in case of static linking -# -# example: -# find_package(SFML 2 COMPONENTS system window graphics audio REQUIRED) -# include_directories(${SFML_INCLUDE_DIR}) -# add_executable(myapp ...) -# target_link_libraries(myapp ${SFML_LIBRARIES}) - -# define the SFML_STATIC macro if static build was chosen -if(SFML_STATIC_LIBRARIES) - add_compile_definitions(SFML_STATIC) -endif() - -# define the list of search paths for headers and libraries -set(FIND_SFML_PATHS - ${SFML_ROOT} - $ENV{SFML_ROOT} - ~/Library/Frameworks - /Library/Frameworks - /usr/local - /usr - /sw - /opt/local - /opt/csw - /opt) - -# find the SFML include directory -find_path(SFML_INCLUDE_DIR SFML/Config.hpp - PATH_SUFFIXES include - PATHS ${FIND_SFML_PATHS}) - -# check the version number -set(SFML_VERSION_OK TRUE) -if(SFML_FIND_VERSION AND SFML_INCLUDE_DIR) - # extract the major and minor version numbers from SFML/Config.hpp - # we have to handle framework a little bit differently: - if("${SFML_INCLUDE_DIR}" MATCHES "SFML.framework") - set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/Headers/Config.hpp") - else() - set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/SFML/Config.hpp") - endif() - FILE(READ "${SFML_CONFIG_HPP_INPUT}" SFML_CONFIG_HPP_CONTENTS) - STRING(REGEX REPLACE ".*#define *SFML_VERSION_MAJOR *([0-9]+).*" "\\1" SFML_VERSION_MAJOR "${SFML_CONFIG_HPP_CONTENTS}") - STRING(REGEX REPLACE ".*#define *SFML_VERSION_MINOR *([0-9]+).*" "\\1" SFML_VERSION_MINOR "${SFML_CONFIG_HPP_CONTENTS}") - STRING(REGEX REPLACE ".*#define *SFML_VERSION_PATCH *([0-9]+).*" "\\1" SFML_VERSION_PATCH "${SFML_CONFIG_HPP_CONTENTS}") - if (NOT "${SFML_VERSION_PATCH}" MATCHES "^[0-9]+$") - set(SFML_VERSION_PATCH 0) - endif() - math(EXPR SFML_REQUESTED_VERSION "${SFML_FIND_VERSION_MAJOR} * 10000 + ${SFML_FIND_VERSION_MINOR} * 100 + ${SFML_FIND_VERSION_PATCH}") - - # if we could extract them, compare with the requested version number - if (SFML_VERSION_MAJOR) - # transform version numbers to an integer - math(EXPR SFML_VERSION "${SFML_VERSION_MAJOR} * 10000 + ${SFML_VERSION_MINOR} * 100 + ${SFML_VERSION_PATCH}") - - # compare them - if(SFML_VERSION LESS SFML_REQUESTED_VERSION) - set(SFML_VERSION_OK FALSE) - endif() - else() - # SFML version is < 2.0 - if (SFML_REQUESTED_VERSION GREATER 10900) - set(SFML_VERSION_OK FALSE) - set(SFML_VERSION_MAJOR 1) - set(SFML_VERSION_MINOR x) - set(SFML_VERSION_PATCH x) - endif() - endif() -endif() - -# find the requested modules -set(SFML_FOUND TRUE) # will be set to false if one of the required modules is not found -foreach(FIND_SFML_COMPONENT ${SFML_FIND_COMPONENTS}) - string(TOLOWER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_LOWER) - string(TOUPPER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_UPPER) - set(FIND_SFML_COMPONENT_NAME sfml-${FIND_SFML_COMPONENT_LOWER}) - - # no suffix for sfml-main, it is always a static library - if(FIND_SFML_COMPONENT_LOWER STREQUAL "main") - # release library - find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE - NAMES ${FIND_SFML_COMPONENT_NAME} - PATH_SUFFIXES lib64 lib - PATHS ${FIND_SFML_PATHS}) - - # debug library - find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG - NAMES ${FIND_SFML_COMPONENT_NAME}-d - PATH_SUFFIXES lib64 lib - PATHS ${FIND_SFML_PATHS}) - else() - # static release library - find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE - NAMES ${FIND_SFML_COMPONENT_NAME}-s - PATH_SUFFIXES lib64 lib - PATHS ${FIND_SFML_PATHS}) - - # static debug library - find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG - NAMES ${FIND_SFML_COMPONENT_NAME}-s-d - PATH_SUFFIXES lib64 lib - PATHS ${FIND_SFML_PATHS}) - - # dynamic release library - find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE - NAMES ${FIND_SFML_COMPONENT_NAME} - PATH_SUFFIXES lib64 lib - PATHS ${FIND_SFML_PATHS}) - - # dynamic debug library - find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG - NAMES ${FIND_SFML_COMPONENT_NAME}-d - PATH_SUFFIXES lib64 lib - PATHS ${FIND_SFML_PATHS}) - - # choose the entries that fit the requested link type - if(SFML_STATIC_LIBRARIES) - if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE}) - endif() - if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG}) - endif() - else() - if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE}) - endif() - if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG}) - endif() - endif() - endif() - - if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG OR SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) - # library found - set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND TRUE) - - # if both are found, set SFML_XXX_LIBRARY to contain both - if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY debug ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG} - optimized ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) - endif() - - # if only one debug/release variant is found, set the other to be equal to the found one - if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE) - # debug and not release - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}) - endif() - if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG) - # release and not debug - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE}) - endif() - else() - # library not found - set(SFML_FOUND FALSE) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND FALSE) - set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY "") - set(FIND_SFML_MISSING "${FIND_SFML_MISSING} SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY") - endif() - - # mark as advanced - MARK_AS_ADVANCED(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY - SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE - SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG - SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE - SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG - SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE - SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG) - - # add to the global list of libraries - set(SFML_LIBRARIES ${SFML_LIBRARIES} "${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY}") -endforeach() - -# in case of static linking, we must also define the list of all the dependencies of SFML libraries -if(SFML_STATIC_LIBRARIES) - - # detect the OS - if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(FIND_SFML_OS_WINDOWS 1) - elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") - set(FIND_SFML_OS_LINUX 1) - elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") - set(FIND_SFML_OS_FREEBSD 1) - elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(FIND_SFML_OS_MACOSX 1) - endif() - - # start with an empty list - set(SFML_DEPENDENCIES) - set(FIND_SFML_DEPENDENCIES_NOTFOUND) - - # macro that searches for a 3rd-party library - macro(find_sfml_dependency output friendlyname) - # No lookup in environment variables (PATH on Windows), as they may contain wrong library versions - find_library(${output} NAMES ${ARGN} PATHS ${FIND_SFML_PATHS} PATH_SUFFIXES lib NO_SYSTEM_ENVIRONMENT_PATH) - if(${${output}} STREQUAL "${output}-NOTFOUND") - unset(output) - set(FIND_SFML_DEPENDENCIES_NOTFOUND "${FIND_SFML_DEPENDENCIES_NOTFOUND} ${friendlyname}") - endif() - endmacro() - - # sfml-system - list(FIND SFML_FIND_COMPONENTS "system" FIND_SFML_SYSTEM_COMPONENT) - if(NOT ${FIND_SFML_SYSTEM_COMPONENT} EQUAL -1) - - # update the list -- these are only system libraries, no need to find them - if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD OR FIND_SFML_OS_MACOSX) - set(SFML_SYSTEM_DEPENDENCIES "pthread") - endif() - if(FIND_SFML_OS_LINUX) - set(SFML_SYSTEM_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} "rt") - endif() - if(FIND_SFML_OS_WINDOWS) - set(SFML_SYSTEM_DEPENDENCIES "winmm") - endif() - set(SFML_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} ${SFML_DEPENDENCIES}) - endif() - - # sfml-network - list(FIND SFML_FIND_COMPONENTS "network" FIND_SFML_NETWORK_COMPONENT) - if(NOT ${FIND_SFML_NETWORK_COMPONENT} EQUAL -1) - - # update the list -- these are only system libraries, no need to find them - if(FIND_SFML_OS_WINDOWS) - set(SFML_NETWORK_DEPENDENCIES "ws2_32") - endif() - set(SFML_DEPENDENCIES ${SFML_NETWORK_DEPENDENCIES} ${SFML_DEPENDENCIES}) - endif() - - # sfml-window - list(FIND SFML_FIND_COMPONENTS "window" FIND_SFML_WINDOW_COMPONENT) - if(NOT ${FIND_SFML_WINDOW_COMPONENT} EQUAL -1) - - # find libraries - if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD) - find_sfml_dependency(X11_LIBRARY "X11" X11) - find_sfml_dependency(XRANDR_LIBRARY "Xrandr" Xrandr) - endif() - - if(FIND_SFML_OS_LINUX) - find_sfml_dependency(UDEV_LIBRARIES "UDev" udev libudev) - endif() - - # update the list - if(FIND_SFML_OS_WINDOWS) - set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "opengl32" "winmm" "gdi32") - elseif(FIND_SFML_OS_LINUX) - set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} ${UDEV_LIBRARIES}) - elseif(FIND_SFML_OS_FREEBSD) - set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} "usbhid") - elseif(FIND_SFML_OS_MACOSX) - set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "-framework OpenGL -framework Foundation -framework AppKit -framework IOKit -framework Carbon") - endif() - set(SFML_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} ${SFML_DEPENDENCIES}) - endif() - - # sfml-graphics - list(FIND SFML_FIND_COMPONENTS "graphics" FIND_SFML_GRAPHICS_COMPONENT) - if(NOT ${FIND_SFML_GRAPHICS_COMPONENT} EQUAL -1) - - # find libraries - find_sfml_dependency(FREETYPE_LIBRARY "FreeType" freetype) - find_sfml_dependency(JPEG_LIBRARY "libjpeg" jpeg) - - # update the list - set(SFML_GRAPHICS_DEPENDENCIES ${FREETYPE_LIBRARY} ${JPEG_LIBRARY}) - set(SFML_DEPENDENCIES ${SFML_GRAPHICS_DEPENDENCIES} ${SFML_DEPENDENCIES}) - endif() - - # sfml-audio - list(FIND SFML_FIND_COMPONENTS "audio" FIND_SFML_AUDIO_COMPONENT) - if(NOT ${FIND_SFML_AUDIO_COMPONENT} EQUAL -1) - - # find libraries - find_sfml_dependency(OPENAL_LIBRARY "OpenAL" openal openal32) - find_sfml_dependency(OGG_LIBRARY "Ogg" ogg) - find_sfml_dependency(VORBIS_LIBRARY "Vorbis" vorbis) - find_sfml_dependency(VORBISFILE_LIBRARY "VorbisFile" vorbisfile) - find_sfml_dependency(VORBISENC_LIBRARY "VorbisEnc" vorbisenc) - find_sfml_dependency(FLAC_LIBRARY "FLAC" FLAC) - - # update the list - set(SFML_AUDIO_DEPENDENCIES ${OPENAL_LIBRARY} ${FLAC_LIBRARY} ${VORBISENC_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) - set(SFML_DEPENDENCIES ${SFML_DEPENDENCIES} ${SFML_AUDIO_DEPENDENCIES}) - endif() - -endif() - -# handle errors -if(NOT SFML_VERSION_OK) - # SFML version not ok - set(FIND_SFML_ERROR "SFML found but version too low (requested: ${SFML_FIND_VERSION}, found: ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH})") - set(SFML_FOUND FALSE) -elseif(SFML_STATIC_LIBRARIES AND FIND_SFML_DEPENDENCIES_NOTFOUND) - set(FIND_SFML_ERROR "SFML found but some of its dependencies are missing (${FIND_SFML_DEPENDENCIES_NOTFOUND})") - set(SFML_FOUND FALSE) -elseif(NOT SFML_FOUND) - # include directory or library not found - set(FIND_SFML_ERROR "Could NOT find SFML (missing: ${FIND_SFML_MISSING})") -endif() -if (NOT SFML_FOUND) - if(SFML_FIND_REQUIRED) - # fatal error - message(FATAL_ERROR ${FIND_SFML_ERROR}) - elseif(NOT SFML_FIND_QUIETLY) - # error but continue - message("${FIND_SFML_ERROR}") - endif() -endif() - -# handle success -if(SFML_FOUND AND NOT SFML_FIND_QUIETLY) - message(STATUS "Found SFML ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH} in ${SFML_INCLUDE_DIR}") -endif() diff --git a/cmake/Options.cmake b/cmake/Options.cmake index f5cbdda5..e0ca3e93 100644 --- a/cmake/Options.cmake +++ b/cmake/Options.cmake @@ -30,7 +30,6 @@ option(VBAM_STATIC "Try to link all libraries statically" ${VBAM_STATIC_DEFAULT} if(VBAM_STATIC) set(SDL2_STATIC ON) - set(SFML_STATIC_LIBRARIES ON) set(FFMPEG_STATIC ON) set(OPENAL_STATIC ON) set_property(GLOBAL PROPERTY LINK_SEARCH_START_STATIC ON) @@ -68,14 +67,8 @@ endif() find_package(PkgConfig) # Link / SFML -if(TRANSLATIONS_ONLY) - set(ENABLE_LINK_DEFAULT OFF) -else() - find_package(SFML 3.0 COMPONENTS network system) - set(ENABLE_LINK_DEFAULT OFF) - if(SFML_FOUND) - set(ENABLE_LINK_DEFAULT ON) - endif() +if(NOT TRANSLATIONS_ONLY) + set(ENABLE_LINK_DEFAULT ON) endif() option(ENABLE_LINK "Enable GBA linking functionality" ${ENABLE_LINK_DEFAULT}) diff --git a/installdeps b/installdeps index 8a22859e..55437a6c 100755 --- a/installdeps +++ b/installdeps @@ -191,7 +191,7 @@ freebsd_installdeps() { check sudo pkg update - pkgs="llvm-devel cmake ccache nasm ffmpeg gettext-tools gettext pkgconf sdl2 sfml wx31-gtk3 iconv zip ninja" + pkgs="llvm-devel cmake ccache nasm ffmpeg gettext-tools gettext pkgconf sdl2 wx31-gtk3 iconv zip ninja" [ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg" @@ -318,12 +318,6 @@ debian_installdeps() { if [ -z "$target" ]; then sudo apt-get -qq -y update - sfml_libs= - - for lib in graphics window network; do - sfml_libs="$sfml_libs $(apt-cache search "libsfml-$lib" | sed 's/ - .*//' | sort -r | head -1)" - done - glew_lib=$(apt-cache search libglew | grep '^libglew[0-9]' | sed 's/ - .*//' | sort -r | head -1) sdl_lib=$(apt-cache search '^libsdl2-2.0' | sed 's/ - .*//' | sort -r | head -1) @@ -350,7 +344,7 @@ debian_installdeps() { ;; esac - pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev libsfml-dev $sfml_libs $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev" + pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev" [ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev" @@ -401,7 +395,7 @@ debian_installdeps() { fi fi - deps="gcc zlib ffmpeg gettext sdl2 sfml openal wxwidgets openal" + deps="gcc zlib ffmpeg gettext sdl2 openal wxwidgets openal" [ -n "$ENABLE_FFMPEG" ] && deps="$deps ffmpeg" set -- @@ -498,7 +492,7 @@ fedora_installdeps() { # this is sometimes necessary for rawhide set -- --exclude='glibc32*' fi - for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel SFML-devel openal-soft-devel wxGTK-devel gtk3-devel; do + for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel openal-soft-devel wxGTK-devel gtk3-devel; do case $pkg in *ffmpeg*) [ -z "$ENABLE_FFMPEG" ] && continue @@ -597,8 +591,6 @@ fedora_installdeps() { # get the necessary win32 headers git submodule update --init --remote --recursive - - warning='SFML is required for LINK support, Fedora does not currently have a MinGW SFML package, if you want LINK support you will need to install it manually' fi [ -z "$rpms_installed" ] && check sudo dnf -y --nogpgcheck --best --allowerasing install "$@" @@ -684,8 +676,6 @@ rhel_installdeps() { set -- --exclude='glibc32*' fi - warning='RHEL does not currently have SFML packages, LINK support will be disabled' - for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel openal-soft-devel wxGTK3-devel gtk3-devel; do case $pkg in *ffmpeg*) @@ -777,8 +767,6 @@ rhel_installdeps() { # get the necessary win32 headers git submodule update --init --remote --recursive - - warning='SFML is required for LINK support, RHEL/EPEL does not currently have a MinGW SFML package, if you want LINK support you will need to install it manually' fi [ -z "$rpms_installed" ] && check sudo yum -y install "$@" @@ -797,7 +785,7 @@ suse_installdeps() { check_cross installing - tools="make cmake ccache nasm gettext-tools pkg-config ccache zip sfml2-devel ninja" + tools="make cmake ccache nasm gettext-tools pkg-config ccache zip ninja" libs="gcc gcc-c++ libSDL2-devel wxGTK3-3_2-devel openal-soft-devel ffmpeg-7-libavcodec-devel ffmpeg-7-libavdevice-devel ffmpeg-7-libavfilter-devel ffmpeg-7-libavformat-devel ffmpeg-7-libavutil-devel ffmpeg-7-libpostproc-devel ffmpeg-7-libswresample-devel ffmpeg-7-libswscale-devel" @@ -866,7 +854,7 @@ archlinux_installdeps() { $pacman -Q gtk3-classic >/dev/null 2>&1 && gtk=gtk3-classic - libs="zlib mesa gettext sdl2 wxgtk3 $gtk sfml openal" + libs="zlib mesa gettext sdl2 wxgtk3 $gtk openal" [ -n "$ENABLE_FFMPEG" ] && libs="$libs ffmpeg" @@ -882,7 +870,7 @@ archlinux_installdeps() { else # try to build 32 bit binaries - # lib32-sfml and lib32-ffmpeg are in AUR + # lib32-ffmpeg is in AUR archlinux_require_yaourt # enable multilib repos if not enabled @@ -976,8 +964,6 @@ EOF # get the necessary win32 headers git submodule update --init --remote --recursive - - warning 'SFML is required for LINK support, the SFML package in AUR is currently broken, if you want LINK support you will need to install it manually' fi build_instructions @@ -1040,13 +1026,12 @@ solus_installdeps() { done else # no 32bit versions of these - set -- "$@" SFML-devel ffmpeg-devel + set -- "$@" ffmpeg-devel fi check sudo eopkg -y install "$@" if [ -n "$amd64" -a "$target" = m32 ]; then - warning 'SFML is required for LINK support, there is no 32 bit SFML package in Solus currently, if you want LINK support you will need to install it manually' warning 'ffmpeg is required for game recording, there is no 32 bit ffmpeg package in Solus currently, you may wish to install it manually' fi @@ -1068,7 +1053,6 @@ gentoo_installdeps() { dev-util/ccache \ sys-devel/binutils \ media-libs/libsdl2 \ - media-libs/libsfml \ media-libs/openal \ x11-libs/wxGTK:$wx_slot \ sys-libs/zlib \ @@ -1086,7 +1070,7 @@ gentoo_installdeps() { alpine_installdeps() { installing - check sudo apk add cmake ninja g++ ccache nasm gettext-dev zlib-dev mesa-dev sdl2-dev glu-dev sfml-dev wxwidgets-dev gtk+3.0-dev zip + check sudo apk add cmake ninja g++ ccache nasm gettext-dev zlib-dev mesa-dev sdl2-dev glu-dev wxwidgets-dev gtk+3.0-dev zip build_instructions } @@ -1121,7 +1105,7 @@ windows_installdeps() { ;; esac - pkgs="$pkgs SDL2 sfml wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal" + pkgs="$pkgs SDL2 wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal" case "$target" in *x86_64) @@ -1197,11 +1181,6 @@ brew_installdeps() { [ -n "$ENABLE_FFMPEG" ] && brews="$brews ffmpeg" - # sfml brew currently broken in the travis mac environment -# if [ -z "$TRAVIS" ]; then - brews="$brews sfml" -# fi - # This is necessary for the GitHub Actions CI: brew -v install python brew link --overwrite python @@ -1224,7 +1203,7 @@ macports_installdeps() { check sudo port -v selfupdate - ports="cmake ccache nasm gettext pkgconfig libsdl2 sfml wxWidgets-3.0 libiconv ninja" + ports="cmake ccache nasm gettext pkgconfig libsdl2 wxWidgets-3.0 libiconv ninja" [ -n "$ENABLE_FFMPEG" ] && ports="$ports ffmpeg" @@ -1240,7 +1219,7 @@ fink_installdeps() { check sudo fink -vy selfupdate - pkgs="cmake ccache nasm libgettext8-dev gettext-tools pkgconfig sdl2 wxwidgets300-osxcocoa libiconv-dev sfml24-dev ccache ninja" + pkgs="cmake ccache nasm libgettext8-dev gettext-tools pkgconfig sdl2 wxwidgets300-osxcocoa libiconv-dev ccache ninja" [ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg" diff --git a/snapcraft.yaml b/snapcraft.yaml index aaf50afd..d15b327d 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -19,8 +19,6 @@ parts: cmake-generator: Ninja stage-packages: - libsdl2-2.0-0 - - libsfml-network2.5 - - libsfml-system2.5 - libnotify4 - libsm6 - libopenal1 @@ -46,7 +44,6 @@ parts: - zip - libnotify-dev - libsdl2-dev - - libsfml-dev - libgtk-3-dev - libopenal-dev - libwxgtk3.0-gtk3-dev diff --git a/src/core/gba/gbaLink.cpp b/src/core/gba/gbaLink.cpp index 17081e09..a8687948 100644 --- a/src/core/gba/gbaLink.cpp +++ b/src/core/gba/gbaLink.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include "SFML/Network.hpp" #include #define _(x) gettext(x) diff --git a/src/core/gba/internal/gbaSockClient.h b/src/core/gba/internal/gbaSockClient.h index 1e4cae43..42b966ba 100644 --- a/src/core/gba/internal/gbaSockClient.h +++ b/src/core/gba/internal/gbaSockClient.h @@ -7,7 +7,7 @@ #include -#include +#include "SFML/Network.hpp" class GBASockClient { public: diff --git a/third_party/sfml/include/SFML/Config.hpp b/third_party/sfml/include/SFML/Config.hpp new file mode 100644 index 00000000..8004df47 --- /dev/null +++ b/third_party/sfml/include/SFML/Config.hpp @@ -0,0 +1,164 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + + +//////////////////////////////////////////////////////////// +// SFML version +//////////////////////////////////////////////////////////// +#define SFML_VERSION_MAJOR 3 +#define SFML_VERSION_MINOR 1 +#define SFML_VERSION_PATCH 0 +#define SFML_VERSION_IS_RELEASE false + + +//////////////////////////////////////////////////////////// +// Identify the operating system +// see https://sourceforge.net/p/predef/wiki/Home/ +//////////////////////////////////////////////////////////// +#if defined(_WIN32) + +// Windows +#define SFML_SYSTEM_WINDOWS +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#elif defined(__APPLE__) && defined(__MACH__) + +// Apple platform, see which one it is +#include "TargetConditionals.h" + +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + +// iOS +#define SFML_SYSTEM_IOS + +#elif TARGET_OS_MAC + +// macOS +#define SFML_SYSTEM_MACOS + +#else + +// Unsupported Apple system +#error This Apple operating system is not supported by SFML library + +#endif + +#elif defined(__unix__) + +// UNIX system, see which one it is +#if defined(__ANDROID__) + +// Android +#define SFML_SYSTEM_ANDROID + +#elif defined(__linux__) + +// Linux +#define SFML_SYSTEM_LINUX + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + +// FreeBSD +#define SFML_SYSTEM_FREEBSD + +#elif defined(__OpenBSD__) + +// OpenBSD +#define SFML_SYSTEM_OPENBSD + +#elif defined(__NetBSD__) + +// NetBSD +#define SFML_SYSTEM_NETBSD + +#else + +// Unsupported UNIX system +#error This UNIX operating system is not supported by SFML library + +#endif + +#else + +// Unsupported system +#error This operating system is not supported by SFML library + +#endif + + +//////////////////////////////////////////////////////////// +// Ensure minimum C++ language standard version is met +//////////////////////////////////////////////////////////// +#if (defined(_MSVC_LANG) && _MSVC_LANG < 201703L) || (!defined(_MSVC_LANG) && __cplusplus < 201703L) +#error "Enable C++17 or newer for your compiler (e.g. -std=c++17 for GCC/Clang or /std:c++17 for MSVC)" +#endif + + +//////////////////////////////////////////////////////////// +// Portable debug macro +//////////////////////////////////////////////////////////// +#if !defined(NDEBUG) + +#define SFML_DEBUG + +#endif + + +//////////////////////////////////////////////////////////// +// Helpers to create portable import / export macros for each module +//////////////////////////////////////////////////////////// +#if !defined(SFML_STATIC) + +#if defined(SFML_SYSTEM_WINDOWS) + +// Windows compilers need specific (and different) keywords for export and import +#define SFML_API_EXPORT __declspec(dllexport) +#define SFML_API_IMPORT __declspec(dllimport) + +// For Visual C++ compilers, we also need to turn off this annoying C4251 & C4275 warning +#ifdef _MSC_VER + +#pragma warning(disable : 4251) // Using standard library types in our own exported types is okay +#pragma warning(disable : 4275) // Exporting types derived from the standard library is okay + +#endif + +#else // Linux, FreeBSD, macOS + +#define SFML_API_EXPORT __attribute__((__visibility__("default"))) +#define SFML_API_IMPORT __attribute__((__visibility__("default"))) + +#endif + +#else + +// Static build doesn't need import/export macros +#define SFML_API_EXPORT +#define SFML_API_IMPORT + +#endif diff --git a/third_party/sfml/include/SFML/Network.hpp b/third_party/sfml/include/SFML/Network.hpp new file mode 100644 index 00000000..ff4990f0 --- /dev/null +++ b/third_party/sfml/include/SFML/Network.hpp @@ -0,0 +1,51 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +//////////////////////////////////////////////////////////// +/// \defgroup network Network module +/// +/// Socket-based communication, utilities and higher-level +/// network protocols (HTTP, FTP). +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/Export.hpp b/third_party/sfml/include/SFML/Network/Export.hpp new file mode 100644 index 00000000..edcc6e55 --- /dev/null +++ b/third_party/sfml/include/SFML/Network/Export.hpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +//////////////////////////////////////////////////////////// +// Portable import / export macros +//////////////////////////////////////////////////////////// +#if defined(SFML_NETWORK_EXPORTS) + +#define SFML_NETWORK_API SFML_API_EXPORT + +#else + +#define SFML_NETWORK_API SFML_API_IMPORT + +#endif diff --git a/third_party/sfml/include/SFML/Network/Ftp.hpp b/third_party/sfml/include/SFML/Network/Ftp.hpp new file mode 100644 index 00000000..12e3bce3 --- /dev/null +++ b/third_party/sfml/include/SFML/Network/Ftp.hpp @@ -0,0 +1,629 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + +#include +#include +#include + + +namespace sf +{ +class IpAddress; + +//////////////////////////////////////////////////////////// +/// \brief A FTP client +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API Ftp +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Enumeration of transfer modes + /// + //////////////////////////////////////////////////////////// + enum class TransferMode + { + Binary, //!< Binary mode (file is transferred as a sequence of bytes) + Ascii, //!< Text mode using ASCII encoding + Ebcdic //!< Text mode using EBCDIC encoding + }; + + //////////////////////////////////////////////////////////// + /// \brief FTP response + /// + //////////////////////////////////////////////////////////// + class SFML_NETWORK_API Response + { + public: + //////////////////////////////////////////////////////////// + /// \brief Status codes possibly returned by a FTP response + /// + //////////////////////////////////////////////////////////// + enum class Status + { + // 1xx: the requested action is being initiated, + // expect another reply before proceeding with a new command + RestartMarkerReply = 110, //!< Restart marker reply + ServiceReadySoon = 120, //!< Service ready in N minutes + DataConnectionAlreadyOpened = 125, //!< Data connection already opened, transfer starting + OpeningDataConnection = 150, //!< File status ok, about to open data connection + + // 2xx: the requested action has been successfully completed + Ok = 200, //!< Command ok + PointlessCommand = 202, //!< Command not implemented + SystemStatus = 211, //!< System status, or system help reply + DirectoryStatus = 212, //!< Directory status + FileStatus = 213, //!< File status + HelpMessage = 214, //!< Help message + SystemType = 215, //!< NAME system type, where NAME is an official system name from the list in the Assigned Numbers document + ServiceReady = 220, //!< Service ready for new user + ClosingConnection = 221, //!< Service closing control connection + DataConnectionOpened = 225, //!< Data connection open, no transfer in progress + ClosingDataConnection = 226, //!< Closing data connection, requested file action successful + EnteringPassiveMode = 227, //!< Entering passive mode + LoggedIn = 230, //!< User logged in, proceed. Logged out if appropriate + FileActionOk = 250, //!< Requested file action ok + DirectoryOk = 257, //!< PATHNAME created + + // 3xx: the command has been accepted, but the requested action + // is dormant, pending receipt of further information + NeedPassword = 331, //!< User name ok, need password + NeedAccountToLogIn = 332, //!< Need account for login + NeedInformation = 350, //!< Requested file action pending further information + + // 4xx: the command was not accepted and the requested action did not take place, + // but the error condition is temporary and the action may be requested again + ServiceUnavailable = 421, //!< Service not available, closing control connection + DataConnectionUnavailable = 425, //!< Can't open data connection + TransferAborted = 426, //!< Connection closed, transfer aborted + FileActionAborted = 450, //!< Requested file action not taken + LocalError = 451, //!< Requested action aborted, local error in processing + InsufficientStorageSpace = 452, //!< Requested action not taken; insufficient storage space in system, file unavailable + + // 5xx: the command was not accepted and + // the requested action did not take place + CommandUnknown = 500, //!< Syntax error, command unrecognized + ParametersUnknown = 501, //!< Syntax error in parameters or arguments + CommandNotImplemented = 502, //!< Command not implemented + BadCommandSequence = 503, //!< Bad sequence of commands + ParameterNotImplemented = 504, //!< Command not implemented for that parameter + NotLoggedIn = 530, //!< Not logged in + NeedAccountToStore = 532, //!< Need account for storing files + FileUnavailable = 550, //!< Requested action not taken, file unavailable + PageTypeUnknown = 551, //!< Requested action aborted, page type unknown + NotEnoughMemory = 552, //!< Requested file action aborted, exceeded storage allocation + FilenameNotAllowed = 553, //!< Requested action not taken, file name not allowed + + // 10xx: SFML custom codes + InvalidResponse = 1000, //!< Not part of the FTP standard, generated by SFML when a received response cannot be parsed + ConnectionFailed = 1001, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection with the server fails + ConnectionClosed = 1002, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection is unexpectedly closed + InvalidFile = 1003 //!< Not part of the FTP standard, generated by SFML when a local file cannot be read or written + }; + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// This constructor is used by the FTP client to build + /// the response. + /// + /// \param code Response status code + /// \param message Response message + /// + //////////////////////////////////////////////////////////// + explicit Response(Status code = Status::InvalidResponse, std::string message = ""); + + //////////////////////////////////////////////////////////// + /// \brief Check if the status code means a success + /// + /// This function is defined for convenience, it is + /// equivalent to testing if the status code is < 400. + /// + /// \return `true` if the status is a success, `false` if it is a failure + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool isOk() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the status code of the response + /// + /// \return Status code + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status getStatus() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the full message contained in the response + /// + /// \return The response message + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] const std::string& getMessage() const; + + private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Status m_status; //!< Status code returned from the server + std::string m_message; //!< Last message received from the server + }; + + //////////////////////////////////////////////////////////// + /// \brief Specialization of FTP response returning a directory + /// + //////////////////////////////////////////////////////////// + class SFML_NETWORK_API DirectoryResponse : public Response + { + public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// \param response Source response + /// + //////////////////////////////////////////////////////////// + DirectoryResponse(const Response& response); + + //////////////////////////////////////////////////////////// + /// \brief Get the directory returned in the response + /// + /// \return Directory name + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] const std::filesystem::path& getDirectory() const; + + private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::filesystem::path m_directory; //!< Directory extracted from the response message + }; + + + //////////////////////////////////////////////////////////// + /// \brief Specialization of FTP response returning a + /// file name listing + //////////////////////////////////////////////////////////// + class SFML_NETWORK_API ListingResponse : public Response + { + public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// \param response Source response + /// \param data Data containing the raw listing + /// + //////////////////////////////////////////////////////////// + ListingResponse(const Response& response, const std::string& data); + + //////////////////////////////////////////////////////////// + /// \brief Return the array of directory/file names + /// + /// \return Array containing the requested listing + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] const std::vector& getListing() const; + + private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::vector m_listing; //!< Directory/file names extracted from the data + }; + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + Ftp() = default; + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + /// Automatically closes the connection with the server if + /// it is still opened. + /// + //////////////////////////////////////////////////////////// + ~Ftp(); + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + //////////////////////////////////////////////////////////// + Ftp(const Ftp&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy assignment + /// + //////////////////////////////////////////////////////////// + Ftp& operator=(const Ftp&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Connect to the specified FTP server + /// + /// The port has a default value of 21, which is the standard + /// port used by the FTP protocol. You shouldn't use a different + /// value, unless you really know what you do. + /// This function tries to connect to the server so it may take + /// a while to complete, especially if the server is not + /// reachable. To avoid blocking your application for too long, + /// you can use a timeout. The default value, `Time::Zero`, means that the + /// system timeout will be used (which is usually pretty long). + /// + /// \param server Name or address of the FTP server to connect to + /// \param port Port used for the connection + /// \param timeout Maximum time to wait + /// + /// \return Server response to the request + /// + /// \see `disconnect` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response connect(IpAddress server, unsigned short port = 21, Time timeout = Time::Zero); + + //////////////////////////////////////////////////////////// + /// \brief Close the connection with the server + /// + /// \return Server response to the request + /// + /// \see `connect` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response disconnect(); + + //////////////////////////////////////////////////////////// + /// \brief Log in using an anonymous account + /// + /// Logging in is mandatory after connecting to the server. + /// Users that are not logged in cannot perform any operation. + /// + /// \return Server response to the request + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response login(); + + //////////////////////////////////////////////////////////// + /// \brief Log in using a username and a password + /// + /// Logging in is mandatory after connecting to the server. + /// Users that are not logged in cannot perform any operation. + /// + /// \param name User name + /// \param password Password + /// + /// \return Server response to the request + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response login(const std::string& name, const std::string& password); + + //////////////////////////////////////////////////////////// + /// \brief Send a null command to keep the connection alive + /// + /// This command is useful because the server may close the + /// connection automatically if no command is sent. + /// + /// \return Server response to the request + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response keepAlive(); + + //////////////////////////////////////////////////////////// + /// \brief Get the current working directory + /// + /// The working directory is the root path for subsequent + /// operations involving directories and/or filenames. + /// + /// \return Server response to the request + /// + /// \see `getDirectoryListing`, `changeDirectory`, `parentDirectory` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] DirectoryResponse getWorkingDirectory(); + + //////////////////////////////////////////////////////////// + /// \brief Get the contents of the given directory + /// + /// This function retrieves the sub-directories and files + /// contained in the given directory. It is not recursive. + /// The `directory` parameter is relative to the current + /// working directory. + /// + /// \param directory Directory to list + /// + /// \return Server response to the request + /// + /// \see `getWorkingDirectory`, `changeDirectory`, `parentDirectory` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] ListingResponse getDirectoryListing(const std::string& directory = ""); + + //////////////////////////////////////////////////////////// + /// \brief Change the current working directory + /// + /// The new directory must be relative to the current one. + /// + /// \param directory New working directory + /// + /// \return Server response to the request + /// + /// \see `getWorkingDirectory`, `getDirectoryListing`, `parentDirectory` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response changeDirectory(const std::string& directory); + + //////////////////////////////////////////////////////////// + /// \brief Go to the parent directory of the current one + /// + /// \return Server response to the request + /// + /// \see `getWorkingDirectory`, `getDirectoryListing`, `changeDirectory` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response parentDirectory(); + + //////////////////////////////////////////////////////////// + /// \brief Create a new directory + /// + /// The new directory is created as a child of the current + /// working directory. + /// + /// \param name Name of the directory to create + /// + /// \return Server response to the request + /// + /// \see `deleteDirectory` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response createDirectory(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Remove an existing directory + /// + /// The directory to remove must be relative to the + /// current working directory. + /// Use this function with caution, the directory will + /// be removed permanently! + /// + /// \param name Name of the directory to remove + /// + /// \return Server response to the request + /// + /// \see `createDirectory` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response deleteDirectory(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief Rename an existing file + /// + /// The file names must be relative to the current working + /// directory. + /// + /// \param file File to rename + /// \param newName New name of the file + /// + /// \return Server response to the request + /// + /// \see `deleteFile` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response renameFile(const std::filesystem::path& file, const std::filesystem::path& newName); + + //////////////////////////////////////////////////////////// + /// \brief Remove an existing file + /// + /// The file name must be relative to the current working + /// directory. + /// Use this function with caution, the file will be + /// removed permanently! + /// + /// \param name File to remove + /// + /// \return Server response to the request + /// + /// \see `renameFile` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response deleteFile(const std::filesystem::path& name); + + //////////////////////////////////////////////////////////// + /// \brief Download a file from the server + /// + /// The file name of the distant file is relative to the + /// current working directory of the server, and the local + /// destination path is relative to the current directory + /// of your application. + /// If a file with the same file name as the distant file + /// already exists in the local destination path, it will + /// be overwritten. + /// + /// \param remoteFile File name of the distant file to download + /// \param localPath The directory in which to put the file on the local computer + /// \param mode Transfer mode + /// + /// \return Server response to the request + /// + /// \see `upload` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response download(const std::filesystem::path& remoteFile, + const std::filesystem::path& localPath, + TransferMode mode = TransferMode::Binary); + + //////////////////////////////////////////////////////////// + /// \brief Upload a file to the server + /// + /// The name of the local file is relative to the current + /// working directory of your application, and the + /// remote path is relative to the current directory of the + /// FTP server. + /// + /// The append parameter controls whether the remote file is + /// appended to or overwritten if it already exists. + /// + /// \param localFile Path of the local file to upload + /// \param remotePath The directory in which to put the file on the server + /// \param mode Transfer mode + /// \param append Pass `true` to append to or `false` to overwrite the remote file if it already exists + /// + /// \return Server response to the request + /// + /// \see `download` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response upload(const std::filesystem::path& localFile, + const std::filesystem::path& remotePath, + TransferMode mode = TransferMode::Binary, + bool append = false); + + //////////////////////////////////////////////////////////// + /// \brief Send a command to the FTP server + /// + /// While the most often used commands are provided as member + /// functions in the `sf::Ftp` class, this method can be used + /// to send any FTP command to the server. If the command + /// requires one or more parameters, they can be specified + /// in `parameter`. If the server returns information, you + /// can extract it from the response using `Response::getMessage()`. + /// + /// \param command Command to send + /// \param parameter Command parameter + /// + /// \return Server response to the request + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response sendCommand(const std::string& command, const std::string& parameter = ""); + +private: + //////////////////////////////////////////////////////////// + /// \brief Receive a response from the server + /// + /// This function must be called after each call to + /// `sendCommand` that expects a response. + /// + /// \return Server response to the request + /// + //////////////////////////////////////////////////////////// + Response getResponse(); + + //////////////////////////////////////////////////////////// + /// \brief Utility class for exchanging data with the server + /// on the data channel + /// + //////////////////////////////////////////////////////////// + class DataChannel; + + friend class DataChannel; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + TcpSocket m_commandSocket; //!< Socket holding the control connection with the server + std::string m_receiveBuffer; //!< Received command data that is yet to be processed +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::Ftp +/// \ingroup network +/// +/// `sf::Ftp` is a very simple FTP client that allows you +/// to communicate with a FTP server. The FTP protocol allows +/// you to manipulate a remote file system (list files, +/// upload, download, create, remove, ...). +/// +/// Using the FTP client consists of 4 parts: +/// \li Connecting to the FTP server +/// \li Logging in (either as a registered user or anonymously) +/// \li Sending commands to the server +/// \li Disconnecting (this part can be done implicitly by the destructor) +/// +/// Every command returns a FTP response, which contains the +/// status code as well as a message from the server. Some +/// commands such as `getWorkingDirectory()` and `getDirectoryListing()` +/// return additional data, and use a class derived from +/// `sf::Ftp::Response` to provide this data. The most often used +/// commands are directly provided as member functions, but it is +/// also possible to use specific commands with the `sendCommand()` function. +/// +/// Note that response statuses >= 1000 are not part of the FTP standard, +/// they are generated by SFML when an internal error occurs. +/// +/// All commands, especially upload and download, may take some +/// time to complete. This is important to know if you don't want +/// to block your application while the server is completing +/// the task. +/// +/// Usage example: +/// \code +/// // Create a new FTP client +/// sf::Ftp ftp; +/// +/// // Connect to the server +/// sf::Ftp::Response response = ftp.connect("ftp://ftp.myserver.com"); +/// if (response.isOk()) +/// std::cout << "Connected" << std::endl; +/// +/// // Log in +/// response = ftp.login("laurent", "dF6Zm89D"); +/// if (response.isOk()) +/// std::cout << "Logged in" << std::endl; +/// +/// // Print the working directory +/// sf::Ftp::DirectoryResponse directory = ftp.getWorkingDirectory(); +/// if (directory.isOk()) +/// std::cout << "Working directory: " << directory.getDirectory() << std::endl; +/// +/// // Create a new directory +/// response = ftp.createDirectory("files"); +/// if (response.isOk()) +/// std::cout << "Created new directory" << std::endl; +/// +/// // Upload a file to this new directory +/// response = ftp.upload("local-path/file.txt", "files", sf::Ftp::TransferMode::Ascii); +/// if (response.isOk()) +/// std::cout << "File uploaded" << std::endl; +/// +/// // Send specific commands (here: FEAT to list supported FTP features) +/// response = ftp.sendCommand("FEAT"); +/// if (response.isOk()) +/// std::cout << "Feature list:\n" << response.getMessage() << std::endl; +/// +/// // Disconnect from the server (optional) +/// ftp.disconnect(); +/// \endcode +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/Http.hpp b/third_party/sfml/include/SFML/Network/Http.hpp new file mode 100644 index 00000000..46d2d98f --- /dev/null +++ b/third_party/sfml/include/SFML/Network/Http.hpp @@ -0,0 +1,480 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include + +#include + +#include +#include +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief A HTTP client +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API Http +{ +public: + //////////////////////////////////////////////////////////// + /// \brief HTTP request + /// + //////////////////////////////////////////////////////////// + class SFML_NETWORK_API Request + { + public: + //////////////////////////////////////////////////////////// + /// \brief Enumerate the available HTTP methods for a request + /// + //////////////////////////////////////////////////////////// + enum class Method + { + Get, //!< Request in get mode, standard method to retrieve a page + Post, //!< Request in post mode, usually to send data to a page + Head, //!< Request a page's header only + Put, //!< Request in put mode, useful for a REST API + Delete //!< Request in delete mode, useful for a REST API + }; + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// This constructor creates a GET request, with the root + /// URI ("/") and an empty body. + /// + /// \param uri Target URI + /// \param method Method to use for the request + /// \param body Content of the request's body + /// + //////////////////////////////////////////////////////////// + Request(const std::string& uri = "/", Method method = Method::Get, const std::string& body = ""); + + //////////////////////////////////////////////////////////// + /// \brief Set the value of a field + /// + /// The field is created if it doesn't exist. The name of + /// the field is case-insensitive. + /// By default, a request doesn't contain any field (but the + /// mandatory fields are added later by the HTTP client when + /// sending the request). + /// + /// \param field Name of the field to set + /// \param value Value of the field + /// + //////////////////////////////////////////////////////////// + void setField(const std::string& field, const std::string& value); + + //////////////////////////////////////////////////////////// + /// \brief Set the request method + /// + /// See the Method enumeration for a complete list of all + /// the available methods. + /// The method is `Http::Request::Method::Get` by default. + /// + /// \param method Method to use for the request + /// + //////////////////////////////////////////////////////////// + void setMethod(Method method); + + //////////////////////////////////////////////////////////// + /// \brief Set the requested URI + /// + /// The URI is the resource (usually a web page or a file) + /// that you want to get or post. + /// The URI is "/" (the root page) by default. + /// + /// \param uri URI to request, relative to the host + /// + //////////////////////////////////////////////////////////// + void setUri(const std::string& uri); + + //////////////////////////////////////////////////////////// + /// \brief Set the HTTP version for the request + /// + /// The HTTP version is 1.0 by default. + /// + /// \param major Major HTTP version number + /// \param minor Minor HTTP version number + /// + //////////////////////////////////////////////////////////// + void setHttpVersion(unsigned int major, unsigned int minor); + + //////////////////////////////////////////////////////////// + /// \brief Set the body of the request + /// + /// The body of a request is optional and only makes sense + /// for POST requests. It is ignored for all other methods. + /// The body is empty by default. + /// + /// \param body Content of the body + /// + //////////////////////////////////////////////////////////// + void setBody(const std::string& body); + + private: + friend class Http; + + //////////////////////////////////////////////////////////// + /// \brief Prepare the final request to send to the server + /// + /// This is used internally by Http before sending the + /// request to the web server. + /// + /// \return String containing the request, ready to be sent + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::string prepare() const; + + //////////////////////////////////////////////////////////// + /// \brief Check if the request defines a field + /// + /// This function uses case-insensitive comparisons. + /// + /// \param field Name of the field to test + /// + /// \return `true` if the field exists, `false` otherwise + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool hasField(const std::string& field) const; + + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// + using FieldTable = std::map; // Use an ordered map for predictable payloads + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + FieldTable m_fields; //!< Fields of the header associated to their value + Method m_method; //!< Method to use for the request + std::string m_uri; //!< Target URI of the request + unsigned int m_majorVersion{1}; //!< Major HTTP version + unsigned int m_minorVersion{}; //!< Minor HTTP version + std::string m_body; //!< Body of the request + }; + + //////////////////////////////////////////////////////////// + /// \brief HTTP response + /// + //////////////////////////////////////////////////////////// + class SFML_NETWORK_API Response + { + public: + //////////////////////////////////////////////////////////// + /// \brief Enumerate all the valid status codes for a response + /// + //////////////////////////////////////////////////////////// + enum class Status + { + // 2xx: success + Ok = 200, //!< Most common code returned when operation was successful + Created = 201, //!< The resource has successfully been created + Accepted = 202, //!< The request has been accepted, but will be processed later by the server + NoContent = 204, //!< The server didn't send any data in return + ResetContent = 205, //!< The server informs the client that it should clear the view (form) that caused the request to be sent + PartialContent = 206, //!< The server has sent a part of the resource, as a response to a partial GET request + + // 3xx: redirection + MultipleChoices = 300, //!< The requested page can be accessed from several locations + MovedPermanently = 301, //!< The requested page has permanently moved to a new location + MovedTemporarily = 302, //!< The requested page has temporarily moved to a new location + NotModified = 304, //!< For conditional requests, means the requested page hasn't changed and doesn't need to be refreshed + + // 4xx: client error + BadRequest = 400, //!< The server couldn't understand the request (syntax error) + Unauthorized = 401, //!< The requested page needs an authentication to be accessed + Forbidden = 403, //!< The requested page cannot be accessed at all, even with authentication + NotFound = 404, //!< The requested page doesn't exist + RangeNotSatisfiable = 407, //!< The server can't satisfy the partial GET request (with a "Range" header field) + + // 5xx: server error + InternalServerError = 500, //!< The server encountered an unexpected error + NotImplemented = 501, //!< The server doesn't implement a requested feature + BadGateway = 502, //!< The gateway server has received an error from the source server + ServiceNotAvailable = 503, //!< The server is temporarily unavailable (overloaded, in maintenance, ...) + GatewayTimeout = 504, //!< The gateway server couldn't receive a response from the source server + VersionNotSupported = 505, //!< The server doesn't support the requested HTTP version + + // 10xx: SFML custom codes + InvalidResponse = 1000, //!< Response is not a valid HTTP one + ConnectionFailed = 1001 //!< Connection with server failed + }; + + //////////////////////////////////////////////////////////// + /// \brief Get the value of a field + /// + /// If the field `field` is not found in the response header, + /// the empty string is returned. This function uses + /// case-insensitive comparisons. + /// + /// \param field Name of the field to get + /// + /// \return Value of the field, or empty string if not found + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] const std::string& getField(const std::string& field) const; + + //////////////////////////////////////////////////////////// + /// \brief Get the response status code + /// + /// The status code should be the first thing to be checked + /// after receiving a response, it defines whether it is a + /// success, a failure or anything else (see the Status + /// enumeration). + /// + /// \return Status code of the response + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status getStatus() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the major HTTP version number of the response + /// + /// \return Major HTTP version number + /// + /// \see `getMinorHttpVersion` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] unsigned int getMajorHttpVersion() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the minor HTTP version number of the response + /// + /// \return Minor HTTP version number + /// + /// \see `getMajorHttpVersion` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] unsigned int getMinorHttpVersion() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the body of the response + /// + /// The body of a response may contain: + /// \li the requested page (for GET requests) + /// \li a response from the server (for POST requests) + /// \li nothing (for HEAD requests) + /// \li an error message (in case of an error) + /// + /// \return The response body + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] const std::string& getBody() const; + + private: + friend class Http; + + //////////////////////////////////////////////////////////// + /// \brief Construct the header from a response string + /// + /// This function is used by `Http` to build the response + /// of a request. + /// + /// \param data Content of the response to parse + /// + //////////////////////////////////////////////////////////// + void parse(const std::string& data); + + + //////////////////////////////////////////////////////////// + /// \brief Read values passed in the answer header + /// + /// This function is used by `Http` to extract values passed + /// in the response. + /// + /// \param in String stream containing the header values + /// + //////////////////////////////////////////////////////////// + void parseFields(std::istream& in); + + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// + using FieldTable = std::map; // Use an ordered map for predictable payloads + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + FieldTable m_fields; //!< Fields of the header + Status m_status{Status::ConnectionFailed}; //!< Status code + unsigned int m_majorVersion{}; //!< Major HTTP version + unsigned int m_minorVersion{}; //!< Minor HTTP version + std::string m_body; //!< Body of the response + }; + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + Http() = default; + + //////////////////////////////////////////////////////////// + /// \brief Construct the HTTP client with the target host + /// + /// This is equivalent to calling `setHost(host, port)`. + /// The port has a default value of 0, which means that the + /// HTTP client will use the right port according to the + /// protocol used (80 for HTTP). You should leave it like + /// this unless you really need a port other than the + /// standard one, or use an unknown protocol. + /// + /// \param host Web server to connect to + /// \param port Port to use for connection + /// + //////////////////////////////////////////////////////////// + Http(const std::string& host, unsigned short port = 0); + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + //////////////////////////////////////////////////////////// + Http(const Http&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy assignment + /// + //////////////////////////////////////////////////////////// + Http& operator=(const Http&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Set the target host + /// + /// This function just stores the host address and port, it + /// doesn't actually connect to it until you send a request. + /// The port has a default value of 0, which means that the + /// HTTP client will use the right port according to the + /// protocol used (80 for HTTP). You should leave it like + /// this unless you really need a port other than the + /// standard one, or use an unknown protocol. + /// + /// \param host Web server to connect to + /// \param port Port to use for connection + /// + //////////////////////////////////////////////////////////// + void setHost(const std::string& host, unsigned short port = 0); + + //////////////////////////////////////////////////////////// + /// \brief Send a HTTP request and return the server's response. + /// + /// You must have a valid host before sending a request (see `setHost`). + /// Any missing mandatory header field in the request will be added + /// with an appropriate value. + /// Warning: this function waits for the server's response and may + /// not return instantly; use a thread if you don't want to block your + /// application, or use a timeout to limit the time to wait. A value + /// of `Time::Zero` means that the client will use the system default timeout + /// (which is usually pretty long). + /// + /// \param request Request to send + /// \param timeout Maximum time to wait + /// + /// \return Server's response + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Response sendRequest(const Request& request, Time timeout = Time::Zero); + +private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + TcpSocket m_connection; //!< Connection to the host + std::optional m_host; //!< Web host address + std::string m_hostName; //!< Web host name + unsigned short m_port{}; //!< Port used for connection with host +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::Http +/// \ingroup network +/// +/// `sf::Http` is a very simple HTTP client that allows you +/// to communicate with a web server. You can retrieve +/// web pages, send data to an interactive resource, +/// download a remote file, etc. The HTTPS protocol is +/// not supported. +/// +/// The HTTP client is split into 3 classes: +/// \li `sf::Http::Request` +/// \li `sf::Http::Response` +/// \li `sf::Http` +/// +/// `sf::Http::Request` builds the request that will be +/// sent to the server. A request is made of: +/// \li a method (what you want to do) +/// \li a target URI (usually the name of the web page or file) +/// \li one or more header fields (options that you can pass to the server) +/// \li an optional body (for POST requests) +/// +/// `sf::Http::Response` parse the response from the web server +/// and provides getters to read them. The response contains: +/// \li a status code +/// \li header fields (that may be answers to the ones that you requested) +/// \li a body, which contains the contents of the requested resource +/// +/// `sf::Http` provides a simple function, SendRequest, to send a +/// `sf::Http::Request` and return the corresponding `sf::Http::Response` +/// from the server. +/// +/// Usage example: +/// \code +/// // Create a new HTTP client +/// sf::Http http; +/// +/// // We'll work on http://www.sfml-dev.org +/// http.setHost("http://www.sfml-dev.org"); +/// +/// // Prepare a request to get the 'features.php' page +/// sf::Http::Request request("features.php"); +/// +/// // Send the request +/// sf::Http::Response response = http.sendRequest(request); +/// +/// // Check the status code and display the result +/// sf::Http::Response::Status status = response.getStatus(); +/// if (status == sf::Http::Response::Status::Ok) +/// { +/// std::cout << response.getBody() << std::endl; +/// } +/// else +/// { +/// std::cout << "Error " << status << std::endl; +/// } +/// \endcode +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/IpAddress.hpp b/third_party/sfml/include/SFML/Network/IpAddress.hpp new file mode 100644 index 00000000..a43fd0fd --- /dev/null +++ b/third_party/sfml/include/SFML/Network/IpAddress.hpp @@ -0,0 +1,297 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include +#include +#include +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Encapsulate an IPv4 network address +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API IpAddress +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Construct the address from a null-terminated string view + /// + /// Here \a address can be either a decimal address + /// (ex: "192.168.1.56") or a network name (ex: "localhost"). + /// + /// \param address IP address or network name + /// + /// \return Address if provided argument was valid, otherwise `std::nullopt` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] static std::optional resolve(std::string_view address); + + //////////////////////////////////////////////////////////// + /// \brief Construct the address from 4 bytes + /// + /// Calling `IpAddress(a, b, c, d)` is equivalent to calling + /// `IpAddress::resolve("a.b.c.d")`, but safer as it doesn't + /// have to parse a string to get the address components. + /// + /// \param byte0 First byte of the address + /// \param byte1 Second byte of the address + /// \param byte2 Third byte of the address + /// \param byte3 Fourth byte of the address + /// + //////////////////////////////////////////////////////////// + IpAddress(std::uint8_t byte0, std::uint8_t byte1, std::uint8_t byte2, std::uint8_t byte3); + + //////////////////////////////////////////////////////////// + /// \brief Construct the address from a 32-bits integer + /// + /// This constructor uses the internal representation of + /// the address directly. It should be used for optimization + /// purposes, and only if you got that representation from + /// `IpAddress::toInteger()`. + /// + /// \param address 4 bytes of the address packed into a 32-bits integer + /// + /// \see `toInteger` + /// + //////////////////////////////////////////////////////////// + explicit IpAddress(std::uint32_t address); + + //////////////////////////////////////////////////////////// + /// \brief Get a string representation of the address + /// + /// The returned string is the decimal representation of the + /// IP address (like "192.168.1.56"), even if it was constructed + /// from a host name. + /// + /// \return String representation of the address + /// + /// \see `toInteger` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::string toString() const; + + //////////////////////////////////////////////////////////// + /// \brief Get an integer representation of the address + /// + /// The returned number is the internal representation of the + /// address, and should be used for optimization purposes only + /// (like sending the address through a socket). + /// The integer produced by this function can then be converted + /// back to a `sf::IpAddress` with the proper constructor. + /// + /// \return 32-bits unsigned integer representation of the address + /// + /// \see `toString` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::uint32_t toInteger() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the computer's local address + /// + /// The local address is the address of the computer from the + /// LAN point of view, i.e. something like 192.168.1.56. It is + /// meaningful only for communications over the local network. + /// Unlike getPublicAddress, this function is fast and may be + /// used safely anywhere. + /// + /// \return Local IP address of the computer on success, `std::nullopt` otherwise + /// + /// \see `getPublicAddress` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] static std::optional getLocalAddress(); + + //////////////////////////////////////////////////////////// + /// \brief Get the computer's public address + /// + /// The public address is the address of the computer from the + /// internet point of view, i.e. something like 89.54.1.169. + /// It is necessary for communications over the world wide web. + /// The only way to get a public address is to ask it to a + /// distant website; as a consequence, this function depends on + /// both your network connection and the server, and may be + /// very slow. You should use it as few as possible. Because + /// this function depends on the network connection and on a distant + /// server, you may use a time limit if you don't want your program + /// to be possibly stuck waiting in case there is a problem; this + /// limit is deactivated by default. + /// + /// \param timeout Maximum time to wait + /// + /// \return Public IP address of the computer on success, `std::nullopt` otherwise + /// + /// \see `getLocalAddress` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] static std::optional getPublicAddress(Time timeout = Time::Zero); + + //////////////////////////////////////////////////////////// + // Static member data + //////////////////////////////////////////////////////////// + // NOLINTBEGIN(readability-identifier-naming) + static const IpAddress Any; //!< Value representing any address (0.0.0.0) + static const IpAddress LocalHost; //!< The "localhost" address (for connecting a computer to itself locally) + static const IpAddress Broadcast; //!< The "broadcast" address (for sending UDP messages to everyone on a local network) + // NOLINTEND(readability-identifier-naming) + +private: + friend SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::uint32_t m_address; //!< Address stored as an unsigned 32 bit integer +}; + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator==` to compare two IP addresses +/// +/// \param left Left operand (a IP address) +/// \param right Right operand (a IP address) +/// +/// \return `true` if both addresses are equal +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_NETWORK_API bool operator==(IpAddress left, IpAddress right); + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator!=` to compare two IP addresses +/// +/// \param left Left operand (a IP address) +/// \param right Right operand (a IP address) +/// +/// \return `true` if both addresses are different +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_NETWORK_API bool operator!=(IpAddress left, IpAddress right); + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator<` to compare two IP addresses +/// +/// \param left Left operand (a IP address) +/// \param right Right operand (a IP address) +/// +/// \return `true` if `left` is lesser than `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right); + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator>` to compare two IP addresses +/// +/// \param left Left operand (a IP address) +/// \param right Right operand (a IP address) +/// +/// \return `true` if `left` is greater than `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_NETWORK_API bool operator>(IpAddress left, IpAddress right); + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator<=` to compare two IP addresses +/// +/// \param left Left operand (a IP address) +/// \param right Right operand (a IP address) +/// +/// \return `true` if \a left is lesser or equal than \a right +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_NETWORK_API bool operator<=(IpAddress left, IpAddress right); + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator>=` to compare two IP addresses +/// +/// \param left Left operand (a IP address) +/// \param right Right operand (a IP address) +/// +/// \return `true` if `left` is greater or equal than `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_NETWORK_API bool operator>=(IpAddress left, IpAddress right); + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator>>` to extract an IP address from an input stream +/// +/// \param stream Input stream +/// \param address IP address to extract +/// +/// \return Reference to the input stream +/// +//////////////////////////////////////////////////////////// +SFML_NETWORK_API std::istream& operator>>(std::istream& stream, std::optional& address); + +//////////////////////////////////////////////////////////// +/// \brief Overload of `operator<<` to print an IP address to an output stream +/// +/// \param stream Output stream +/// \param address IP address to print +/// +/// \return Reference to the output stream +/// +//////////////////////////////////////////////////////////// +SFML_NETWORK_API std::ostream& operator<<(std::ostream& stream, IpAddress address); + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::IpAddress +/// \ingroup network +/// +/// `sf::IpAddress` is a utility class for manipulating network +/// addresses. It provides a set a implicit constructors and +/// conversion functions to easily build or transform an IP +/// address from/to various representations. +/// +/// Usage example: +/// \code +/// auto a2 = sf::IpAddress::resolve("127.0.0.1"); // the local host address +/// auto a3 = sf::IpAddress::Broadcast; // the broadcast address +/// sf::IpAddress a4(192, 168, 1, 56); // a local address +/// auto a5 = sf::IpAddress::resolve("my_computer"); // a local address created from a network name +/// auto a6 = sf::IpAddress::resolve("89.54.1.169"); // a distant address +/// auto a7 = sf::IpAddress::resolve("www.google.com"); // a distant address created from a network name +/// auto a8 = sf::IpAddress::getLocalAddress(); // my address on the local network +/// auto a9 = sf::IpAddress::getPublicAddress(); // my address on the internet +/// \endcode +/// +/// Note that `sf::IpAddress` currently doesn't support IPv6 +/// nor other types of network addresses. +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/Packet.hpp b/third_party/sfml/include/SFML/Network/Packet.hpp new file mode 100644 index 00000000..2ba56dbb --- /dev/null +++ b/third_party/sfml/include/SFML/Network/Packet.hpp @@ -0,0 +1,552 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include + +#include +#include + + +namespace sf +{ +class String; + +//////////////////////////////////////////////////////////// +/// \brief Utility class to build blocks of data to transfer +/// over the network +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API Packet +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty packet. + /// + //////////////////////////////////////////////////////////// + Packet() = default; + + //////////////////////////////////////////////////////////// + /// \brief Virtual destructor + /// + //////////////////////////////////////////////////////////// + virtual ~Packet() = default; + + //////////////////////////////////////////////////////////// + /// \brief Copy constructor + /// + //////////////////////////////////////////////////////////// + Packet(const Packet&) = default; + + //////////////////////////////////////////////////////////// + /// \brief Copy assignment + /// + //////////////////////////////////////////////////////////// + Packet& operator=(const Packet&) = default; + + //////////////////////////////////////////////////////////// + /// \brief Move constructor + /// + //////////////////////////////////////////////////////////// + Packet(Packet&&) noexcept = default; + + //////////////////////////////////////////////////////////// + /// \brief Move assignment + /// + //////////////////////////////////////////////////////////// + Packet& operator=(Packet&&) noexcept = default; + + //////////////////////////////////////////////////////////// + /// \brief Append data to the end of the packet + /// + /// \param data Pointer to the sequence of bytes to append + /// \param sizeInBytes Number of bytes to append + /// + /// \see `clear` + /// \see `getReadPosition` + /// + //////////////////////////////////////////////////////////// + void append(const void* data, std::size_t sizeInBytes); + + //////////////////////////////////////////////////////////// + /// \brief Get the current reading position in the packet + /// + /// The next read operation will read data from this position + /// + /// \return The byte offset of the current read position + /// + /// \see `append` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::size_t getReadPosition() const; + + //////////////////////////////////////////////////////////// + /// \brief Clear the packet + /// + /// After calling Clear, the packet is empty. + /// + /// \see `append` + /// + //////////////////////////////////////////////////////////// + void clear(); + + //////////////////////////////////////////////////////////// + /// \brief Get a pointer to the data contained in the packet + /// + /// Warning: the returned pointer may become invalid after + /// you append data to the packet, therefore it should never + /// be stored. + /// The return pointer is a `nullptr` if the packet is empty. + /// + /// \return Pointer to the data + /// + /// \see `getDataSize` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] const void* getData() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the size of the data contained in the packet + /// + /// This function returns the number of bytes pointed to by + /// what `getData` returns. + /// + /// \return Data size, in bytes + /// + /// \see `getData` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::size_t getDataSize() const; + + //////////////////////////////////////////////////////////// + /// \brief Tell if the reading position has reached the + /// end of the packet + /// + /// This function is useful to know if there is some data + /// left to be read, without actually reading it. + /// + /// \return `true` if all data was read, `false` otherwise + /// + /// \see `operator` bool + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool endOfPacket() const; + + //////////////////////////////////////////////////////////// + /// \brief Test the validity of the packet, for reading + /// + /// This operator allows to test the packet as a boolean + /// variable, to check if a reading operation was successful. + /// + /// A packet will be in an invalid state if it has no more + /// data to read. + /// + /// This behavior is the same as standard C++ streams. + /// + /// Usage example: + /// \code + /// float x; + /// packet >> x; + /// if (packet) + /// { + /// // ok, x was extracted successfully + /// } + /// + /// // -- or -- + /// + /// float x; + /// if (packet >> x) + /// { + /// // ok, x was extracted successfully + /// } + /// \endcode + /// + /// Don't focus on the return type, it's equivalent to bool but + /// it disallows unwanted implicit conversions to integer or + /// pointer types. + /// + /// \return `true` if last data extraction from packet was successful + /// + /// \see `endOfPacket` + /// + //////////////////////////////////////////////////////////// + explicit operator bool() const; + + //////////////////////////////////////////////////////////// + /// Overload of `operator>>` to read data from the packet + /// + //////////////////////////////////////////////////////////// + Packet& operator>>(bool& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::int8_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::uint8_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::int16_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::uint16_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::int32_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::uint32_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::int64_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::uint64_t& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(float& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(double& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(char* data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::string& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(wchar_t* data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(std::wstring& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator>>(String& data); + + //////////////////////////////////////////////////////////// + /// Overload of `operator<<` to write data into the packet + /// + //////////////////////////////////////////////////////////// + Packet& operator<<(bool data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::int8_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::uint8_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::int16_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::uint16_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::int32_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::uint32_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::int64_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(std::uint64_t data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(float data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(double data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(const char* data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(const std::string& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(const wchar_t* data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(const std::wstring& data); + + //////////////////////////////////////////////////////////// + /// \overload + //////////////////////////////////////////////////////////// + Packet& operator<<(const String& data); + +protected: + friend class TcpSocket; + friend class UdpSocket; + + //////////////////////////////////////////////////////////// + /// \brief Called before the packet is sent over the network + /// + /// This function can be defined by derived classes to + /// transform the data before it is sent; this can be + /// used for compression, encryption, etc. + /// The function must return a pointer to the modified data, + /// as well as the number of bytes pointed. + /// The default implementation provides the packet's data + /// without transforming it. + /// + /// \param size Variable to fill with the size of data to send + /// + /// \return Pointer to the array of bytes to send + /// + /// \see `onReceive` + /// + //////////////////////////////////////////////////////////// + virtual const void* onSend(std::size_t& size); + + //////////////////////////////////////////////////////////// + /// \brief Called after the packet is received over the network + /// + /// This function can be defined by derived classes to + /// transform the data after it is received; this can be + /// used for decompression, decryption, etc. + /// The function receives a pointer to the received data, + /// and must fill the packet with the transformed bytes. + /// The default implementation fills the packet directly + /// without transforming the data. + /// + /// \param data Pointer to the received bytes + /// \param size Number of bytes + /// + /// \see `onSend` + /// + //////////////////////////////////////////////////////////// + virtual void onReceive(const void* data, std::size_t size); + +private: + //////////////////////////////////////////////////////////// + /// \brief Check if the packet can extract a given number of bytes + /// + /// This function updates accordingly the state of the packet. + /// + /// \param size Size to check + /// + /// \return `true` if \a size bytes can be read from the packet + /// + //////////////////////////////////////////////////////////// + bool checkSize(std::size_t size); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::vector m_data; //!< Data stored in the packet + std::size_t m_readPos{}; //!< Current reading position in the packet + std::size_t m_sendPos{}; //!< Current send position in the packet (for handling partial sends) + bool m_isValid{true}; //!< Reading state of the packet +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::Packet +/// \ingroup network +/// +/// Packets provide a safe and easy way to serialize data, +/// in order to send it over the network using sockets +/// (`sf::TcpSocket`, `sf::UdpSocket`). +/// +/// Packets solve 2 fundamental problems that arise when +/// transferring data over the network: +/// \li data is interpreted correctly according to the endianness +/// \li the bounds of the packet are preserved (one send == one receive) +/// +/// The `sf::Packet` class provides both input and output modes. +/// It is designed to follow the behavior of standard C++ streams, +/// using operators >> and << to extract and insert data. +/// +/// It is recommended to use only fixed-size types (like `std::int32_t`, etc.), +/// to avoid possible differences between the sender and the receiver. +/// Indeed, the native C++ types may have different sizes on two platforms +/// and your data may be corrupted if that happens. +/// +/// Usage example: +/// \code +/// std::uint32_t x = 24; +/// std::string s = "hello"; +/// double d = 5.89; +/// +/// // Group the variables to send into a packet +/// sf::Packet packet; +/// packet << x << s << d; +/// +/// // Send it over the network (socket is a valid sf::TcpSocket) +/// socket.send(packet); +/// +/// ----------------------------------------------------------------- +/// +/// // Receive the packet at the other end +/// sf::Packet packet; +/// socket.receive(packet); +/// +/// // Extract the variables contained in the packet +/// std::uint32_t x; +/// std::string s; +/// double d; +/// if (packet >> x >> s >> d) +/// { +/// // Data extracted successfully... +/// } +/// \endcode +/// +/// Packets have built-in `operator>>` and << overloads for +/// standard types: +/// \li `bool` +/// \li fixed-size integer types (`int[8|16|32]_t`, `uint[8|16|32]_t`) +/// \li floating point numbers (`float`, `double`) +/// \li string types (`char*`, `wchar_t*`, `std::string`, `std::wstring`, `sf::String`) +/// +/// Like standard streams, it is also possible to define your own +/// overloads of operators >> and << in order to handle your +/// custom types. +/// +/// \code +/// struct MyStruct +/// { +/// float number{}; +/// std::int8_t integer{}; +/// std::string str; +/// }; +/// +/// sf::Packet& operator <<(sf::Packet& packet, const MyStruct& m) +/// { +/// return packet << m.number << m.integer << m.str; +/// } +/// +/// sf::Packet& operator >>(sf::Packet& packet, MyStruct& m) +/// { +/// return packet >> m.number >> m.integer >> m.str; +/// } +/// \endcode +/// +/// Packets also provide an extra feature that allows to apply +/// custom transformations to the data before it is sent, +/// and after it is received. This is typically used to +/// handle automatic compression or encryption of the data. +/// This is achieved by inheriting from `sf::Packet`, and overriding +/// the onSend and onReceive functions. +/// +/// Here is an example: +/// \code +/// class ZipPacket : public sf::Packet +/// { +/// const void* onSend(std::size_t& size) override +/// { +/// const void* srcData = getData(); +/// std::size_t srcSize = getDataSize(); +/// +/// return MySuperZipFunction(srcData, srcSize, &size); +/// } +/// +/// void onReceive(const void* data, std::size_t size) override +/// { +/// std::size_t dstSize; +/// const void* dstData = MySuperUnzipFunction(data, size, &dstSize); +/// +/// append(dstData, dstSize); +/// } +/// }; +/// +/// // Use like regular packets: +/// ZipPacket packet; +/// packet << x << s << d; +/// ... +/// \endcode +/// +/// \see `sf::TcpSocket`, `sf::UdpSocket` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/Socket.hpp b/third_party/sfml/include/SFML/Network/Socket.hpp new file mode 100644 index 00000000..2b7b79fa --- /dev/null +++ b/third_party/sfml/include/SFML/Network/Socket.hpp @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Base class for all the socket types +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API Socket +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Status codes that may be returned by socket functions + /// + //////////////////////////////////////////////////////////// + enum class Status + { + Done, //!< The socket has sent / received the data + NotReady, //!< The socket is not ready to send / receive data yet + Partial, //!< The socket sent a part of the data + Disconnected, //!< The TCP socket has been disconnected + Error //!< An unexpected error happened + }; + + //////////////////////////////////////////////////////////// + /// \brief Some special values used by sockets + /// + //////////////////////////////////////////////////////////// + // NOLINTNEXTLINE(readability-identifier-naming) + static constexpr unsigned short AnyPort{0}; //!< Special value that tells the system to pick any available port + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~Socket(); + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + //////////////////////////////////////////////////////////// + Socket(const Socket&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy assignment + /// + //////////////////////////////////////////////////////////// + Socket& operator=(const Socket&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Move constructor + /// + //////////////////////////////////////////////////////////// + Socket(Socket&& socket) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Move assignment + /// + //////////////////////////////////////////////////////////// + Socket& operator=(Socket&& socket) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Set the blocking state of the socket + /// + /// In blocking mode, calls will not return until they have + /// completed their task. For example, a call to Receive in + /// blocking mode won't return until some data was actually + /// received. + /// In non-blocking mode, calls will always return immediately, + /// using the return code to signal whether there was data + /// available or not. + /// By default, all sockets are blocking. + /// + /// \param blocking `true` to set the socket as blocking, `false` for non-blocking + /// + /// \see `isBlocking` + /// + //////////////////////////////////////////////////////////// + void setBlocking(bool blocking); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the socket is in blocking or non-blocking mode + /// + /// \return `true` if the socket is blocking, `false` otherwise + /// + /// \see `setBlocking` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool isBlocking() const; + +protected: + //////////////////////////////////////////////////////////// + /// \brief Types of protocols that the socket can use + /// + //////////////////////////////////////////////////////////// + enum class Type + { + Tcp, //!< TCP protocol + Udp //!< UDP protocol + }; + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// This constructor can only be accessed by derived classes. + /// + /// \param type Type of the socket (TCP or UDP) + /// + //////////////////////////////////////////////////////////// + explicit Socket(Type type); + + //////////////////////////////////////////////////////////// + /// \brief Return the internal handle of the socket + /// + /// The returned handle may be invalid if the socket + /// was not created yet (or already destroyed). + /// This function can only be accessed by derived classes. + /// + /// \return The internal (OS-specific) handle of the socket + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SocketHandle getNativeHandle() const; + + //////////////////////////////////////////////////////////// + /// \brief Create the internal representation of the socket + /// + /// This function can only be accessed by derived classes. + /// + //////////////////////////////////////////////////////////// + void create(); + + //////////////////////////////////////////////////////////// + /// \brief Create the internal representation of the socket + /// from a socket handle + /// + /// This function can only be accessed by derived classes. + /// + /// \param handle OS-specific handle of the socket to wrap + /// + //////////////////////////////////////////////////////////// + void create(SocketHandle handle); + + //////////////////////////////////////////////////////////// + /// \brief Close the socket gracefully + /// + /// This function can only be accessed by derived classes. + /// + //////////////////////////////////////////////////////////// + void close(); + +private: + friend class SocketSelector; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Type m_type; //!< Type of the socket (TCP or UDP) + SocketHandle m_socket; //!< Socket descriptor + bool m_isBlocking{true}; //!< Current blocking mode of the socket +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::Socket +/// \ingroup network +/// +/// This class mainly defines internal stuff to be used by +/// derived classes. +/// +/// The only public features that it defines, and which +/// is therefore common to all the socket classes, is the +/// blocking state. All sockets can be set as blocking or +/// non-blocking. +/// +/// In blocking mode, socket functions will hang until +/// the operation completes, which means that the entire +/// program (well, in fact the current thread if you use +/// multiple ones) will be stuck waiting for your socket +/// operation to complete. +/// +/// In non-blocking mode, all the socket functions will +/// return immediately. If the socket is not ready to complete +/// the requested operation, the function simply returns +/// the proper status code (`Socket::Status::NotReady`). +/// +/// The default mode, which is blocking, is the one that is +/// generally used, in combination with threads or selectors. +/// The non-blocking mode is rather used in real-time +/// applications that run an endless loop that can poll +/// the socket often enough, and cannot afford blocking +/// this loop. +/// +/// \see `sf::TcpListener`, `sf::TcpSocket`, `sf::UdpSocket` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/SocketHandle.hpp b/third_party/sfml/include/SFML/Network/SocketHandle.hpp new file mode 100644 index 00000000..70250693 --- /dev/null +++ b/third_party/sfml/include/SFML/Network/SocketHandle.hpp @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#if defined(SFML_SYSTEM_WINDOWS) +#include +#endif + + +namespace sf +{ +//////////////////////////////////////////////////////////// +// Low-level socket handle type, specific to each platform +//////////////////////////////////////////////////////////// +#if defined(SFML_SYSTEM_WINDOWS) + +using SocketHandle = UINT_PTR; + +#else + +using SocketHandle = int; + +#endif + +} // namespace sf diff --git a/third_party/sfml/include/SFML/Network/SocketSelector.hpp b/third_party/sfml/include/SFML/Network/SocketSelector.hpp new file mode 100644 index 00000000..ac80a870 --- /dev/null +++ b/third_party/sfml/include/SFML/Network/SocketSelector.hpp @@ -0,0 +1,273 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + + +namespace sf +{ +class Socket; + +//////////////////////////////////////////////////////////// +/// \brief Multiplexer that allows to read from multiple sockets +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API SocketSelector +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + SocketSelector(); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~SocketSelector(); + + //////////////////////////////////////////////////////////// + /// \brief Copy constructor + /// + /// \param copy Instance to copy + /// + //////////////////////////////////////////////////////////// + SocketSelector(const SocketSelector& copy); + + //////////////////////////////////////////////////////////// + /// \brief Overload of assignment operator + /// + /// \param right Instance to assign + /// + /// \return Reference to self + /// + //////////////////////////////////////////////////////////// + SocketSelector& operator=(const SocketSelector& right); + + //////////////////////////////////////////////////////////// + /// \brief Move constructor + /// + //////////////////////////////////////////////////////////// + SocketSelector(SocketSelector&&) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Move assignment + /// + //////////////////////////////////////////////////////////// + SocketSelector& operator=(SocketSelector&&) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Add a new socket to the selector + /// + /// This function keeps a weak reference to the socket, + /// so you have to make sure that the socket is not destroyed + /// while it is stored in the selector. + /// This function does nothing if the socket is not valid. + /// + /// \param socket Reference to the socket to add + /// + /// \see `remove`, `clear` + /// + //////////////////////////////////////////////////////////// + void add(Socket& socket); + + //////////////////////////////////////////////////////////// + /// \brief Remove a socket from the selector + /// + /// This function doesn't destroy the socket, it simply + /// removes the reference that the selector has to it. + /// + /// \param socket Reference to the socket to remove + /// + /// \see `add`, `clear` + /// + //////////////////////////////////////////////////////////// + void remove(Socket& socket); + + //////////////////////////////////////////////////////////// + /// \brief Remove all the sockets stored in the selector + /// + /// This function doesn't destroy any instance, it simply + /// removes all the references that the selector has to + /// external sockets. + /// + /// \see `add`, `remove` + /// + //////////////////////////////////////////////////////////// + void clear(); + + //////////////////////////////////////////////////////////// + /// \brief Wait until one or more sockets are ready to receive + /// + /// This function returns as soon as at least one socket has + /// some data available to be received. To know which sockets are + /// ready, use the `isReady` function. + /// If you use a timeout and no socket is ready before the timeout + /// is over, the function returns `false`. + /// + /// \param timeout Maximum time to wait, (use Time::Zero for infinity) + /// + /// \return `true` if there are sockets ready, `false` otherwise + /// + /// \see `isReady` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool wait(Time timeout = Time::Zero); + + //////////////////////////////////////////////////////////// + /// \brief Test a socket to know if it is ready to receive data + /// + /// This function must be used after a call to Wait, to know + /// which sockets are ready to receive data. If a socket is + /// ready, a call to receive will never block because we know + /// that there is data available to read. + /// Note that if this function returns `true` for a TcpListener, + /// this means that it is ready to accept a new connection. + /// + /// \param socket Socket to test + /// + /// \return `true` if the socket is ready to read, `false` otherwise + /// + /// \see `isReady` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool isReady(Socket& socket) const; + +private: + struct SocketSelectorImpl; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::unique_ptr m_impl; //!< Opaque pointer to the implementation (which requires OS-specific types) +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::SocketSelector +/// \ingroup network +/// +/// Socket selectors provide a way to wait until some data is +/// available on a set of sockets, instead of just one. This +/// is convenient when you have multiple sockets that may +/// possibly receive data, but you don't know which one will +/// be ready first. In particular, it avoids to use a thread +/// for each socket; with selectors, a single thread can handle +/// all the sockets. +/// +/// All types of sockets can be used in a selector: +/// \li `sf::TcpListener` +/// \li `sf::TcpSocket` +/// \li `sf::UdpSocket` +/// +/// A selector doesn't store its own copies of the sockets +/// (socket classes are not copyable anyway), it simply keeps +/// a reference to the original sockets that you pass to the +/// "add" function. Therefore, you can't use the selector as a +/// socket container, you must store them outside and make sure +/// that they are alive as long as they are used in the selector. +/// +/// Using a selector is simple: +/// \li populate the selector with all the sockets that you want to observe +/// \li make it wait until there is data available on any of the sockets +/// \li test each socket to find out which ones are ready +/// +/// Usage example: +/// \code +/// // Create a socket to listen to new connections +/// sf::TcpListener listener; +/// if (listener.listen(55001) != sf::Socket::Status::Done) +/// { +/// // Handle error... +/// } +/// +/// // Create a list to store the future clients +/// std::vector clients; +/// +/// // Create a selector +/// sf::SocketSelector selector; +/// +/// // Add the listener to the selector +/// selector.add(listener); +/// +/// // Endless loop that waits for new connections +/// while (running) +/// { +/// // Make the selector wait for data on any socket +/// if (selector.wait()) +/// { +/// // Test the listener +/// if (selector.isReady(listener)) +/// { +/// // The listener is ready: there is a pending connection +/// sf::TcpSocket client; +/// if (listener.accept(client) == sf::Socket::Status::Done) +/// { +/// // Add the new client to the selector so that we will +/// // be notified when they send something +/// selector.add(client); +/// +/// // Add the new client to the clients list +/// clients.push_back(std::move(client)); +/// } +/// else +/// { +/// // Handle error... +/// } +/// } +/// else +/// { +/// // The listener socket is not ready, test all other sockets (the clients) +/// for (sf::TcpSocket& client : clients) +/// { +/// if (selector.isReady(client)) +/// { +/// // The client has sent some data, we can receive it +/// sf::Packet packet; +/// if (client.receive(packet) == sf::Socket::Status::Done) +/// { +/// ... +/// } +/// } +/// } +/// } +/// } +/// } +/// \endcode +/// +/// \see `sf::Socket` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/TcpListener.hpp b/third_party/sfml/include/SFML/Network/TcpListener.hpp new file mode 100644 index 00000000..54cd6e60 --- /dev/null +++ b/third_party/sfml/include/SFML/Network/TcpListener.hpp @@ -0,0 +1,166 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include + + +namespace sf +{ +class TcpSocket; + +//////////////////////////////////////////////////////////// +/// \brief Socket that listens to new TCP connections +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API TcpListener : public Socket +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + TcpListener(); + + //////////////////////////////////////////////////////////// + /// \brief Get the port to which the socket is bound locally + /// + /// If the socket is not listening to a port, this function + /// returns 0. + /// + /// \return Port to which the socket is bound + /// + /// \see `listen` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] unsigned short getLocalPort() const; + + //////////////////////////////////////////////////////////// + /// \brief Start listening for incoming connection attempts + /// + /// This function makes the socket start listening on the + /// specified port, waiting for incoming connection attempts. + /// + /// If the socket is already listening on a port when this + /// function is called, it will stop listening on the old + /// port before starting to listen on the new port. + /// + /// When providing `sf::Socket::AnyPort` as port, the listener + /// will request an available port from the system. + /// The chosen port can be retrieved by calling `getLocalPort()`. + /// + /// \param port Port to listen on for incoming connection attempts + /// \param address Address of the interface to listen on + /// + /// \return Status code + /// + /// \see `accept`, `close` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status listen(unsigned short port, IpAddress address = IpAddress::Any); + + //////////////////////////////////////////////////////////// + /// \brief Stop listening and close the socket + /// + /// This function gracefully stops the listener. If the + /// socket is not listening, this function has no effect. + /// + /// \see `listen` + /// + //////////////////////////////////////////////////////////// + void close(); + + //////////////////////////////////////////////////////////// + /// \brief Accept a new connection + /// + /// If the socket is in blocking mode, this function will + /// not return until a connection is actually received. + /// + /// \param socket Socket that will hold the new connection + /// + /// \return Status code + /// + /// \see `listen` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status accept(TcpSocket& socket); +}; + + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::TcpListener +/// \ingroup network +/// +/// A listener socket is a special type of socket that listens to +/// a given port and waits for connections on that port. +/// This is all it can do. +/// +/// When a new connection is received, you must call accept and +/// the listener returns a new instance of `sf::TcpSocket` that +/// is properly initialized and can be used to communicate with +/// the new client. +/// +/// Listener sockets are specific to the TCP protocol, +/// UDP sockets are connectionless and can therefore communicate +/// directly. As a consequence, a listener socket will always +/// return the new connections as `sf::TcpSocket` instances. +/// +/// A listener is automatically closed on destruction, like all +/// other types of socket. However if you want to stop listening +/// before the socket is destroyed, you can call its `close()` +/// function. +/// +/// Usage example: +/// \code +/// // Create a listener socket and make it wait for new +/// // connections on port 55001 +/// sf::TcpListener listener; +/// listener.listen(55001); +/// +/// // Endless loop that waits for new connections +/// while (running) +/// { +/// sf::TcpSocket client; +/// if (listener.accept(client) == sf::Socket::Done) +/// { +/// // A new client just connected! +/// std::cout << "New connection received from " << client.getRemoteAddress().value() << std::endl; +/// doSomethingWith(client); +/// } +/// } +/// \endcode +/// +/// \see `sf::TcpSocket`, `sf::Socket` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/TcpSocket.hpp b/third_party/sfml/include/SFML/Network/TcpSocket.hpp new file mode 100644 index 00000000..05df0d60 --- /dev/null +++ b/third_party/sfml/include/SFML/Network/TcpSocket.hpp @@ -0,0 +1,317 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + +#include +#include + +#include +#include + + +namespace sf +{ +class TcpListener; +class IpAddress; +class Packet; + +//////////////////////////////////////////////////////////// +/// \brief Specialized socket using the TCP protocol +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API TcpSocket : public Socket +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + TcpSocket(); + + //////////////////////////////////////////////////////////// + /// \brief Get the port to which the socket is bound locally + /// + /// If the socket is not connected, this function returns 0. + /// + /// \return Port to which the socket is bound + /// + /// \see `connect`, `getRemotePort` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] unsigned short getLocalPort() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the address of the connected peer + /// + /// If the socket is not connected, this function returns + /// an unset optional. + /// + /// \return Address of the remote peer + /// + /// \see `getRemotePort` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::optional getRemoteAddress() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the port of the connected peer to which + /// the socket is connected + /// + /// If the socket is not connected, this function returns 0. + /// + /// \return Remote port to which the socket is connected + /// + /// \see `getRemoteAddress` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] unsigned short getRemotePort() const; + + //////////////////////////////////////////////////////////// + /// \brief Connect the socket to a remote peer + /// + /// In blocking mode, this function may take a while, especially + /// if the remote peer is not reachable. The last parameter allows + /// you to stop trying to connect after a given timeout. + /// If the socket is already connected, the connection is + /// forcibly disconnected before attempting to connect again. + /// + /// \param remoteAddress Address of the remote peer + /// \param remotePort Port of the remote peer + /// \param timeout Optional maximum time to wait + /// + /// \return Status code + /// + /// \see `disconnect` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status connect(IpAddress remoteAddress, unsigned short remotePort, Time timeout = Time::Zero); + + //////////////////////////////////////////////////////////// + /// \brief Disconnect the socket from its remote peer + /// + /// This function gracefully closes the connection. If the + /// socket is not connected, this function has no effect. + /// + /// \see `connect` + /// + //////////////////////////////////////////////////////////// + void disconnect(); + + //////////////////////////////////////////////////////////// + /// \brief Send raw data to the remote peer + /// + /// To be able to handle partial sends over non-blocking + /// sockets, use the `send(const void*, std::size_t, std::size_t&)` + /// overload instead. + /// This function will fail if the socket is not connected. + /// + /// \param data Pointer to the sequence of bytes to send + /// \param size Number of bytes to send + /// + /// \return Status code + /// + /// \see `receive` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status send(const void* data, std::size_t size); + + //////////////////////////////////////////////////////////// + /// \brief Send raw data to the remote peer + /// + /// This function will fail if the socket is not connected. + /// + /// \param data Pointer to the sequence of bytes to send + /// \param size Number of bytes to send + /// \param sent The number of bytes sent will be written here + /// + /// \return Status code + /// + /// \see `receive` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status send(const void* data, std::size_t size, std::size_t& sent); + + //////////////////////////////////////////////////////////// + /// \brief Receive raw data from the remote peer + /// + /// In blocking mode, this function will wait until some + /// bytes are actually received. + /// This function will fail if the socket is not connected. + /// + /// \param data Pointer to the array to fill with the received bytes + /// \param size Maximum number of bytes that can be received + /// \param received This variable is filled with the actual number of bytes received + /// + /// \return Status code + /// + /// \see `send` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status receive(void* data, std::size_t size, std::size_t& received); + + //////////////////////////////////////////////////////////// + /// \brief Send a formatted packet of data to the remote peer + /// + /// In non-blocking mode, if this function returns `sf::Socket::Status::Partial`, + /// you \em must retry sending the same unmodified packet before sending + /// anything else in order to guarantee the packet arrives at the remote + /// peer uncorrupted. + /// This function will fail if the socket is not connected. + /// + /// \param packet Packet to send + /// + /// \return Status code + /// + /// \see `receive` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status send(Packet& packet); + + //////////////////////////////////////////////////////////// + /// \brief Receive a formatted packet of data from the remote peer + /// + /// In blocking mode, this function will wait until the whole packet + /// has been received. + /// This function will fail if the socket is not connected. + /// + /// \param packet Packet to fill with the received data + /// + /// \return Status code + /// + /// \see `send` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status receive(Packet& packet); + +private: + friend class TcpListener; + + //////////////////////////////////////////////////////////// + /// \brief Structure holding the data of a pending packet + /// + //////////////////////////////////////////////////////////// + struct PendingPacket + { + std::uint32_t size{}; //!< Data of packet size + std::size_t sizeReceived{}; //!< Number of size bytes received so far + std::vector data; //!< Data of the packet + }; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + PendingPacket m_pendingPacket; //!< Temporary data of the packet currently being received + std::vector m_blockToSendBuffer; //!< Buffer used to prepare data being sent from the socket +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::TcpSocket +/// \ingroup network +/// +/// TCP is a connected protocol, which means that a TCP +/// socket can only communicate with the host it is connected +/// to. It can't send or receive anything if it is not connected. +/// +/// The TCP protocol is reliable but adds a slight overhead. +/// It ensures that your data will always be received in order +/// and without errors (no data corrupted, lost or duplicated). +/// +/// When a socket is connected to a remote host, you can +/// retrieve information about this host with the +/// `getRemoteAddress` and `getRemotePort` functions. You can +/// also get the local port to which the socket is bound +/// (which is automatically chosen when the socket is connected), +/// with the getLocalPort function. +/// +/// Sending and receiving data can use either the low-level +/// or the high-level functions. The low-level functions +/// process a raw sequence of bytes, and cannot ensure that +/// one call to Send will exactly match one call to Receive +/// at the other end of the socket. +/// +/// The high-level interface uses packets (see `sf::Packet`), +/// which are easier to use and provide more safety regarding +/// the data that is exchanged. You can look at the `sf::Packet` +/// class to get more details about how they work. +/// +/// The socket is automatically disconnected when it is destroyed, +/// but if you want to explicitly close the connection while +/// the socket instance is still alive, you can call disconnect. +/// +/// Usage example: +/// \code +/// // ----- The client ----- +/// +/// // Create a socket and connect it to 192.168.1.50 on port 55001 +/// sf::TcpSocket socket; +/// socket.connect("192.168.1.50", 55001); +/// +/// // Send a message to the connected host +/// std::string message = "Hi, I am a client"; +/// socket.send(message.c_str(), message.size() + 1); +/// +/// // Receive an answer from the server +/// std::array buffer; +/// std::size_t received = 0; +/// socket.receive(buffer.data(), buffer.size(), received); +/// std::cout << "The server said: " << buffer.data() << std::endl; +/// +/// // ----- The server ----- +/// +/// // Create a listener to wait for incoming connections on port 55001 +/// sf::TcpListener listener; +/// listener.listen(55001); +/// +/// // Wait for a connection +/// sf::TcpSocket socket; +/// listener.accept(socket); +/// std::cout << "New client connected: " << socket.getRemoteAddress().value() << std::endl; +/// +/// // Receive a message from the client +/// std::array buffer; +/// std::size_t received = 0; +/// socket.receive(buffer.data(), buffer.size(), received); +/// std::cout << "The client said: " << buffer.data() << std::endl; +/// +/// // Send an answer +/// std::string message = "Welcome, client"; +/// socket.send(message.c_str(), message.size() + 1); +/// \endcode +/// +/// \see `sf::Socket`, `sf::UdpSocket`, `sf::Packet` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/Network/UdpSocket.hpp b/third_party/sfml/include/SFML/Network/UdpSocket.hpp new file mode 100644 index 00000000..09505611 --- /dev/null +++ b/third_party/sfml/include/SFML/Network/UdpSocket.hpp @@ -0,0 +1,293 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include + +#include +#include + +#include + + +namespace sf +{ +class Packet; + +//////////////////////////////////////////////////////////// +/// \brief Specialized socket using the UDP protocol +/// +//////////////////////////////////////////////////////////// +class SFML_NETWORK_API UdpSocket : public Socket +{ +public: + //////////////////////////////////////////////////////////// + // Constants + //////////////////////////////////////////////////////////// + // NOLINTNEXTLINE(readability-identifier-naming) + static constexpr std::size_t MaxDatagramSize{65507}; //!< The maximum number of bytes that can be sent in a single UDP datagram + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + UdpSocket(); + + //////////////////////////////////////////////////////////// + /// \brief Get the port to which the socket is bound locally + /// + /// If the socket is not bound to a port, this function + /// returns 0. + /// + /// \return Port to which the socket is bound + /// + /// \see `bind` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] unsigned short getLocalPort() const; + + //////////////////////////////////////////////////////////// + /// \brief Bind the socket to a specific port + /// + /// Binding the socket to a port is necessary for being + /// able to receive data on that port. + /// + /// When providing `sf::Socket::AnyPort` as port, the listener + /// will request an available port from the system. + /// The chosen port can be retrieved by calling `getLocalPort()`. + /// + /// Since the socket can only be bound to a single port at + /// any given moment, if it is already bound when this + /// function is called, it will be unbound from the previous + /// port before being bound to the new one. + /// + /// \param port Port to bind the socket to + /// \param address Address of the interface to bind to + /// + /// \return Status code + /// + /// \see `unbind`, `getLocalPort` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status bind(unsigned short port, IpAddress address = IpAddress::Any); + + //////////////////////////////////////////////////////////// + /// \brief Unbind the socket from the local port to which it is bound + /// + /// The port that the socket was previously bound to is immediately + /// made available to the operating system after this function is called. + /// This means that a subsequent call to `bind()` will be able to re-bind + /// the port if no other process has done so in the mean time. + /// If the socket is not bound to a port, this function has no effect. + /// + /// \see `bind` + /// + //////////////////////////////////////////////////////////// + void unbind(); + + //////////////////////////////////////////////////////////// + /// \brief Send raw data to a remote peer + /// + /// Make sure that `size` is not greater than + /// `UdpSocket::MaxDatagramSize`, otherwise this function will + /// fail and no data will be sent. + /// + /// \param data Pointer to the sequence of bytes to send + /// \param size Number of bytes to send + /// \param remoteAddress Address of the receiver + /// \param remotePort Port of the receiver to send the data to + /// + /// \return Status code + /// + /// \see `receive` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status send(const void* data, std::size_t size, IpAddress remoteAddress, unsigned short remotePort); + + //////////////////////////////////////////////////////////// + /// \brief Receive raw data from a remote peer + /// + /// In blocking mode, this function will wait until some + /// bytes are actually received. + /// Be careful to use a buffer which is large enough for + /// the data that you intend to receive, if it is too small + /// then an error will be returned and *all* the data will + /// be lost. + /// + /// \param data Pointer to the array to fill with the received bytes + /// \param size Maximum number of bytes that can be received + /// \param received This variable is filled with the actual number of bytes received + /// \param remoteAddress Address of the peer that sent the data + /// \param remotePort Port of the peer that sent the data + /// + /// \return Status code + /// + /// \see `send` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status receive(void* data, + std::size_t size, + std::size_t& received, + std::optional& remoteAddress, + unsigned short& remotePort); + + //////////////////////////////////////////////////////////// + /// \brief Send a formatted packet of data to a remote peer + /// + /// Make sure that the packet size is not greater than + /// `UdpSocket::MaxDatagramSize`, otherwise this function will + /// fail and no data will be sent. + /// + /// \param packet Packet to send + /// \param remoteAddress Address of the receiver + /// \param remotePort Port of the receiver to send the data to + /// + /// \return Status code + /// + /// \see `receive` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status send(Packet& packet, IpAddress remoteAddress, unsigned short remotePort); + + //////////////////////////////////////////////////////////// + /// \brief Receive a formatted packet of data from a remote peer + /// + /// In blocking mode, this function will wait until the whole packet + /// has been received. + /// + /// \param packet Packet to fill with the received data + /// \param remoteAddress Address of the peer that sent the data + /// \param remotePort Port of the peer that sent the data + /// + /// \return Status code + /// + /// \see `send` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Status receive(Packet& packet, std::optional& remoteAddress, unsigned short& remotePort); + +private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::vector m_buffer{MaxDatagramSize}; //!< Temporary buffer holding the received data in Receive(Packet) +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::UdpSocket +/// \ingroup network +/// +/// A UDP socket is a connectionless socket. Instead of +/// connecting once to a remote host, like TCP sockets, +/// it can send to and receive from any host at any time. +/// +/// It is a datagram protocol: bounded blocks of data (datagrams) +/// are transferred over the network rather than a continuous +/// stream of data (TCP). Therefore, one call to send will always +/// match one call to receive (if the datagram is not lost), +/// with the same data that was sent. +/// +/// The UDP protocol is lightweight but unreliable. Unreliable +/// means that datagrams may be duplicated, be lost or +/// arrive reordered. However, if a datagram arrives, its +/// data is guaranteed to be valid. +/// +/// UDP is generally used for real-time communication +/// (audio or video streaming, real-time games, etc.) where +/// speed is crucial and lost data doesn't matter much. +/// +/// Sending and receiving data can use either the low-level +/// or the high-level functions. The low-level functions +/// process a raw sequence of bytes, whereas the high-level +/// interface uses packets (see `sf::Packet`), which are easier +/// to use and provide more safety regarding the data that is +/// exchanged. You can look at the `sf::Packet` class to get +/// more details about how they work. +/// +/// It is important to note that `UdpSocket` is unable to send +/// datagrams bigger than `MaxDatagramSize`. In this case, it +/// returns an error and doesn't send anything. This applies +/// to both raw data and packets. Indeed, even packets are +/// unable to split and recompose data, due to the unreliability +/// of the protocol (dropped, mixed or duplicated datagrams may +/// lead to a big mess when trying to recompose a packet). +/// +/// If the socket is bound to a port, it is automatically +/// unbound from it when the socket is destroyed. However, +/// you can unbind the socket explicitly with the Unbind +/// function if necessary, to stop receiving messages or +/// make the port available for other sockets. +/// +/// Usage example: +/// \code +/// // ----- The client ----- +/// +/// // Create a socket and bind it to the port 55001 +/// sf::UdpSocket socket; +/// socket.bind(55001); +/// +/// // Send a message to 192.168.1.50 on port 55002 +/// std::string message = "Hi, I am " + sf::IpAddress::getLocalAddress().toString(); +/// socket.send(message.c_str(), message.size() + 1, "192.168.1.50", 55002); +/// +/// // Receive an answer (most likely from 192.168.1.50, but could be anyone else) +/// std::array buffer; +/// std::size_t received = 0; +/// std::optional sender; +/// unsigned short port; +/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == sf::Socket::Status::Done) +/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl; +/// +/// // ----- The server ----- +/// +/// // Create a socket and bind it to the port 55002 +/// sf::UdpSocket socket; +/// socket.bind(55002); +/// +/// // Receive a message from anyone +/// std::array buffer; +/// std::size_t received = 0; +/// std::optional sender; +/// unsigned short port; +/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == sf::Socket::Status::Done) +/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl; +/// +/// // Send an answer +/// std::string message = "Welcome " + sender.toString(); +/// socket.send(message.c_str(), message.size() + 1, sender, port); +/// \endcode +/// +/// \see `sf::Socket`, `sf::TcpSocket`, `sf::Packet` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System.hpp b/third_party/sfml/include/SFML/System.hpp new file mode 100644 index 00000000..36599db6 --- /dev/null +++ b/third_party/sfml/include/SFML/System.hpp @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////// +/// \defgroup system System module +/// +/// Base module of SFML, defining various utilities. It provides +/// vector classes, Unicode strings and conversion functions, +/// threads and mutexes, timing classes. +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Angle.hpp b/third_party/sfml/include/SFML/System/Angle.hpp new file mode 100644 index 00000000..e6355d6e --- /dev/null +++ b/third_party/sfml/include/SFML/System/Angle.hpp @@ -0,0 +1,511 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Represents an angle value. +/// +//////////////////////////////////////////////////////////// +class Angle +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Sets the angle value to zero. + /// + //////////////////////////////////////////////////////////// + constexpr Angle() = default; + + //////////////////////////////////////////////////////////// + /// \brief Return the angle's value in degrees + /// + /// \return Angle in degrees + /// + /// \see `asRadians` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr float asDegrees() const; + + //////////////////////////////////////////////////////////// + /// \brief Return the angle's value in radians + /// + /// \return Angle in radians + /// + /// \see `asDegrees` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr float asRadians() const; + + //////////////////////////////////////////////////////////// + /// \brief Wrap to a range such that -180° <= angle < 180° + /// + /// Similar to a modulo operation, this returns a copy of the angle + /// constrained to the range [-180°, 180°) == [-Pi, Pi). + /// The resulting angle represents a rotation which is equivalent to `*this`. + /// + /// The name "signed" originates from the similarity to signed integers: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
signedunsigned
char[-128, 128)[0, 256)
Angle[-180°, 180°)[0°, 360°)
+ /// + /// \return Signed angle, wrapped to [-180°, 180°) + /// + /// \see `wrapUnsigned` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Angle wrapSigned() const; + + //////////////////////////////////////////////////////////// + /// \brief Wrap to a range such that 0° <= angle < 360° + /// + /// Similar to a modulo operation, this returns a copy of the angle + /// constrained to the range [0°, 360°) == [0, Tau) == [0, 2*Pi). + /// The resulting angle represents a rotation which is equivalent to `*this`. + /// + /// The name "unsigned" originates from the similarity to unsigned integers: + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + ///
signedunsigned
char[-128, 128)[0, 256)
Angle[-180°, 180°)[0°, 360°)
+ /// + /// \return Unsigned angle, wrapped to [0°, 360°) + /// + /// \see `wrapSigned` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Angle wrapUnsigned() const; + + //////////////////////////////////////////////////////////// + // Static member data + //////////////////////////////////////////////////////////// + // NOLINTNEXTLINE(readability-identifier-naming) + static const Angle Zero; //!< Predefined 0 degree angle value + +private: + friend constexpr Angle degrees(float angle); + friend constexpr Angle radians(float angle); + + //////////////////////////////////////////////////////////// + /// \brief Construct from a number of radians + /// + /// This function is internal. To construct angle values, + /// use `sf::radians` or `sf::degrees` instead. + /// + /// \param radians Angle in radians + /// + //////////////////////////////////////////////////////////// + constexpr explicit Angle(float radians); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + float m_radians{}; //!< Angle value stored as radians +}; + +//////////////////////////////////////////////////////////// +/// \brief Construct an angle value from a number of degrees +/// +/// \param angle Number of degrees +/// +/// \return Angle value constructed from the number of degrees +/// +/// \see `radians` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle degrees(float angle); + +//////////////////////////////////////////////////////////// +/// \brief Construct an angle value from a number of radians +/// +/// \param angle Number of radians +/// +/// \return Angle value constructed from the number of radians +/// +/// \see `degrees` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle radians(float angle); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of `operator==` to compare two angle values +/// \note Does not automatically wrap the angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `true` if both angle values are equal +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator==(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of `operator!=` to compare two angle values +/// \note Does not automatically wrap the angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `true` if both angle values are different +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator!=(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of `operator<` to compare two angle values +/// \note Does not automatically wrap the angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `true` if \a left is less than \a right +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator<(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of `operator>` to compare two angle values +/// \note Does not automatically wrap the angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `true` if \a left is greater than \a right +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator>(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of `operator<=` to compare two angle values +/// \note Does not automatically wrap the angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `true` if \a left is less than or equal to \a right +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator<=(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of `operator>=` to compare two angle values +/// \note Does not automatically wrap the angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `true` if \a left is greater than or equal to \a right +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator>=(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of unary `operator-` to negate an angle value. +/// +/// Represents a rotation in the opposite direction. +/// +/// \param right Right operand (an angle) +/// +/// \return Negative of the angle value +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator-(Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator+` to add two angle values +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return Sum of the two angle values +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator+(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator+=` to add/assign two angle values +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return Sum of the two angle values +/// +//////////////////////////////////////////////////////////// +constexpr Angle& operator+=(Angle& left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator-` to subtract two angle values +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return Difference of the two angle values +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator-(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator-=` to subtract/assign two angle values +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return Difference of the two angle values +/// +//////////////////////////////////////////////////////////// +constexpr Angle& operator-=(Angle& left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator*` to scale an angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (a number) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator*(Angle left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator*` to scale an angle value +/// +/// \param left Left operand (a number) +/// \param right Right operand (an angle) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator*(float left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator*=` to scale/assign an angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (a number) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +constexpr Angle& operator*=(Angle& left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator/` to scale an angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (a number) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator/(Angle left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator/=` to scale/assign an angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (a number) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +constexpr Angle& operator/=(Angle& left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator/` to compute the ratio of two angle values +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr float operator/(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator%` to compute modulo of an angle value. +/// +/// Right hand angle must be greater than zero. +/// +/// Examples: +/// \code +/// sf::degrees(90) % sf::degrees(40) // 10 degrees +/// sf::degrees(-90) % sf::degrees(40) // 30 degrees (not -10) +/// \endcode +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `left` modulo `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator%(Angle left, Angle right); + +//////////////////////////////////////////////////////////// +/// \relates Angle +/// \brief Overload of binary `operator%=` to compute/assign remainder of an angle value +/// +/// \param left Left operand (an angle) +/// \param right Right operand (an angle) +/// +/// \return `left` modulo `right` +/// +//////////////////////////////////////////////////////////// +constexpr Angle& operator%=(Angle& left, Angle right); + +namespace Literals +{ + +//////////////////////////////////////////////////////////// +/// \relates sf::Angle +/// \brief User defined literal for angles in degrees, e.g.\ `10.5_deg` +/// +/// \param angle Angle in degrees +/// +/// \return Angle +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator""_deg(long double angle); + +//////////////////////////////////////////////////////////// +/// \relates sf::Angle +/// \brief User defined literal for angles in degrees, e.g.\ `90_deg` +/// +/// \param angle Angle in degrees +/// +/// \return Angle +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator""_deg(unsigned long long int angle); + +//////////////////////////////////////////////////////////// +/// \relates sf::Angle +/// \brief User defined literal for angles in radians, e.g.\ `0.1_rad` +/// +/// \param angle Angle in radians +/// +/// \return Angle +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator""_rad(long double angle); + +//////////////////////////////////////////////////////////// +/// \relates sf::Angle +/// \brief User defined literal for angles in radians, e.g.\ `2_rad` +/// +/// \param angle Angle in radians +/// +/// \return Angle +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Angle operator""_rad(unsigned long long int angle); + +} // namespace Literals +} // namespace sf + +#include + + +//////////////////////////////////////////////////////////// +/// \class sf::Angle +/// \ingroup system +/// +/// `sf::Angle` encapsulates an angle value in a flexible way. +/// It allows for defining an angle value either as a number +/// of degrees or radians. It also works the other way +/// around. You can read an angle value as either a number +/// of degrees or radians. +/// +/// By using such a flexible interface, the API doesn't +/// impose any fixed type or unit for angle values and lets +/// the user choose their own preferred representation. +/// +/// Angle values support the usual mathematical operations. +/// You can add or subtract two angles, multiply or divide +/// an angle by a number, compare two angles, etc. +/// +/// Usage example: +/// \code +/// sf::Angle a1 = sf::degrees(90); +/// float radians = a1.asRadians(); // 1.5708f +/// +/// sf::Angle a2 = sf::radians(3.141592654f); +/// float degrees = a2.asDegrees(); // 180.0f +/// +/// using namespace sf::Literals; +/// sf::Angle a3 = 10_deg; // 10 degrees +/// sf::Angle a4 = 1.5_deg; // 1.5 degrees +/// sf::Angle a5 = 1_rad; // 1 radians +/// sf::Angle a6 = 3.14_rad; // 3.14 radians +/// \endcode +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Angle.inl b/third_party/sfml/include/SFML/System/Angle.inl new file mode 100644 index 00000000..a894c8e8 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Angle.inl @@ -0,0 +1,274 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // NOLINT(misc-header-include-cycle) + +#include + + +namespace sf +{ +namespace priv +{ +inline constexpr float pi = 3.141592654f; +inline constexpr float tau = pi * 2.f; + +constexpr float positiveRemainder(float a, float b) +{ + assert(b > 0.f && "Cannot calculate remainder with non-positive divisor"); + const float val = a - static_cast(static_cast(a / b)) * b; + return val >= 0.f ? val : val + b; +} +} // namespace priv + +//////////////////////////////////////////////////////////// +constexpr float Angle::asDegrees() const +{ + return m_radians * (180.f / priv::pi); +} + + +//////////////////////////////////////////////////////////// +constexpr float Angle::asRadians() const +{ + return m_radians; +} + + +//////////////////////////////////////////////////////////// +constexpr Angle Angle::wrapSigned() const +{ + return radians(priv::positiveRemainder(m_radians + priv::pi, priv::tau) - priv::pi); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle Angle::wrapUnsigned() const +{ + return radians(priv::positiveRemainder(m_radians, priv::tau)); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle::Angle(float radians) : m_radians(radians) +{ +} + + +//////////////////////////////////////////////////////////// +constexpr Angle degrees(float angle) +{ + return Angle(angle * (priv::pi / 180.f)); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle radians(float angle) +{ + return Angle(angle); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator==(Angle left, Angle right) +{ + return left.asRadians() == right.asRadians(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator!=(Angle left, Angle right) +{ + return left.asRadians() != right.asRadians(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator<(Angle left, Angle right) +{ + return left.asRadians() < right.asRadians(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator>(Angle left, Angle right) +{ + return left.asRadians() > right.asRadians(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator<=(Angle left, Angle right) +{ + return left.asRadians() <= right.asRadians(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator>=(Angle left, Angle right) +{ + return left.asRadians() >= right.asRadians(); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator-(Angle right) +{ + return radians(-right.asRadians()); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator+(Angle left, Angle right) +{ + return radians(left.asRadians() + right.asRadians()); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle& operator+=(Angle& left, Angle right) +{ + return left = left + right; +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator-(Angle left, Angle right) +{ + return radians(left.asRadians() - right.asRadians()); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle& operator-=(Angle& left, Angle right) +{ + return left = left - right; +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator*(Angle left, float right) +{ + return radians(left.asRadians() * right); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator*(float left, Angle right) +{ + return right * left; +} + + +//////////////////////////////////////////////////////////// +constexpr Angle& operator*=(Angle& left, float right) +{ + return left = left * right; +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator/(Angle left, float right) +{ + assert(right != 0.f && "Angle::operator/ cannot divide by 0"); + return radians(left.asRadians() / right); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle& operator/=(Angle& left, float right) +{ + assert(right != 0.f && "Angle::operator/= cannot divide by 0"); + return left = left / right; +} + + +//////////////////////////////////////////////////////////// +constexpr float operator/(Angle left, Angle right) +{ + assert(right.asRadians() != 0.f && "Angle::operator/ cannot divide by 0"); + return left.asRadians() / right.asRadians(); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator%(Angle left, Angle right) +{ + assert(right.asRadians() != 0.f && "Angle::operator% cannot modulus by 0"); + return radians(priv::positiveRemainder(left.asRadians(), right.asRadians())); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle& operator%=(Angle& left, Angle right) +{ + assert(right.asRadians() != 0.f && "Angle::operator%= cannot modulus by 0"); + return left = left % right; +} + +namespace Literals +{ + +//////////////////////////////////////////////////////////// +constexpr Angle operator""_deg(long double angle) +{ + return degrees(static_cast(angle)); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator""_deg(unsigned long long angle) +{ + return degrees(static_cast(angle)); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator""_rad(long double angle) +{ + return radians(static_cast(angle)); +} + + +//////////////////////////////////////////////////////////// +constexpr Angle operator""_rad(unsigned long long angle) +{ + return radians(static_cast(angle)); +} + +} // namespace Literals + + +//////////////////////////////////////////////////////////// +// Static member data +//////////////////////////////////////////////////////////// + +// Note: the 'inline' keyword here is technically not required, but VS2019 fails +// to compile with a bogus "multiple definition" error if not explicitly used. +inline constexpr Angle Angle::Zero; + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/Clock.hpp b/third_party/sfml/include/SFML/System/Clock.hpp new file mode 100644 index 00000000..92615f53 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Clock.hpp @@ -0,0 +1,196 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +#ifdef SFML_SYSTEM_ANDROID +#include +#endif + + +namespace sf +{ +namespace priv +{ + +//////////////////////////////////////////////////////////// +/// \brief Chooses a monotonic clock of highest resolution +/// +/// The `high_resolution_clock` is usually an alias for other +/// clocks: `steady_clock` or `system_clock`, whichever has a +/// higher precision. +/// +/// `sf::Clock`, however, is aimed towards monotonic time +/// measurements and so `system_clock` could never be a choice +/// as its subject to discontinuous jumps in the system time +/// (e.g., if the system administrator manually changes +/// the clock), and by the incremental adjustments performed +/// by `adjtime` and Network Time Protocol. On the other +/// hand, monotonic clocks are unaffected by this behavior. +/// +/// Note: Linux implementation of a monotonic clock that +/// takes sleep time into account is represented by +/// `CLOCK_BOOTTIME`. Android devices can define the macro: +/// `SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK` to use a separate +/// implementation of that clock, instead. +/// +/// For more information on Linux clocks visit: +/// https://linux.die.net/man/2/clock_gettime +/// +//////////////////////////////////////////////////////////// +#if defined(SFML_SYSTEM_ANDROID) && defined(SFML_ANDROID_USE_SUSPEND_AWARE_CLOCK) +using ClockImpl = SuspendAwareClock; +#else +using ClockImpl = std::conditional_t; +#endif + +static_assert(ClockImpl::is_steady, "Provided implementation is not a monotonic clock"); +static_assert(std::ratio_less_equal_v, + "Clock resolution is too low. Expecting at least a microsecond precision"); + +} // namespace priv + +class Time; + +//////////////////////////////////////////////////////////// +/// \brief Utility class that measures the elapsed time +/// +/// The clock starts automatically after being constructed. +/// +//////////////////////////////////////////////////////////// +class SFML_SYSTEM_API Clock +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Get the elapsed time + /// + /// This function returns the time elapsed since the last call + /// to `restart()` (or the construction of the instance if `restart()` + /// has not been called). + /// + /// \return Time elapsed + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Time getElapsedTime() const; + + //////////////////////////////////////////////////////////// + /// \brief Check whether the clock is running + /// + /// \return `true` if the clock is running, `false` otherwise + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool isRunning() const; + + //////////////////////////////////////////////////////////// + /// \brief Start the clock + /// + /// \see `stop` + /// + //////////////////////////////////////////////////////////// + void start(); + + //////////////////////////////////////////////////////////// + /// \brief Stop the clock + /// + /// \see `start` + /// + //////////////////////////////////////////////////////////// + void stop(); + + //////////////////////////////////////////////////////////// + /// \brief Restart the clock + /// + /// This function puts the time counter back to zero, returns + /// the elapsed time, and leaves the clock in a running state. + /// + /// \return Time elapsed + /// + /// \see `reset` + /// + //////////////////////////////////////////////////////////// + Time restart(); + + //////////////////////////////////////////////////////////// + /// \brief Reset the clock + /// + /// This function puts the time counter back to zero, returns + /// the elapsed time, and leaves the clock in a paused state. + /// + /// \return Time elapsed + /// + /// \see `restart` + /// + //////////////////////////////////////////////////////////// + Time reset(); + +private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + priv::ClockImpl::time_point m_refPoint{priv::ClockImpl::now()}; //!< Time of last reset + priv::ClockImpl::time_point m_stopPoint; //!< Time of last stop +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::Clock +/// \ingroup system +/// +/// `sf::Clock` is a lightweight class for measuring time. +/// +/// It provides the most precise time that the underlying +/// OS can achieve (generally microseconds or nanoseconds). +/// It also ensures monotonicity, which means that the returned +/// time can never go backward, even if the system time is +/// changed. +/// +/// Usage example: +/// \code +/// sf::Clock clock; +/// ... +/// Time time1 = clock.getElapsedTime(); +/// ... +/// Time time2 = clock.restart(); +/// ... +/// Time time3 = clock.reset(); +/// \endcode +/// +/// The `sf::Time` value returned by the clock can then be +/// converted to a number of seconds, milliseconds or even +/// microseconds. +/// +/// \see `sf::Time` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Err.hpp b/third_party/sfml/include/SFML/System/Err.hpp new file mode 100644 index 00000000..ea59da44 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Err.hpp @@ -0,0 +1,77 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Standard stream used by SFML to output warnings and errors +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API std::ostream& err(); + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \fn sf::err +/// \ingroup system +/// +/// By default, `sf::err()` outputs to the same location as `std::cerr`, +/// (-> the stderr descriptor) which is the console if there's +/// one available. +/// +/// It is a standard `std::ostream` instance, so it supports all the +/// insertion operations defined by the STL +/// (`operator<<`, manipulators, etc.). +/// +/// `sf::err()` can be redirected to write to another output, independently +/// of `std::cerr`, by using the `rdbuf()` function provided by the +/// `std::ostream` class. +/// +/// Example: +/// \code +/// // Redirect to a file +/// std::ofstream file("sfml-log.txt"); +/// std::streambuf* previous = sf::err().rdbuf(file.rdbuf()); +/// +/// // Redirect to nothing +/// sf::err().rdbuf(nullptr); +/// +/// // Restore the original output +/// sf::err().rdbuf(previous); +/// \endcode +/// +/// \return Reference to `std::ostream` representing the SFML error stream +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Exception.hpp b/third_party/sfml/include/SFML/System/Exception.hpp new file mode 100644 index 00000000..bee07e46 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Exception.hpp @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Library-specific exception type +/// +//////////////////////////////////////////////////////////// +class SFML_SYSTEM_API Exception : public std::runtime_error +{ +public: + using std::runtime_error::runtime_error; +}; +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/Export.hpp b/third_party/sfml/include/SFML/System/Export.hpp new file mode 100644 index 00000000..2362d3a2 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Export.hpp @@ -0,0 +1,44 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +//////////////////////////////////////////////////////////// +// Portable import / export macros +//////////////////////////////////////////////////////////// +#if defined(SFML_SYSTEM_EXPORTS) + +#define SFML_SYSTEM_API SFML_API_EXPORT + +#else + +#define SFML_SYSTEM_API SFML_API_IMPORT + +#endif diff --git a/third_party/sfml/include/SFML/System/FileInputStream.hpp b/third_party/sfml/include/SFML/System/FileInputStream.hpp new file mode 100644 index 00000000..3a036e42 --- /dev/null +++ b/third_party/sfml/include/SFML/System/FileInputStream.hpp @@ -0,0 +1,212 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + +#include +#include + +#include +#include + +#ifdef SFML_SYSTEM_ANDROID +namespace sf::priv +{ +class SFML_SYSTEM_API ResourceStream; +} +#endif + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Implementation of input stream based on a file +/// +//////////////////////////////////////////////////////////// +class SFML_SYSTEM_API FileInputStream : public InputStream +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Construct a file input stream that is not associated + /// with a file to read. + /// + //////////////////////////////////////////////////////////// + FileInputStream(); + + //////////////////////////////////////////////////////////// + /// \brief Default destructor + /// + //////////////////////////////////////////////////////////// + ~FileInputStream() override; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + //////////////////////////////////////////////////////////// + FileInputStream(const FileInputStream&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy assignment + /// + //////////////////////////////////////////////////////////// + FileInputStream& operator=(const FileInputStream&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Move constructor + /// + //////////////////////////////////////////////////////////// + FileInputStream(FileInputStream&&) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Move assignment + /// + //////////////////////////////////////////////////////////// + FileInputStream& operator=(FileInputStream&&) noexcept; + + //////////////////////////////////////////////////////////// + /// \brief Construct the stream from a file path + /// + /// \param filename Name of the file to open + /// + /// \throws sf::Exception on error + /// + //////////////////////////////////////////////////////////// + explicit FileInputStream(const std::filesystem::path& filename); + + //////////////////////////////////////////////////////////// + /// \brief Open the stream from a file path + /// + /// \param filename Name of the file to open + /// + /// \return `true` on success, `false` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool open(const std::filesystem::path& filename); + + //////////////////////////////////////////////////////////// + /// \brief Read data from the stream + /// + /// After reading, the stream's reading position must be + /// advanced by the amount of bytes read. + /// + /// \param data Buffer where to copy the read data + /// \param size Desired number of bytes to read + /// + /// \return The number of bytes actually read, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::optional read(void* data, std::size_t size) override; + + //////////////////////////////////////////////////////////// + /// \brief Change the current reading position + /// + /// \param position The position to seek to, from the beginning + /// + /// \return The position actually sought to, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::optional seek(std::size_t position) override; + + //////////////////////////////////////////////////////////// + /// \brief Get the current reading position in the stream + /// + /// \return The current position, or `std::nullopt` on error. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::optional tell() override; + + //////////////////////////////////////////////////////////// + /// \brief Return the size of the stream + /// + /// \return The total number of bytes available in the stream, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + std::optional getSize() override; + +private: + //////////////////////////////////////////////////////////// + /// \brief Deleter for stdio file stream that closes the file stream + /// + //////////////////////////////////////////////////////////// + struct FileCloser + { + void operator()(std::FILE* file); + }; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// +#ifdef SFML_SYSTEM_ANDROID + std::unique_ptr m_androidFile; +#endif + + std::unique_ptr m_file; //!< stdio file stream +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::FileInputStream +/// \ingroup system +/// +/// This class is a specialization of `InputStream` that +/// reads from a file on disk. +/// +/// It wraps a file in the common `InputStream` interface +/// and therefore allows to use generic classes or functions +/// that accept such a stream, with a file on disk as the data +/// source. +/// +/// In addition to the virtual functions inherited from +/// `InputStream`, `FileInputStream` adds a function to +/// specify the file to open. +/// +/// SFML resource classes can usually be loaded directly from +/// a filename, so this class shouldn't be useful to you unless +/// you create your own algorithms that operate on an InputStream. +/// +/// Usage example: +/// \code +/// void process(InputStream& stream); +/// +/// std::optional stream = sf::FileInputStream::open("some_file.dat"); +/// if (stream) +/// process(*stream); +/// \endcode +/// +/// \see `InputStream`, `MemoryInputStream` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/InputStream.hpp b/third_party/sfml/include/SFML/System/InputStream.hpp new file mode 100644 index 00000000..6eda58de --- /dev/null +++ b/third_party/sfml/include/SFML/System/InputStream.hpp @@ -0,0 +1,166 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Abstract class for custom file input streams +/// +//////////////////////////////////////////////////////////// +class SFML_SYSTEM_API InputStream +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Virtual destructor + /// + //////////////////////////////////////////////////////////// + virtual ~InputStream() = default; + + //////////////////////////////////////////////////////////// + /// \brief Read data from the stream + /// + /// After reading, the stream's reading position must be + /// advanced by the amount of bytes read. + /// + /// \param data Buffer where to copy the read data + /// \param size Desired number of bytes to read + /// + /// \return The number of bytes actually read, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] virtual std::optional read(void* data, std::size_t size) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Change the current reading position + /// + /// \param position The position to seek to, from the beginning + /// + /// \return The position actually sought to, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] virtual std::optional seek(std::size_t position) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the current reading position in the stream + /// + /// \return The current position, or `std::nullopt` on error. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] virtual std::optional tell() = 0; + + //////////////////////////////////////////////////////////// + /// \brief Return the size of the stream + /// + /// \return The total number of bytes available in the stream, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + virtual std::optional getSize() = 0; +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::InputStream +/// \ingroup system +/// +/// This class allows users to define their own file input sources +/// from which SFML can load resources. +/// +/// SFML resource classes like `sf::Texture` and +/// `sf::SoundBuffer` provide `loadFromFile` and `loadFromMemory` functions, +/// which read data from conventional sources. However, if you +/// have data coming from a different source (over a network, +/// embedded, encrypted, compressed, etc) you can derive your +/// own class from `sf::InputStream` and load SFML resources with +/// their `loadFromStream` function. +/// +/// Usage example: +/// \code +/// // custom stream class that reads from inside a zip file +/// class ZipStream : public sf::InputStream +/// { +/// public: +/// +/// ZipStream(const std::string& archive); +/// +/// [[nodiscard]] bool open(const std::filesystem::path& filename); +/// +/// [[nodiscard]] std::optional read(void* data, std::size_t size); +/// +/// [[nodiscard]] std::optional seek(std::size_t position); +/// +/// [[nodiscard]] std::optional tell(); +/// +/// std::optional getSize(); +/// +/// private: +/// +/// ... +/// }; +/// +/// // now you can load textures... +/// ZipStream stream("resources.zip"); +/// +/// if (!stream.open("images/img.png")) +/// { +/// // Handle error... +/// } +/// +/// const sf::Texture texture(stream); +/// +/// // musics... +/// sf::Music music; +/// ZipStream stream("resources.zip"); +/// +/// if (!stream.open("musics/msc.ogg")) +/// { +/// // Handle error... +/// } +/// +/// if (!music.openFromStream(stream)) +/// { +/// // Handle error... +/// } +/// +/// // etc. +/// \endcode +/// +/// \see `FileInputStream`, `MemoryInputStream` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/MemoryInputStream.hpp b/third_party/sfml/include/SFML/System/MemoryInputStream.hpp new file mode 100644 index 00000000..e5eaf50c --- /dev/null +++ b/third_party/sfml/include/SFML/System/MemoryInputStream.hpp @@ -0,0 +1,139 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Implementation of input stream based on a memory chunk +/// +//////////////////////////////////////////////////////////// +class SFML_SYSTEM_API MemoryInputStream : public InputStream +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Construct the stream from its data + /// + /// \param data Pointer to the data in memory + /// \param sizeInBytes Size of the data, in bytes + /// + //////////////////////////////////////////////////////////// + MemoryInputStream(const void* data, std::size_t sizeInBytes); + + //////////////////////////////////////////////////////////// + /// \brief Read data from the stream + /// + /// After reading, the stream's reading position must be + /// advanced by the amount of bytes read. + /// + /// \param data Buffer where to copy the read data + /// \param size Desired number of bytes to read + /// + /// \return The number of bytes actually read, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::optional read(void* data, std::size_t size) override; + + //////////////////////////////////////////////////////////// + /// \brief Change the current reading position + /// + /// \param position The position to seek to, from the beginning + /// + /// \return The position actually sought to, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::optional seek(std::size_t position) override; + + //////////////////////////////////////////////////////////// + /// \brief Get the current reading position in the stream + /// + /// \return The current position, or `std::nullopt` on error. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::optional tell() override; + + //////////////////////////////////////////////////////////// + /// \brief Return the size of the stream + /// + /// \return The total number of bytes available in the stream, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + std::optional getSize() override; + +private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + const std::byte* m_data{}; //!< Pointer to the data in memory + std::size_t m_size{}; //!< Total size of the data + std::size_t m_offset{}; //!< Current reading position +}; + +} // namespace sf + + +//////////////////////////////////////////////////////////// +/// \class sf::MemoryInputStream +/// \ingroup system +/// +/// This class is a specialization of `InputStream` that +/// reads from data in memory. +/// +/// It wraps a memory chunk in the common `InputStream` interface +/// and therefore allows to use generic classes or functions +/// that accept such a stream, with content already loaded in memory. +/// +/// In addition to the virtual functions inherited from +/// `InputStream`, `MemoryInputStream` adds a function to +/// specify the pointer and size of the data in memory. +/// +/// SFML resource classes can usually be loaded directly from +/// memory, so this class shouldn't be useful to you unless +/// you create your own algorithms that operate on an InputStream. +/// +/// Usage example: +/// \code +/// void process(InputStream& stream); +/// +/// MemoryInputStream stream(thePtr, theSize); +/// process(stream); +/// \endcode +/// +/// \see `InputStream`, `FileInputStream` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/NativeActivity.hpp b/third_party/sfml/include/SFML/System/NativeActivity.hpp new file mode 100644 index 00000000..207f4682 --- /dev/null +++ b/third_party/sfml/include/SFML/System/NativeActivity.hpp @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +#if !defined(SFML_SYSTEM_ANDROID) +#error NativeActivity.hpp: This header is Android only. +#endif + + +struct ANativeActivity; + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \ingroup system +/// \brief Return a pointer to the Android native activity +/// +/// You shouldn't have to use this function, unless you want +/// to implement very specific details, that SFML doesn't +/// support, or to use a workaround for a known issue. +/// +/// \return Pointer to Android native activity structure +/// +/// \sfplatform{Android,SFML/System/NativeActivity.hpp} +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API ANativeActivity* getNativeActivity(); + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/Sleep.hpp b/third_party/sfml/include/SFML/System/Sleep.hpp new file mode 100644 index 00000000..7aaa0ab9 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Sleep.hpp @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +class Time; + +//////////////////////////////////////////////////////////// +/// \ingroup system +/// \brief Make the current thread sleep for a given duration +/// +/// `sf::sleep` is the best way to block a program or one of its +/// threads, as it doesn't consume any CPU power. Compared to +/// the standard `std::this_thread::sleep_for` function, this +/// one provides more accurate sleeping time thanks to some +/// platform-specific tweaks. +/// +/// `sf::sleep` only guarantees millisecond precision. Sleeping +/// for a duration less than 1 millisecond is prone to result +/// in the actual sleep duration being less than what is +/// requested. +/// +/// \param duration Time to sleep +/// +//////////////////////////////////////////////////////////// +void SFML_SYSTEM_API sleep(Time duration); + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/String.hpp b/third_party/sfml/include/SFML/System/String.hpp new file mode 100644 index 00000000..7cea505f --- /dev/null +++ b/third_party/sfml/include/SFML/System/String.hpp @@ -0,0 +1,700 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include +#include + +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Character traits for `std::uint8_t` +/// +//////////////////////////////////////////////////////////// +struct SFML_SYSTEM_API U8StringCharTraits +{ + // NOLINTBEGIN(readability-identifier-naming) + using char_type = std::uint8_t; + using int_type = std::char_traits::int_type; + using off_type = std::char_traits::off_type; + using pos_type = std::char_traits::pos_type; + using state_type = std::char_traits::state_type; + + static void assign(char_type& c1, char_type c2) noexcept; + static char_type* assign(char_type* s, std::size_t n, char_type c); + static bool eq(char_type c1, char_type c2) noexcept; + static bool lt(char_type c1, char_type c2) noexcept; + static char_type* move(char_type* s1, const char_type* s2, std::size_t n); + static char_type* copy(char_type* s1, const char_type* s2, std::size_t n); + static int compare(const char_type* s1, const char_type* s2, std::size_t n); + static std::size_t length(const char_type* s); + static const char_type* find(const char_type* s, std::size_t n, const char_type& c); + static char_type to_char_type(int_type i) noexcept; + static int_type to_int_type(char_type c) noexcept; + static bool eq_int_type(int_type i1, int_type i2) noexcept; + static int_type eof() noexcept; + static int_type not_eof(int_type i) noexcept; + // NOLINTEND(readability-identifier-naming) +}; + +//////////////////////////////////////////////////////////// +/// \brief Portable replacement for `std::basic_string` +/// +/// While all major C++ implementations happen to define this +/// as of early 2024, this specialization is not strictly speaking +/// standard C++. Thus we can't depend on its continued existence. +/// +//////////////////////////////////////////////////////////// +using U8String = std::basic_string; + +//////////////////////////////////////////////////////////// +/// \brief Utility string class that automatically handles +/// conversions between types and encodings +/// +//////////////////////////////////////////////////////////// +class SFML_SYSTEM_API String +{ +public: + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// + using Iterator = std::u32string::iterator; //!< Iterator type + using ConstIterator = std::u32string::const_iterator; //!< Read-only iterator type + + //////////////////////////////////////////////////////////// + // Static member data + //////////////////////////////////////////////////////////// + // NOLINTBEGIN(readability-identifier-naming) + /// Represents an invalid position in the string + static inline const std::size_t InvalidPos{std::u32string::npos}; + // NOLINTEND(readability-identifier-naming) + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// This constructor creates an empty string. + /// + //////////////////////////////////////////////////////////// + String() = default; + + //////////////////////////////////////////////////////////// + /// \brief Deleted `std::nullptr_t` constructor + /// + /// Disallow construction from `nullptr` literal + /// + //////////////////////////////////////////////////////////// + String(std::nullptr_t, const std::locale& = {}) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Construct from a single ANSI character and a locale + /// + /// The source character is converted to UTF-32 according + /// to the given locale. + /// + /// \param ansiChar ANSI character to convert + /// \param locale Locale to use for conversion + /// + //////////////////////////////////////////////////////////// + String(char ansiChar, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Construct from single wide character + /// + /// \param wideChar Wide character to convert + /// + //////////////////////////////////////////////////////////// + String(wchar_t wideChar); + + //////////////////////////////////////////////////////////// + /// \brief Construct from single UTF-32 character + /// + /// \param utf32Char UTF-32 character to convert + /// + //////////////////////////////////////////////////////////// + String(char32_t utf32Char); + + //////////////////////////////////////////////////////////// + /// \brief Construct from a null-terminated C-style ANSI string and a locale + /// + /// The source string is converted to UTF-32 according + /// to the given locale. + /// + /// \param ansiString ANSI string to convert + /// \param locale Locale to use for conversion + /// + //////////////////////////////////////////////////////////// + String(const char* ansiString, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Construct from an ANSI string and a locale + /// + /// The source string is converted to UTF-32 according + /// to the given locale. + /// + /// \param ansiString ANSI string to convert + /// \param locale Locale to use for conversion + /// + //////////////////////////////////////////////////////////// + String(const std::string& ansiString, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Construct from null-terminated C-style wide string + /// + /// \param wideString Wide string to convert + /// + //////////////////////////////////////////////////////////// + String(const wchar_t* wideString); + + //////////////////////////////////////////////////////////// + /// \brief Construct from a wide string + /// + /// \param wideString Wide string to convert + /// + //////////////////////////////////////////////////////////// + String(const std::wstring& wideString); + + //////////////////////////////////////////////////////////// + /// \brief Construct from a null-terminated C-style UTF-32 string + /// + /// \param utf32String UTF-32 string to assign + /// + //////////////////////////////////////////////////////////// + String(const char32_t* utf32String); + + //////////////////////////////////////////////////////////// + /// \brief Construct from an UTF-32 string + /// + /// \param utf32String UTF-32 string to assign + /// + //////////////////////////////////////////////////////////// + String(std::u32string utf32String); + + //////////////////////////////////////////////////////////// + /// \brief Create a new `sf::String` from a UTF-8 encoded string + /// + /// \param begin Forward iterator to the beginning of the UTF-8 sequence + /// \param end Forward iterator to the end of the UTF-8 sequence + /// + /// \return A `sf::String` containing the source string + /// + /// \see `fromUtf16`, `fromUtf32` + /// + //////////////////////////////////////////////////////////// + template + [[nodiscard]] static String fromUtf8(T begin, T end); + + //////////////////////////////////////////////////////////// + /// \brief Create a new `sf::String` from a UTF-16 encoded string + /// + /// \param begin Forward iterator to the beginning of the UTF-16 sequence + /// \param end Forward iterator to the end of the UTF-16 sequence + /// + /// \return A `sf::String` containing the source string + /// + /// \see `fromUtf8`, `fromUtf32` + /// + //////////////////////////////////////////////////////////// + template + [[nodiscard]] static String fromUtf16(T begin, T end); + + //////////////////////////////////////////////////////////// + /// \brief Create a new `sf::String` from a UTF-32 encoded string + /// + /// This function is provided for consistency, it is equivalent to + /// using the constructors that takes a `const char32_t*` or + /// a `std::u32string`. + /// + /// \param begin Forward iterator to the beginning of the UTF-32 sequence + /// \param end Forward iterator to the end of the UTF-32 sequence + /// + /// \return A `sf::String` containing the source string + /// + /// \see `fromUtf8`, `fromUtf16` + /// + //////////////////////////////////////////////////////////// + template + [[nodiscard]] static String fromUtf32(T begin, T end); + + //////////////////////////////////////////////////////////// + /// \brief Implicit conversion operator to `std::string` (ANSI string) + /// + /// The current global locale is used for conversion. If you + /// want to explicitly specify a locale, see toAnsiString. + /// Characters that do not fit in the target encoding are + /// discarded from the returned string. + /// This operator is defined for convenience, and is equivalent + /// to calling `toAnsiString()`. + /// + /// \return Converted ANSI string + /// + /// \see `toAnsiString`, `operator std::wstring` + /// + //////////////////////////////////////////////////////////// + operator std::string() const; + + //////////////////////////////////////////////////////////// + /// \brief Implicit conversion operator to `std::wstring` (wide string) + /// + /// Characters that do not fit in the target encoding are + /// discarded from the returned string. + /// This operator is defined for convenience, and is equivalent + /// to calling `toWideString()`. + /// + /// \return Converted wide string + /// + /// \see `toWideString`, `operator std::string` + /// + //////////////////////////////////////////////////////////// + operator std::wstring() const; + + //////////////////////////////////////////////////////////// + /// \brief Convert the Unicode string to an ANSI string + /// + /// The UTF-32 string is converted to an ANSI string in + /// the encoding defined by `locale`. + /// Characters that do not fit in the target encoding are + /// discarded from the returned string. + /// + /// \param locale Locale to use for conversion + /// + /// \return Converted ANSI string + /// + /// \see `toWideString`, `operator std::string` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::string toAnsiString(const std::locale& locale = {}) const; + + //////////////////////////////////////////////////////////// + /// \brief Convert the Unicode string to a wide string + /// + /// Characters that do not fit in the target encoding are + /// discarded from the returned string. + /// + /// \return Converted wide string + /// + /// \see `toAnsiString`, `operator std::wstring` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::wstring toWideString() const; + + //////////////////////////////////////////////////////////// + /// \brief Convert the Unicode string to a UTF-8 string + /// + /// \return Converted UTF-8 string + /// + /// \see `toUtf16`, `toUtf32` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] sf::U8String toUtf8() const; + + //////////////////////////////////////////////////////////// + /// \brief Convert the Unicode string to a UTF-16 string + /// + /// \return Converted UTF-16 string + /// + /// \see `toUtf8`, `toUtf32` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::u16string toUtf16() const; + + //////////////////////////////////////////////////////////// + /// \brief Convert the Unicode string to a UTF-32 string + /// + /// This function doesn't perform any conversion, since the + /// string is already stored as UTF-32 internally. + /// + /// \return Converted UTF-32 string + /// + /// \see `toUtf8`, `toUtf16` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::u32string toUtf32() const; + + //////////////////////////////////////////////////////////// + /// \brief Overload of `operator+=` to append an UTF-32 string + /// + /// \param right String to append + /// + /// \return Reference to self + /// + //////////////////////////////////////////////////////////// + String& operator+=(const String& right); + + //////////////////////////////////////////////////////////// + /// \brief Overload of `operator[]` to access a character by its position + /// + /// This function provides read-only access to characters. + /// Note: the behavior is undefined if `index` is out of range. + /// + /// \param index Index of the character to get + /// + /// \return Character at position `index` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] char32_t operator[](std::size_t index) const; + + //////////////////////////////////////////////////////////// + /// \brief Overload of `operator[]` to access a character by its position + /// + /// This function provides read and write access to characters. + /// Note: the behavior is undefined if `index` is out of range. + /// + /// \param index Index of the character to get + /// + /// \return Reference to the character at position `index` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] char32_t& operator[](std::size_t index); + + //////////////////////////////////////////////////////////// + /// \brief Clear the string + /// + /// This function removes all the characters from the string. + /// + /// \see `isEmpty`, `erase` + /// + //////////////////////////////////////////////////////////// + void clear(); + + //////////////////////////////////////////////////////////// + /// \brief Get the size of the string + /// + /// \return Number of characters in the string + /// + /// \see `isEmpty` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::size_t getSize() const; + + //////////////////////////////////////////////////////////// + /// \brief Check whether the string is empty or not + /// + /// \return `true` if the string is empty (i.e. contains no character) + /// + /// \see `clear`, `getSize` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool isEmpty() const; + + //////////////////////////////////////////////////////////// + /// \brief Erase one or more characters from the string + /// + /// This function removes a sequence of `count` characters + /// starting from `position`. + /// + /// \param position Position of the first character to erase + /// \param count Number of characters to erase + /// + //////////////////////////////////////////////////////////// + void erase(std::size_t position, std::size_t count = 1); + + //////////////////////////////////////////////////////////// + /// \brief Insert one or more characters into the string + /// + /// This function inserts the characters of `str` + /// into the string, starting from `position`. + /// + /// \param position Position of insertion + /// \param str Characters to insert + /// + //////////////////////////////////////////////////////////// + void insert(std::size_t position, const String& str); + + //////////////////////////////////////////////////////////// + /// \brief Find a sequence of one or more characters in the string + /// + /// This function searches for the characters of `str` + /// in the string, starting from `start`. + /// + /// \param str Characters to find + /// \param start Where to begin searching + /// + /// \return Position of `str` in the string, or `String::InvalidPos` if not found + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] std::size_t find(const String& str, std::size_t start = 0) const; + + //////////////////////////////////////////////////////////// + /// \brief Replace a substring with another string + /// + /// This function replaces the substring that starts at index `position` + /// and spans `length` characters with the string `replaceWith`. + /// + /// \param position Index of the first character to be replaced + /// \param length Number of characters to replace. You can pass InvalidPos to + /// replace all characters until the end of the string. + /// \param replaceWith String that replaces the given substring. + /// + //////////////////////////////////////////////////////////// + void replace(std::size_t position, std::size_t length, const String& replaceWith); + + //////////////////////////////////////////////////////////// + /// \brief Replace all occurrences of a substring with a replacement string + /// + /// This function replaces all occurrences of `searchFor` in this string + /// with the string `replaceWith`. + /// + /// \param searchFor The value being searched for + /// \param replaceWith The value that replaces found `searchFor` values + /// + //////////////////////////////////////////////////////////// + void replace(const String& searchFor, const String& replaceWith); + + //////////////////////////////////////////////////////////// + /// \brief Return a part of the string + /// + /// This function returns the substring that starts at index `position` + /// and spans `length` characters. + /// + /// \param position Index of the first character + /// \param length Number of characters to include in the substring (if + /// the string is shorter, as many characters as possible + /// are included). `InvalidPos` can be used to include all + /// characters until the end of the string. + /// + /// \return String object containing a substring of this object + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] String substring(std::size_t position, std::size_t length = InvalidPos) const; + + //////////////////////////////////////////////////////////// + /// \brief Get a pointer to the C-style array of characters + /// + /// This functions provides a read-only access to a + /// null-terminated C-style representation of the string. + /// The returned pointer is temporary and is meant only for + /// immediate use, thus it is not recommended to store it. + /// + /// \return Read-only pointer to the array of characters + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] const char32_t* getData() const; + + //////////////////////////////////////////////////////////// + /// \brief Return an iterator to the beginning of the string + /// + /// \return Read-write iterator to the beginning of the string characters + /// + /// \see `end` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Iterator begin(); + + //////////////////////////////////////////////////////////// + /// \brief Return an iterator to the beginning of the string + /// + /// \return Read-only iterator to the beginning of the string characters + /// + /// \see `end` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] ConstIterator begin() const; + + //////////////////////////////////////////////////////////// + /// \brief Return an iterator to the end of the string + /// + /// The end iterator refers to 1 position past the last character; + /// thus it represents an invalid character and should never be + /// accessed. + /// + /// \return Read-write iterator to the end of the string characters + /// + /// \see `begin` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] Iterator end(); + + //////////////////////////////////////////////////////////// + /// \brief Return an iterator to the end of the string + /// + /// The end iterator refers to 1 position past the last character; + /// thus it represents an invalid character and should never be + /// accessed. + /// + /// \return Read-only iterator to the end of the string characters + /// + /// \see `begin` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] ConstIterator end() const; + +private: + friend SFML_SYSTEM_API bool operator==(const String& left, const String& right); + friend SFML_SYSTEM_API bool operator<(const String& left, const String& right); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::u32string m_string; //!< Internal string of UTF-32 characters +}; + +//////////////////////////////////////////////////////////// +/// \relates String +/// \brief Overload of `operator==` to compare two UTF-32 strings +/// +/// \param left Left operand (a string) +/// \param right Right operand (a string) +/// +/// \return `true` if both strings are equal +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API bool operator==(const String& left, const String& right); + +//////////////////////////////////////////////////////////// +/// \relates String +/// \brief Overload of `operator!=` to compare two UTF-32 strings +/// +/// \param left Left operand (a string) +/// \param right Right operand (a string) +/// +/// \return `true` if both strings are different +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API bool operator!=(const String& left, const String& right); + +//////////////////////////////////////////////////////////// +/// \relates String +/// \brief Overload of `operator<` to compare two UTF-32 strings +/// +/// \param left Left operand (a string) +/// \param right Right operand (a string) +/// +/// \return `true` if `left` is lexicographically before `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API bool operator<(const String& left, const String& right); + +//////////////////////////////////////////////////////////// +/// \relates String +/// \brief Overload of `operator>` to compare two UTF-32 strings +/// +/// \param left Left operand (a string) +/// \param right Right operand (a string) +/// +/// \return `true` if `left` is lexicographically after `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API bool operator>(const String& left, const String& right); + +//////////////////////////////////////////////////////////// +/// \relates String +/// \brief Overload of `operator<=` to compare two UTF-32 strings +/// +/// \param left Left operand (a string) +/// \param right Right operand (a string) +/// +/// \return `true` if `left` is lexicographically before or equivalent to `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API bool operator<=(const String& left, const String& right); + +//////////////////////////////////////////////////////////// +/// \relates String +/// \brief Overload of `operator>=` to compare two UTF-32 strings +/// +/// \param left Left operand (a string) +/// \param right Right operand (a string) +/// +/// \return `true` if `left` is lexicographically after or equivalent to `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API bool operator>=(const String& left, const String& right); + +//////////////////////////////////////////////////////////// +/// \relates String +/// \brief Overload of binary `operator+` to concatenate two strings +/// +/// \param left Left operand (a string) +/// \param right Right operand (a string) +/// +/// \return Concatenated string +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] SFML_SYSTEM_API String operator+(const String& left, const String& right); + +} // namespace sf + +#include + + +//////////////////////////////////////////////////////////// +/// \class sf::String +/// \ingroup system +/// +/// `sf::String` is a utility string class defined mainly for +/// convenience. It is a Unicode string (implemented using +/// UTF-32), thus it can store any character in the world +/// (European, Chinese, Arabic, Hebrew, etc.). +/// +/// It automatically handles conversions from/to ANSI and +/// wide strings, so that you can work with standard string +/// classes and still be compatible with functions taking a +/// `sf::String`. +/// +/// \code +/// sf::String s; +/// +/// std::string s1 = s; // automatically converted to ANSI string +/// std::wstring s2 = s; // automatically converted to wide string +/// s = "hello"; // automatically converted from ANSI string +/// s = L"hello"; // automatically converted from wide string +/// s += 'a'; // automatically converted from ANSI string +/// s += L'a'; // automatically converted from wide string +/// \endcode +/// +/// Conversions involving ANSI strings use the default user locale. However +/// it is possible to use a custom locale if necessary: +/// \code +/// std::locale locale; +/// sf::String s; +/// ... +/// std::string s1 = s.toAnsiString(locale); +/// s = sf::String("hello", locale); +/// \endcode +/// +/// `sf::String` defines the most important functions of the +/// standard `std::string` class: removing, random access, iterating, +/// appending, comparing, etc. However it is a simple class +/// provided for convenience, and you may have to consider using +/// a more optimized class if your program requires complex string +/// handling. The automatic conversion functions will then take +/// care of converting your string to `sf::String` whenever SFML +/// requires it. +/// +/// Please note that SFML also defines a low-level, generic +/// interface for Unicode handling, see the `sf::Utf` classes. +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/String.inl b/third_party/sfml/include/SFML/System/String.inl new file mode 100644 index 00000000..eb22d989 --- /dev/null +++ b/third_party/sfml/include/SFML/System/String.inl @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // NOLINT(misc-header-include-cycle) + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +template +String String::fromUtf8(T begin, T end) +{ + String string; + Utf8::toUtf32(begin, end, std::back_inserter(string.m_string)); + return string; +} + + +//////////////////////////////////////////////////////////// +template +String String::fromUtf16(T begin, T end) +{ + String string; + Utf16::toUtf32(begin, end, std::back_inserter(string.m_string)); + return string; +} + + +//////////////////////////////////////////////////////////// +template +String String::fromUtf32(T begin, T end) +{ + String string; + string.m_string.assign(begin, end); + return string; +} + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/SuspendAwareClock.hpp b/third_party/sfml/include/SFML/System/SuspendAwareClock.hpp new file mode 100644 index 00000000..4a1cfd29 --- /dev/null +++ b/third_party/sfml/include/SFML/System/SuspendAwareClock.hpp @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Android, chrono-compatible, suspend-aware clock +/// +/// Linux steady clock is represented by `CLOCK_MONOTONIC`. +/// However, this implementation does not work properly for +/// long-running clocks that work in the background when the +/// system is suspended. +/// +/// `SuspendAwareClock` uses `CLOCK_BOOTTIME` which is identical +/// to `CLOCK_MONOTONIC`, except that it also includes any time +/// that the system is suspended. +/// +/// Note: In most cases, `CLOCK_MONOTONIC` is a better choice. +/// Make sure this implementation is required for your use case. +/// +//////////////////////////////////////////////////////////// +struct SFML_SYSTEM_API SuspendAwareClock +{ + //////////////////////////////////////////////////////////// + /// \brief Type traits and static members + /// + /// These type traits and static members meet the requirements + /// of a Clock concept in the C++ Standard. More specifically, + /// TrivialClock requirements are met. Thus, naming convention + /// has been kept consistent to allow for extended use e.g. + /// https://en.cppreference.com/w/cpp/chrono/is_clock + /// + //////////////////////////////////////////////////////////// + using duration = std::chrono::nanoseconds; + using rep = duration::rep; + using period = duration::period; + using time_point = std::chrono::time_point; + + static constexpr bool is_steady = true; // NOLINT(readability-identifier-naming) + + static time_point now() noexcept; +}; + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/Time.hpp b/third_party/sfml/include/SFML/System/Time.hpp new file mode 100644 index 00000000..bca73834 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Time.hpp @@ -0,0 +1,500 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Represents a time value +/// +//////////////////////////////////////////////////////////// +class Time +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Sets the time value to zero. + /// + //////////////////////////////////////////////////////////// + constexpr Time() = default; + + //////////////////////////////////////////////////////////// + /// \brief Construct from `std::chrono::duration` + /// + //////////////////////////////////////////////////////////// + template + constexpr Time(const std::chrono::duration& duration); + + //////////////////////////////////////////////////////////// + /// \brief Return the time value as a number of seconds + /// + /// \return Time in seconds + /// + /// \see `asMilliseconds`, `asMicroseconds` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr float asSeconds() const; + + //////////////////////////////////////////////////////////// + /// \brief Return the time value as a number of milliseconds + /// + /// \return Time in milliseconds + /// + /// \see `asSeconds`, `asMicroseconds` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr std::int32_t asMilliseconds() const; + + //////////////////////////////////////////////////////////// + /// \brief Return the time value as a number of microseconds + /// + /// \return Time in microseconds + /// + /// \see `asSeconds`, `asMilliseconds` + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr std::int64_t asMicroseconds() const; + + //////////////////////////////////////////////////////////// + /// \brief Return the time value as a `std::chrono::duration` + /// + /// \return Time in microseconds + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr std::chrono::microseconds toDuration() const; + + //////////////////////////////////////////////////////////// + /// \brief Implicit conversion to `std::chrono::duration` + /// + /// \return Duration in microseconds + /// + //////////////////////////////////////////////////////////// + template + constexpr operator std::chrono::duration() const; + + //////////////////////////////////////////////////////////// + // Static member data + //////////////////////////////////////////////////////////// + // NOLINTNEXTLINE(readability-identifier-naming) + static const Time Zero; //!< Predefined "zero" time value + +private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::chrono::microseconds m_microseconds{}; //!< Time value stored as microseconds +}; + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Construct a time value from a number of seconds +/// +/// \param amount Number of seconds +/// +/// \return Time value constructed from the amount of seconds +/// +/// \see `milliseconds`, `microseconds` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time seconds(float amount); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Construct a time value from a number of milliseconds +/// +/// \param amount Number of milliseconds +/// +/// \return Time value constructed from the amount of milliseconds +/// +/// \see `seconds`, `microseconds` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time milliseconds(std::int32_t amount); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Construct a time value from a number of microseconds +/// +/// \param amount Number of microseconds +/// +/// \return Time value constructed from the amount of microseconds +/// +/// \see `seconds`, `milliseconds` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time microseconds(std::int64_t amount); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of `operator==` to compare two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `true` if both time values are equal +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator==(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of `operator!=` to compare two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `true` if both time values are different +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator!=(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of `operator<` to compare two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `true` if `left` is lesser than `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator<(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of `operator>` to compare two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `true` if `left` is greater than `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator>(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of `operator<=` to compare two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `true` if `left` is lesser or equal than `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator<=(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of `operator>=` to compare two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `true` if `left` is greater or equal than `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr bool operator>=(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of unary `operator-` to negate a time value +/// +/// \param right Right operand (a time) +/// +/// \return Opposite of the time value +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator-(Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator+` to add two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return Sum of the two times values +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator+(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator+=` to add/assign two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return Sum of the two times values +/// +//////////////////////////////////////////////////////////// +constexpr Time& operator+=(Time& left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator-` to subtract two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return Difference of the two times values +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator-(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator-=` to subtract/assign two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return Difference of the two times values +/// +//////////////////////////////////////////////////////////// +constexpr Time& operator-=(Time& left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator*` to scale a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator*(Time left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator*` to scale a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator*(Time left, std::int64_t right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator*` to scale a time value +/// +/// \param left Left operand (a number) +/// \param right Right operand (a time) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator*(float left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator*` to scale a time value +/// +/// \param left Left operand (a number) +/// \param right Right operand (a time) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator*(std::int64_t left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator*=` to scale/assign a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +constexpr Time& operator*=(Time& left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator*=` to scale/assign a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` multiplied by `right` +/// +//////////////////////////////////////////////////////////// +constexpr Time& operator*=(Time& left, std::int64_t right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator/` to scale a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator/(Time left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator/` to scale a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator/(Time left, std::int64_t right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator/=` to scale/assign a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +constexpr Time& operator/=(Time& left, float right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator/=` to scale/assign a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a number) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +constexpr Time& operator/=(Time& left, std::int64_t right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator/` to compute the ratio of two time values +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `left` divided by `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr float operator/(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator%` to compute remainder of a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `left` modulo `right` +/// +//////////////////////////////////////////////////////////// +[[nodiscard]] constexpr Time operator%(Time left, Time right); + +//////////////////////////////////////////////////////////// +/// \relates Time +/// \brief Overload of binary `operator%=` to compute/assign remainder of a time value +/// +/// \param left Left operand (a time) +/// \param right Right operand (a time) +/// +/// \return `left` modulo `right` +/// +//////////////////////////////////////////////////////////// +constexpr Time& operator%=(Time& left, Time right); + +} // namespace sf + +#include + + +//////////////////////////////////////////////////////////// +/// \class sf::Time +/// \ingroup system +/// +/// `sf::Time` encapsulates a time value in a flexible way. +/// It allows to define a time value either as a number of +/// seconds, milliseconds or microseconds. It also works the +/// other way round: you can read a time value as either +/// a number of seconds, milliseconds or microseconds. It +/// even interoperates with the `` header. You can +/// construct an `sf::Time` from a `chrono::duration` and read +/// any `sf::Time` as a chrono::duration. +/// +/// By using such a flexible interface, the API doesn't +/// impose any fixed type or resolution for time values, +/// and let the user choose its own favorite representation. +/// +/// Time values support the usual mathematical operations: +/// you can add or subtract two times, multiply or divide +/// a time by a number, compare two times, etc. +/// +/// Since they represent a time span and not an absolute time +/// value, times can also be negative. +/// +/// Usage example: +/// \code +/// sf::Time t1 = sf::seconds(0.1f); +/// std::int32_t milli = t1.asMilliseconds(); // 100 +/// +/// sf::Time t2 = sf::milliseconds(30); +/// std::int64_t micro = t2.asMicroseconds(); // 30'000 +/// +/// sf::Time t3 = sf::microseconds(-800'000); +/// float sec = t3.asSeconds(); // -0.8 +/// +/// sf::Time t4 = std::chrono::milliseconds(250); +/// std::chrono::microseconds micro2 = t4.toDuration(); // 250'000us +/// \endcode +/// +/// \code +/// void update(sf::Time elapsed) +/// { +/// position += speed * elapsed.asSeconds(); +/// } +/// +/// update(sf::milliseconds(100)); +/// \endcode +/// +/// \see `sf::Clock` +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Time.inl b/third_party/sfml/include/SFML/System/Time.inl new file mode 100644 index 00000000..ffdaff17 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Time.inl @@ -0,0 +1,284 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // NOLINT(misc-header-include-cycle) + +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +template +constexpr Time::Time(const std::chrono::duration& duration) : m_microseconds(duration) +{ +} + + +//////////////////////////////////////////////////////////// +constexpr float Time::asSeconds() const +{ + return std::chrono::duration(m_microseconds).count(); +} + + +//////////////////////////////////////////////////////////// +constexpr std::int32_t Time::asMilliseconds() const +{ + return std::chrono::duration_cast>(m_microseconds).count(); +} + + +//////////////////////////////////////////////////////////// +constexpr std::int64_t Time::asMicroseconds() const +{ + return m_microseconds.count(); +} + + +//////////////////////////////////////////////////////////// +constexpr std::chrono::microseconds Time::toDuration() const +{ + return m_microseconds; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Time::operator std::chrono::duration() const +{ + return m_microseconds; +} + + +//////////////////////////////////////////////////////////// +constexpr Time seconds(float amount) +{ + return std::chrono::duration_cast(std::chrono::duration(amount)); +} + + +//////////////////////////////////////////////////////////// +constexpr Time milliseconds(std::int32_t amount) +{ + return std::chrono::milliseconds(amount); +} + + +//////////////////////////////////////////////////////////// +constexpr Time microseconds(std::int64_t amount) +{ + return std::chrono::microseconds(amount); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator==(Time left, Time right) +{ + return left.asMicroseconds() == right.asMicroseconds(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator!=(Time left, Time right) +{ + return left.asMicroseconds() != right.asMicroseconds(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator<(Time left, Time right) +{ + return left.asMicroseconds() < right.asMicroseconds(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator>(Time left, Time right) +{ + return left.asMicroseconds() > right.asMicroseconds(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator<=(Time left, Time right) +{ + return left.asMicroseconds() <= right.asMicroseconds(); +} + + +//////////////////////////////////////////////////////////// +constexpr bool operator>=(Time left, Time right) +{ + return left.asMicroseconds() >= right.asMicroseconds(); +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator-(Time right) +{ + return microseconds(-right.asMicroseconds()); +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator+(Time left, Time right) +{ + return microseconds(left.asMicroseconds() + right.asMicroseconds()); +} + + +//////////////////////////////////////////////////////////// +constexpr Time& operator+=(Time& left, Time right) +{ + return left = left + right; +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator-(Time left, Time right) +{ + return microseconds(left.asMicroseconds() - right.asMicroseconds()); +} + + +//////////////////////////////////////////////////////////// +constexpr Time& operator-=(Time& left, Time right) +{ + return left = left - right; +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator*(Time left, float right) +{ + return seconds(left.asSeconds() * right); +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator*(Time left, std::int64_t right) +{ + return microseconds(left.asMicroseconds() * right); +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator*(float left, Time right) +{ + return right * left; +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator*(std::int64_t left, Time right) +{ + return right * left; +} + + +//////////////////////////////////////////////////////////// +constexpr Time& operator*=(Time& left, float right) +{ + return left = left * right; +} + + +//////////////////////////////////////////////////////////// +constexpr Time& operator*=(Time& left, std::int64_t right) +{ + return left = left * right; +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator/(Time left, float right) +{ + assert(right != 0 && "Time::operator/ cannot divide by 0"); + return seconds(left.asSeconds() / right); +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator/(Time left, std::int64_t right) +{ + assert(right != 0 && "Time::operator/ cannot divide by 0"); + return microseconds(left.asMicroseconds() / right); +} + + +//////////////////////////////////////////////////////////// +constexpr Time& operator/=(Time& left, float right) +{ + assert(right != 0 && "Time::operator/= cannot divide by 0"); + return left = left / right; +} + + +//////////////////////////////////////////////////////////// +constexpr Time& operator/=(Time& left, std::int64_t right) +{ + assert(right != 0 && "Time::operator/= cannot divide by 0"); + return left = left / right; +} + + +//////////////////////////////////////////////////////////// +constexpr float operator/(Time left, Time right) +{ + assert(right.asMicroseconds() != 0 && "Time::operator/ cannot divide by 0"); + return left.asSeconds() / right.asSeconds(); +} + + +//////////////////////////////////////////////////////////// +constexpr Time operator%(Time left, Time right) +{ + assert(right.asMicroseconds() != 0 && "Time::operator% cannot modulus by 0"); + return microseconds(left.asMicroseconds() % right.asMicroseconds()); +} + + +//////////////////////////////////////////////////////////// +constexpr Time& operator%=(Time& left, Time right) +{ + assert(right.asMicroseconds() != 0 && "Time::operator%= cannot modulus by 0"); + return left = left % right; +} + + +//////////////////////////////////////////////////////////// +// Static member data +//////////////////////////////////////////////////////////// + +// Note: the 'inline' keyword here is technically not required, but VS2019 fails +// to compile with a bogus "multiple definition" error if not explicitly used. +inline constexpr Time Time::Zero; + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/Utf.hpp b/third_party/sfml/include/SFML/System/Utf.hpp new file mode 100644 index 00000000..6e5edb20 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Utf.hpp @@ -0,0 +1,758 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include + +#include +#include + + +namespace sf +{ +template +class Utf; + +//////////////////////////////////////////////////////////// +/// \brief Specialization of the Utf template for UTF-8 +/// +//////////////////////////////////////////////////////////// +template <> +class Utf<8> +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Decode a single UTF-8 character + /// + /// Decoding a character means finding its unique 32-bits + /// code (called the codepoint) in the Unicode standard. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Codepoint of the decoded UTF-8 character + /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In decode(In begin, In end, char32_t& output, char32_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-8 character + /// + /// Encoding a character means converting a unique 32-bits + /// code (called the codepoint) in the target encoding, UTF-8. + /// + /// \param input Codepoint to encode as UTF-8 + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to UTF-8 (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out encode(char32_t input, Out output, std::uint8_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Advance to the next UTF-8 character + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In next(In begin, In end); + + //////////////////////////////////////////////////////////// + /// \brief Count the number of characters of a UTF-8 sequence + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element, thus the + /// total size can be different from (begin - end). + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static std::size_t count(In begin, In end); + + //////////////////////////////////////////////////////////// + /// \brief Convert an ANSI characters range to UTF-8 + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the `locale` parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromAnsi(In begin, In end, Out output, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Convert a wide characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromWide(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromLatin1(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-8 characters range to ANSI characters + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the `locale` parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toAnsi(In begin, In end, Out output, char replacement = 0, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-8 characters range to wide characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toWide(In begin, In end, Out output, wchar_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-8 characters range to latin-1 (ISO-5589-1) characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toLatin1(In begin, In end, Out output, char replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-8 characters range to UTF-8 + /// + /// This functions does nothing more than a direct copy; + /// it is defined only to provide the same interface as other + /// specializations of the `sf::Utf<>` template, and allow + /// generic code to be written on top of it. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf8(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-8 characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf16(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-8 characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf32(In begin, In end, Out output); +}; + +//////////////////////////////////////////////////////////// +/// \brief Specialization of the Utf template for UTF-16 +/// +//////////////////////////////////////////////////////////// +template <> +class Utf<16> +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Decode a single UTF-16 character + /// + /// Decoding a character means finding its unique 32-bits + /// code (called the codepoint) in the Unicode standard. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Codepoint of the decoded UTF-16 character + /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In decode(In begin, In end, char32_t& output, char32_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-16 character + /// + /// Encoding a character means converting a unique 32-bits + /// code (called the codepoint) in the target encoding, UTF-16. + /// + /// \param input Codepoint to encode as UTF-16 + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to UTF-16 (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out encode(char32_t input, Out output, char16_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Advance to the next UTF-16 character + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In next(In begin, In end); + + //////////////////////////////////////////////////////////// + /// \brief Count the number of characters of a UTF-16 sequence + /// + /// This function is necessary for multi-elements encodings, as + /// a single character may use more than 1 storage element, thus the + /// total size can be different from (begin - end). + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static std::size_t count(In begin, In end); + + //////////////////////////////////////////////////////////// + /// \brief Convert an ANSI characters range to UTF-16 + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the `locale` parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromAnsi(In begin, In end, Out output, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Convert a wide characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromWide(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromLatin1(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to ANSI characters + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the `locale` parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toAnsi(In begin, In end, Out output, char replacement = 0, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to wide characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toWide(In begin, In end, Out output, wchar_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toLatin1(In begin, In end, Out output, char replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-16 characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf8(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-16 characters range to UTF-16 + /// + /// This functions does nothing more than a direct copy; + /// it is defined only to provide the same interface as other + /// specializations of the `sf::Utf<>` template, and allow + /// generic code to be written on top of it. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf16(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-16 characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf32(In begin, In end, Out output); +}; + +//////////////////////////////////////////////////////////// +/// \brief Specialization of the Utf template for UTF-32 +/// +//////////////////////////////////////////////////////////// +template <> +class Utf<32> +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Decode a single UTF-32 character + /// + /// Decoding a character means finding its unique 32-bits + /// code (called the codepoint) in the Unicode standard. + /// For UTF-32, the character value is the same as the codepoint. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Codepoint of the decoded UTF-32 character + /// \param replacement Replacement character to use in case the UTF-8 sequence is invalid + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In decode(In begin, In end, char32_t& output, char32_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-32 character + /// + /// Encoding a character means converting a unique 32-bits + /// code (called the codepoint) in the target encoding, UTF-32. + /// For UTF-32, the codepoint is the same as the character value. + /// + /// \param input Codepoint to encode as UTF-32 + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to UTF-32 (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out encode(char32_t input, Out output, char32_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Advance to the next UTF-32 character + /// + /// This function is trivial for UTF-32, which can store + /// every character in a single storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static In next(In begin, In end); + + //////////////////////////////////////////////////////////// + /// \brief Count the number of characters of a UTF-32 sequence + /// + /// This function is trivial for UTF-32, which can store + /// every character in a single storage element. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// + /// \return Iterator pointing to one past the last read element of the input sequence + /// + //////////////////////////////////////////////////////////// + template + static std::size_t count(In begin, In end); + + //////////////////////////////////////////////////////////// + /// \brief Convert an ANSI characters range to UTF-32 + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the `locale` parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromAnsi(In begin, In end, Out output, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Convert a wide characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromWide(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a latin-1 (ISO-5589-1) characters range to UTF-32 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out fromLatin1(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-32 characters range to ANSI characters + /// + /// The current global locale will be used by default, unless you + /// pass a custom one in the `locale` parameter. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to ANSI (use 0 to skip them) + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toAnsi(In begin, In end, Out output, char replacement = 0, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-32 characters range to wide characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toWide(In begin, In end, Out output, wchar_t replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Convert an UTF-16 characters range to latin-1 (ISO-5589-1) characters + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement for characters not convertible to wide (use 0 to skip them) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toLatin1(In begin, In end, Out output, char replacement = 0); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-32 characters range to UTF-8 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf8(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-32 characters range to UTF-16 + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf16(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Convert a UTF-32 characters range to UTF-32 + /// + /// This functions does nothing more than a direct copy; + /// it is defined only to provide the same interface as other + /// specializations of the `sf::Utf<>` template, and allow + /// generic code to be written on top of it. + /// + /// \param begin Iterator pointing to the beginning of the input sequence + /// \param end Iterator pointing to the end of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out toUtf32(In begin, In end, Out output); + + //////////////////////////////////////////////////////////// + /// \brief Decode a single ANSI character to UTF-32 + /// + /// This function does not exist in other specializations + /// of `sf::Utf<>`, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param input Input ANSI character + /// \param locale Locale to use for conversion + /// + /// \return Converted character + /// + //////////////////////////////////////////////////////////// + template + static char32_t decodeAnsi(In input, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Decode a single wide character to UTF-32 + /// + /// This function does not exist in other specializations + /// of `sf::Utf<>`, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param input Input wide character + /// + /// \return Converted character + /// + //////////////////////////////////////////////////////////// + template + static char32_t decodeWide(In input); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-32 character to ANSI + /// + /// This function does not exist in other specializations + /// of `sf::Utf<>`, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param codepoint Iterator pointing to the beginning of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement if the input character is not convertible to ANSI (use 0 to skip it) + /// \param locale Locale to use for conversion + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out encodeAnsi(char32_t codepoint, Out output, char replacement = 0, const std::locale& locale = {}); + + //////////////////////////////////////////////////////////// + /// \brief Encode a single UTF-32 character to wide + /// + /// This function does not exist in other specializations + /// of `sf::Utf<>`, it is defined for convenience (it is used by + /// several other conversion functions). + /// + /// \param codepoint Iterator pointing to the beginning of the input sequence + /// \param output Iterator pointing to the beginning of the output sequence + /// \param replacement Replacement if the input character is not convertible to wide (use 0 to skip it) + /// + /// \return Iterator to the end of the output sequence which has been written + /// + //////////////////////////////////////////////////////////// + template + static Out encodeWide(char32_t codepoint, Out output, wchar_t replacement = 0); +}; + +// Make type aliases to get rid of the template syntax +using Utf8 = Utf<8>; +using Utf16 = Utf<16>; +using Utf32 = Utf<32>; + +} // namespace sf + +#include + + +//////////////////////////////////////////////////////////// +/// \class sf::Utf +/// \ingroup system +/// +/// Utility class providing generic functions for UTF conversions. +/// +/// `sf::Utf` is a low-level, generic interface for counting, iterating, +/// encoding and decoding Unicode characters and strings. It is able +/// to handle ANSI, wide, latin-1, UTF-8, UTF-16 and UTF-32 encodings. +/// +/// `sf::Utf` functions are all static, these classes are not meant to +/// be instantiated. All the functions are template, so that you +/// can use any character / string type for a given encoding. +/// +/// It has 3 specializations: +/// \li `sf::Utf<8>` (with `sf::Utf8` type alias) +/// \li `sf::Utf<16>` (with `sf::Utf16` type alias) +/// \li `sf::Utf<32>` (with `sf::Utf32` type alias) +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Utf.inl b/third_party/sfml/include/SFML/System/Utf.inl new file mode 100644 index 00000000..d4f42824 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Utf.inl @@ -0,0 +1,829 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // NOLINT(misc-header-include-cycle) + +#include + + +//////////////////////////////////////////////////////////// +// References: +// +// https://www.unicode.org/ +// https://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c +// https://www.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.h +// https://people.w3.org/rishida/scripts/uniview/conversion +// +//////////////////////////////////////////////////////////// + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +template +Out copyBits(In begin, In end, Out output) +{ + using InputType = typename std::iterator_traits::value_type; + using OutputType = typename Out::container_type::value_type; + + static_assert(sizeof(OutputType) >= sizeof(InputType)); + static_assert(std::is_integral_v); + static_assert(std::is_integral_v); + + // The goal is to copy the byte representation of the input into the output type. + // A single static_cast will try to preserve the value as opposed to the byte representation + // which leads to issues when the input is signed and has a negative value. That will get + // wrapped to a very large unsigned value which is incorrect. To address this, we first + // cast the input to its unsigned equivalent then cast that to the destination type which has + // the property of preserving the byte representation of the input. A simple memcpy seems + // like a viable solution but copying the bytes of a type into a larger type yields different + // results on big versus little endian machines so it's not a possibility. + // + // Why do this? For example take the Latin1 character é. It has a byte representation of 0xE9 + // and a signed integer value of -23. If you cast -23 to a char32_t, you get a value of + // 4294967273 which is not a valid Unicode codepoint. What we actually wanted was a char32_t + // with the byte representation 0x000000E9. + while (begin != end) + *output++ = static_cast(static_cast>(*begin++)); + + return output; +} +} // namespace priv + +template +In Utf<8>::decode(In begin, In end, char32_t& output, char32_t replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + // clang-format off + // Some useful precomputed data + static constexpr std::array trailing = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 + }; + + static constexpr std::array offsets = + { + 0x00000000, 0x00003080, 0x000E2080, 0x03C82080, 0xFA082080, 0x82082080 + }; + // clang-format on + + // decode the character + const auto trailingBytes = trailing[static_cast(*begin)]; + if (trailingBytes < std::distance(begin, end)) + { + output = 0; + + // clang-format off + switch (trailingBytes) + { + case 5: output += static_cast(*begin++); output <<= 6; [[fallthrough]]; + case 4: output += static_cast(*begin++); output <<= 6; [[fallthrough]]; + case 3: output += static_cast(*begin++); output <<= 6; [[fallthrough]]; + case 2: output += static_cast(*begin++); output <<= 6; [[fallthrough]]; + case 1: output += static_cast(*begin++); output <<= 6; [[fallthrough]]; + case 0: output += static_cast(*begin++); + } + // clang-format on + + output -= offsets[trailingBytes]; + } + else + { + // Incomplete character + begin = end; + output = replacement; + } + + return begin; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::encode(char32_t input, Out output, std::uint8_t replacement) +{ + // Some useful precomputed data + static constexpr std::array firstBytes = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; + + // encode the character + if ((input > 0x0010FFFF) || ((input >= 0xD800) && (input <= 0xDBFF))) + { + // Invalid character + if (replacement) + *output++ = static_cast(replacement); + } + else + { + // Valid character + + // Get the number of bytes to write + std::size_t bytestoWrite = 1; + + // clang-format off + if (input < 0x80) bytestoWrite = 1; + else if (input < 0x800) bytestoWrite = 2; + else if (input < 0x10000) bytestoWrite = 3; + else if (input <= 0x0010FFFF) bytestoWrite = 4; + // clang-format on + + // Extract the bytes to write + std::array bytes{}; + + // clang-format off + switch (bytestoWrite) + { + case 4: bytes[3] = static_cast((input | 0x80) & 0xBF); input >>= 6; [[fallthrough]]; + case 3: bytes[2] = static_cast((input | 0x80) & 0xBF); input >>= 6; [[fallthrough]]; + case 2: bytes[1] = static_cast((input | 0x80) & 0xBF); input >>= 6; [[fallthrough]]; + case 1: bytes[0] = static_cast (input | firstBytes[bytestoWrite]); + } + // clang-format on + + // Add them to the output + output = priv::copyBits(bytes.data(), bytes.data() + bytestoWrite, output); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +In Utf<8>::next(In begin, In end) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + char32_t codepoint = 0; + return decode(begin, end, codepoint); +} + + +//////////////////////////////////////////////////////////// +template +std::size_t Utf<8>::count(In begin, In end) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + std::size_t length = 0; + while (begin != end) + { + begin = next(begin, end); + ++length; + } + + return length; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::fromAnsi(In begin, In end, Out output, const std::locale& locale) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + while (begin != end) + { + const char32_t codepoint = Utf<32>::decodeAnsi(*begin++, locale); + output = encode(codepoint, output); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::fromWide(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(wchar_t)); + + while (begin != end) + { + const char32_t codepoint = Utf<32>::decodeWide(*begin++); + output = encode(codepoint, output); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::fromLatin1(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while (begin != end) + output = encode(static_cast(*begin++), output); + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::toAnsi(In begin, In end, Out output, char replacement, const std::locale& locale) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + output = Utf<32>::encodeAnsi(codepoint, output, replacement, locale); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::toWide(In begin, In end, Out output, wchar_t replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + output = Utf<32>::encodeWide(codepoint, output, replacement); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::toLatin1(In begin, In end, Out output, char replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + *output++ = codepoint < 256 ? static_cast(codepoint) : replacement; + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::toUtf8(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + return priv::copyBits(begin, end, output); +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::toUtf16(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + output = Utf<16>::encode(codepoint, output); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<8>::toUtf32(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + *output++ = codepoint; + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +In Utf<16>::decode(In begin, In end, char32_t& output, char32_t replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + const char16_t first = *begin++; + + // If it's a surrogate pair, first convert to a single UTF-32 character + if ((first >= 0xD800) && (first <= 0xDBFF)) + { + if (begin != end) + { + const std::uint32_t second = *begin++; + if ((second >= 0xDC00) && (second <= 0xDFFF)) + { + // The second element is valid: convert the two elements to a UTF-32 character + output = ((first - 0xD800u) << 10) + (second - 0xDC00) + 0x0010000; + } + else + { + // Invalid character + output = replacement; + } + } + else + { + // Invalid character + begin = end; + output = replacement; + } + } + else + { + // We can make a direct copy + output = first; + } + + return begin; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::encode(char32_t input, Out output, char16_t replacement) +{ + if (input <= 0xFFFF) + { + // The character can be copied directly, we just need to check if it's in the valid range + if ((input >= 0xD800) && (input <= 0xDFFF)) + { + // Invalid character (this range is reserved) + if (replacement) + *output++ = replacement; + } + else + { + // Valid character directly convertible to a single UTF-16 character + *output++ = static_cast(input); + } + } + else if (input > 0x0010FFFF) + { + // Invalid character (greater than the maximum Unicode value) + if (replacement) + *output++ = replacement; + } + else + { + // The input character will be converted to two UTF-16 elements + input -= 0x0010000; + *output++ = static_cast((input >> 10) + 0xD800); + *output++ = static_cast((input & 0x3FFUL) + 0xDC00); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +In Utf<16>::next(In begin, In end) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + char32_t codepoint = 0; + return decode(begin, end, codepoint); +} + + +//////////////////////////////////////////////////////////// +template +std::size_t Utf<16>::count(In begin, In end) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + std::size_t length = 0; + while (begin != end) + { + begin = next(begin, end); + ++length; + } + + return length; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::fromAnsi(In begin, In end, Out output, const std::locale& locale) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + while (begin != end) + { + const char32_t codepoint = Utf<32>::decodeAnsi(*begin++, locale); + output = encode(codepoint, output); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::fromWide(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(wchar_t)); + + while (begin != end) + { + const char32_t codepoint = Utf<32>::decodeWide(*begin++); + output = encode(codepoint, output); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::fromLatin1(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + return priv::copyBits(begin, end, output); +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::toAnsi(In begin, In end, Out output, char replacement, const std::locale& locale) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + output = Utf<32>::encodeAnsi(codepoint, output, replacement, locale); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::toWide(In begin, In end, Out output, wchar_t replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + output = Utf<32>::encodeWide(codepoint, output, replacement); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::toLatin1(In begin, In end, Out output, char replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while (begin != end) + { + *output++ = *begin < 256 ? static_cast(*begin) : replacement; + ++begin; + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::toUtf8(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + output = Utf<8>::encode(codepoint, output); + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::toUtf16(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + return priv::copyBits(begin, end, output); +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<16>::toUtf32(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char16_t)); + + while (begin != end) + { + char32_t codepoint = 0; + begin = decode(begin, end, codepoint); + *output++ = codepoint; + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +In Utf<32>::decode(In begin, In /*end*/, char32_t& output, char32_t /*replacement*/) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + output = *begin++; + return begin; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::encode(char32_t input, Out output, char32_t /*replacement*/) +{ + *output++ = input; + return output; +} + + +//////////////////////////////////////////////////////////// +template +In Utf<32>::next(In begin, In /*end*/) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + return ++begin; +} + + +//////////////////////////////////////////////////////////// +template +std::size_t Utf<32>::count(In begin, In end) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + return static_cast(end - begin); +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::fromAnsi(In begin, In end, Out output, const std::locale& locale) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + while (begin != end) + *output++ = decodeAnsi(*begin++, locale); + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::fromWide(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(wchar_t)); + + while (begin != end) + *output++ = decodeWide(*begin++); + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::fromLatin1(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char)); + + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + return priv::copyBits(begin, end, output); +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::toAnsi(In begin, In end, Out output, char replacement, const std::locale& locale) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + while (begin != end) + output = encodeAnsi(*begin++, output, replacement, locale); + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::toWide(In begin, In end, Out output, wchar_t replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + while (begin != end) + output = encodeWide(*begin++, output, replacement); + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::toLatin1(In begin, In end, Out output, char replacement) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + // Latin-1 is directly compatible with Unicode encodings, + // and can thus be treated as (a sub-range of) UTF-32 + while (begin != end) + { + *output++ = *begin < 256 ? static_cast(*begin) : replacement; + ++begin; + } + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::toUtf8(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + while (begin != end) + output = Utf<8>::encode(*begin++, output); + + return output; +} + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::toUtf16(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + while (begin != end) + output = Utf<16>::encode(*begin++, output); + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::toUtf32(In begin, In end, Out output) +{ + static_assert(sizeof(decltype(*begin)) == sizeof(char32_t)); + + return priv::copyBits(begin, end, output); +} + + +//////////////////////////////////////////////////////////// +template +char32_t Utf<32>::decodeAnsi(In input, const std::locale& locale) +{ + // Get the facet of the locale which deals with character conversion + const auto& facet = std::use_facet>(locale); + + // Use the facet to convert each character of the input string + return static_cast(facet.widen(input)); +} + + +//////////////////////////////////////////////////////////// +template +char32_t Utf<32>::decodeWide(In input) +{ + // The encoding of wide characters is not well defined and is left to the system; + // however we can safely assume that it is UCS-2 on Windows and + // UCS-4 on Unix systems. + // In both cases, a simple copy is enough (UCS-2 is a subset of UCS-4, + // and UCS-4 *is* UTF-32). + + return static_cast(input); +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::encodeAnsi(char32_t codepoint, Out output, char replacement, const std::locale& locale) +{ + // Get the facet of the locale which deals with character conversion + const auto& facet = std::use_facet>(locale); + + // Use the facet to convert each character of the input string + *output++ = facet.narrow(static_cast(codepoint), replacement); + + return output; +} + + +//////////////////////////////////////////////////////////// +template +Out Utf<32>::encodeWide(char32_t codepoint, Out output, wchar_t replacement) +{ + // The encoding of wide characters is not well defined and is left to the system; + // however we can safely assume that it is UCS-2 on Windows and + // UCS-4 on Unix systems. + // For UCS-2 we need to check if the source characters fits in (UCS-2 is a subset of UCS-4). + // For UCS-4 we can do a direct copy (UCS-4 *is* UTF-32). + + if constexpr (sizeof(wchar_t) == 4) + { + *output++ = static_cast(codepoint); + } + else + { + if ((codepoint <= 0xFFFF) && ((codepoint < 0xD800) || (codepoint > 0xDFFF))) + { + *output++ = static_cast(codepoint); + } + else if (replacement) + { + *output++ = replacement; + } + } + + return output; +} + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/Vector2.hpp b/third_party/sfml/include/SFML/System/Vector2.hpp new file mode 100644 index 00000000..f71d41b6 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Vector2.hpp @@ -0,0 +1,435 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Class template for manipulating +/// 2-dimensional vectors +/// +//////////////////////////////////////////////////////////// +template +class Vector2 +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a `Vector2(0, 0)`. + /// + //////////////////////////////////////////////////////////// + constexpr Vector2() = default; + + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from cartesian coordinates + /// + /// \param x X coordinate + /// \param y Y coordinate + /// + //////////////////////////////////////////////////////////// + constexpr Vector2(T x, T y); + + //////////////////////////////////////////////////////////// + /// \brief Converts the vector to another type of vector + /// + //////////////////////////////////////////////////////////// + template + constexpr explicit operator Vector2() const; + + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from polar coordinates (floating-point) + /// + /// \param r Length of vector (can be negative) + /// \param phi Angle from X axis + /// + /// Note that this constructor is lossy: calling `length()` and `angle()` + /// may return values different to those provided in this constructor. + /// + /// In particular, these transforms can be applied: + /// * `Vector2(r, phi) == Vector2(-r, phi + 180_deg)` + /// * `Vector2(r, phi) == Vector2(r, phi + n * 360_deg)` + /// + //////////////////////////////////////////////////////////// + SFML_SYSTEM_API Vector2(T r, Angle phi); + + //////////////////////////////////////////////////////////// + /// \brief Length of the vector (floating-point). + /// + /// If you are not interested in the actual length, but only in comparisons, consider using `lengthSquared()`. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API T length() const; + + //////////////////////////////////////////////////////////// + /// \brief Square of vector's length. + /// + /// Suitable for comparisons, more efficient than `length()`. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr T lengthSquared() const; + + //////////////////////////////////////////////////////////// + /// \brief Vector with same direction but length 1 (floating-point). + /// + /// \pre `*this` is no zero vector. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API Vector2 normalized() const; + + //////////////////////////////////////////////////////////// + /// \brief Signed angle from `*this` to `rhs` (floating-point). + /// + /// \return The smallest angle which rotates `*this` in positive + /// or negative direction, until it has the same direction as `rhs`. + /// The result has a sign and lies in the range [-180, 180) degrees. + /// \pre Neither `*this` nor `rhs` is a zero vector. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API Angle angleTo(Vector2 rhs) const; + + //////////////////////////////////////////////////////////// + /// \brief Signed angle from +X or (1,0) vector (floating-point). + /// + /// For example, the vector (1,0) corresponds to 0 degrees, (0,1) corresponds to 90 degrees. + /// + /// \return Angle in the range [-180, 180) degrees. + /// \pre This vector is no zero vector. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API Angle angle() const; + + //////////////////////////////////////////////////////////// + /// \brief Rotate by angle \c phi (floating-point). + /// + /// Returns a vector with same length but different direction. + /// + /// In SFML's default coordinate system with +X right and +Y down, + /// this amounts to a clockwise rotation by `phi`. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API Vector2 rotatedBy(Angle phi) const; + + //////////////////////////////////////////////////////////// + /// \brief Projection of this vector onto `axis` (floating-point). + /// + /// \param axis Vector being projected onto. Need not be normalized. + /// \pre `axis` must not have length zero. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API Vector2 projectedOnto(Vector2 axis) const; + + //////////////////////////////////////////////////////////// + /// \brief Returns a perpendicular vector. + /// + /// Returns `*this` rotated by +90 degrees; (x,y) becomes (-y,x). + /// For example, the vector (1,0) is transformed to (0,1). + /// + /// In SFML's default coordinate system with +X right and +Y down, + /// this amounts to a clockwise rotation. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Vector2 perpendicular() const; + + //////////////////////////////////////////////////////////// + /// \brief Dot product of two 2D vectors. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr T dot(Vector2 rhs) const; + + //////////////////////////////////////////////////////////// + /// \brief Z component of the cross product of two 2D vectors. + /// + /// Treats the operands as 3D vectors, computes their cross product + /// and returns the result's Z component (X and Y components are always zero). + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr T cross(Vector2 rhs) const; + + //////////////////////////////////////////////////////////// + /// \brief Component-wise multiplication of `*this` and `rhs`. + /// + /// Computes `(lhs.x*rhs.x, lhs.y*rhs.y)`. + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// This operation is also known as the Hadamard or Schur product. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Vector2 componentWiseMul(Vector2 rhs) const; + + //////////////////////////////////////////////////////////// + /// \brief Component-wise division of `*this` and `rhs`. + /// + /// Computes `(lhs.x/rhs.x, lhs.y/rhs.y)`. + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// + /// \pre Neither component of `rhs` is zero. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Vector2 componentWiseDiv(Vector2 rhs) const; + + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + T x{}; //!< X coordinate of the vector + T y{}; //!< Y coordinate of the vector +}; + +// Define the most common types +using Vector2i = Vector2; +using Vector2u = Vector2; +using Vector2f = Vector2; + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of unary `operator-` +/// +/// \param right Vector to negate +/// +/// \return Member-wise opposite of the vector +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector2 operator-(Vector2 right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator+=` +/// +/// This operator performs a member-wise addition of both vectors, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Reference to `left` +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator+=(Vector2& left, Vector2 right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator-=` +/// +/// This operator performs a member-wise subtraction of both vectors, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Reference to \c left +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator-=(Vector2& left, Vector2 right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator+` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Member-wise addition of both vectors +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector2 operator+(Vector2 left, Vector2 right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator-` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Member-wise subtraction of both vectors +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector2 operator-(Vector2 left, Vector2 right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator*` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Member-wise multiplication by `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector2 operator*(Vector2 left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator*` +/// +/// \param left Left operand (a scalar value) +/// \param right Right operand (a vector) +/// +/// \return Member-wise multiplication by `left` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector2 operator*(T left, Vector2 right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator*=` +/// +/// This operator performs a member-wise multiplication by `right`, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Reference to `left` +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator*=(Vector2& left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator/` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Member-wise division by `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector2 operator/(Vector2 left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator/=` +/// +/// This operator performs a member-wise division by `right`, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Reference to `left` +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator/=(Vector2& left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator==` +/// +/// This operator compares strict equality between two vectors. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return `true` if `left` is equal to `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr bool operator==(Vector2 left, Vector2 right); + +//////////////////////////////////////////////////////////// +/// \relates Vector2 +/// \brief Overload of binary `operator!=` +/// +/// This operator compares strict difference between two vectors. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return `true` if `left` is not equal to `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr bool operator!=(Vector2 left, Vector2 right); + +} // namespace sf + +#include + + +//////////////////////////////////////////////////////////// +/// \class sf::Vector2 +/// \ingroup system +/// +/// `sf::Vector2` is a simple class that defines a mathematical +/// vector with two coordinates (x and y). It can be used to +/// represent anything that has two dimensions: a size, a point, +/// a velocity, a scale, etc. +/// +/// The API provides basic arithmetic (addition, subtraction, scale), as +/// well as more advanced geometric operations, such as dot/cross products, +/// length and angle computations, projections, rotations, etc. +/// +/// The template parameter T is the type of the coordinates. It +/// can be any type that supports arithmetic operations (+, -, /, *) +/// and comparisons (==, !=), for example int or float. +/// Note that some operations are only meaningful for vectors where T is +/// a floating point type (e.g. float or double), often because +/// results cannot be represented accurately with integers. +/// The method documentation mentions "(floating-point)" in those cases. +/// +/// You generally don't have to care about the templated form (`sf::Vector2`), +/// the most common specializations have special type aliases: +/// \li `sf::Vector2` is `sf::Vector2f` +/// \li `sf::Vector2` is `sf::Vector2i` +/// \li `sf::Vector2` is `sf::Vector2u` +/// +/// The `sf::Vector2` class has a simple interface, its x and y members +/// can be accessed directly (there are no accessors like setX(), getX()). +/// +/// Usage example: +/// \code +/// sf::Vector2f v(16.5f, 24.f); +/// v.x = 18.2f; +/// float y = v.y; +/// +/// sf::Vector2f w = v * 5.f; +/// sf::Vector2f u; +/// u = v + w; +/// +/// float s = v.dot(w); +/// +/// bool different = (v != u); +/// \endcode +/// +/// Note: for 3-dimensional vectors, see `sf::Vector3`. +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Vector2.inl b/third_party/sfml/include/SFML/System/Vector2.inl new file mode 100644 index 00000000..6a0ce697 --- /dev/null +++ b/third_party/sfml/include/SFML/System/Vector2.inl @@ -0,0 +1,217 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // NOLINT(misc-header-include-cycle) + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#endif +template +constexpr Vector2::Vector2(T x, T y) : x(x), y(y) +{ +} +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + +//////////////////////////////////////////////////////////// +template +template +constexpr Vector2::operator Vector2() const +{ + return Vector2(static_cast(x), static_cast(y)); +} + + +//////////////////////////////////////////////////////////// +template +constexpr T Vector2::lengthSquared() const +{ + return dot(*this); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 Vector2::perpendicular() const +{ + return Vector2(-y, x); +} + + +//////////////////////////////////////////////////////////// +template +constexpr T Vector2::dot(Vector2 rhs) const +{ + return x * rhs.x + y * rhs.y; +} + + +//////////////////////////////////////////////////////////// +template +constexpr T Vector2::cross(Vector2 rhs) const +{ + return x * rhs.y - y * rhs.x; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 Vector2::componentWiseMul(Vector2 rhs) const +{ + return Vector2(x * rhs.x, y * rhs.y); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 Vector2::componentWiseDiv(Vector2 rhs) const +{ + assert(rhs.x != 0 && "Vector2::componentWiseDiv() cannot divide by 0"); + assert(rhs.y != 0 && "Vector2::componentWiseDiv() cannot divide by 0"); + return Vector2(x / rhs.x, y / rhs.y); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 operator-(Vector2 right) +{ + return Vector2(-right.x, -right.y); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator+=(Vector2& left, Vector2 right) +{ + left.x += right.x; + left.y += right.y; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator-=(Vector2& left, Vector2 right) +{ + left.x -= right.x; + left.y -= right.y; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 operator+(Vector2 left, Vector2 right) +{ + return Vector2(left.x + right.x, left.y + right.y); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 operator-(Vector2 left, Vector2 right) +{ + return Vector2(left.x - right.x, left.y - right.y); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 operator*(Vector2 left, T right) +{ + return Vector2(left.x * right, left.y * right); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 operator*(T left, Vector2 right) +{ + return Vector2(right.x * left, right.y * left); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator*=(Vector2& left, T right) +{ + left.x *= right; + left.y *= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2 operator/(Vector2 left, T right) +{ + assert(right != 0 && "Vector2::operator/ cannot divide by 0"); + return Vector2(left.x / right, left.y / right); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector2& operator/=(Vector2& left, T right) +{ + assert(right != 0 && "Vector2::operator/= cannot divide by 0"); + left.x /= right; + left.y /= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr bool operator==(Vector2 left, Vector2 right) +{ + return (left.x == right.x) && (left.y == right.y); +} + + +//////////////////////////////////////////////////////////// +template +constexpr bool operator!=(Vector2 left, Vector2 right) +{ + return !(left == right); +} + +} // namespace sf diff --git a/third_party/sfml/include/SFML/System/Vector3.hpp b/third_party/sfml/include/SFML/System/Vector3.hpp new file mode 100644 index 00000000..8fa5308f --- /dev/null +++ b/third_party/sfml/include/SFML/System/Vector3.hpp @@ -0,0 +1,356 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +/// \brief Utility template class for manipulating +/// 3-dimensional vectors +/// +//////////////////////////////////////////////////////////// +template +class Vector3 +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a `Vector3(0, 0, 0)`. + /// + //////////////////////////////////////////////////////////// + constexpr Vector3() = default; + + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from its coordinates + /// + /// \param x X coordinate + /// \param y Y coordinate + /// \param z Z coordinate + /// + //////////////////////////////////////////////////////////// + constexpr Vector3(T x, T y, T z); + + //////////////////////////////////////////////////////////// + /// \brief Converts the vector to another type of vector + /// + //////////////////////////////////////////////////////////// + template + constexpr explicit operator Vector3() const; + + //////////////////////////////////////////////////////////// + /// \brief Length of the vector (floating-point). + /// + /// If you are not interested in the actual length, but only in comparisons, consider using `lengthSquared()`. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API T length() const; + + //////////////////////////////////////////////////////////// + /// \brief Square of vector's length. + /// + /// Suitable for comparisons, more efficient than `length()`. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr T lengthSquared() const; + + //////////////////////////////////////////////////////////// + /// \brief Vector with same direction but length 1 (floating-point). + /// + /// \pre `*this` is no zero vector. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] SFML_SYSTEM_API Vector3 normalized() const; + + //////////////////////////////////////////////////////////// + /// \brief Dot product of two 3D vectors. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr T dot(const Vector3& rhs) const; + + //////////////////////////////////////////////////////////// + /// \brief Cross product of two 3D vectors. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Vector3 cross(const Vector3& rhs) const; + + //////////////////////////////////////////////////////////// + /// \brief Component-wise multiplication of `*this` and `rhs`. + /// + /// Computes `(lhs.x*rhs.x, lhs.y*rhs.y, lhs.z*rhs.z)`. + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// This operation is also known as the Hadamard or Schur product. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Vector3 componentWiseMul(const Vector3& rhs) const; + + //////////////////////////////////////////////////////////// + /// \brief Component-wise division of `*this` and `rhs`. + /// + /// Computes `(lhs.x/rhs.x, lhs.y/rhs.y, lhs.z/rhs.z)`. + /// + /// Scaling is the most common use case for component-wise multiplication/division. + /// + /// \pre Neither component of `rhs` is zero. + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] constexpr Vector3 componentWiseDiv(const Vector3& rhs) const; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + T x{}; //!< X coordinate of the vector + T y{}; //!< Y coordinate of the vector + T z{}; //!< Z coordinate of the vector +}; + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of unary `operator-` +/// +/// \param left Vector to negate +/// +/// \return Member-wise opposite of the vector +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector3 operator-(const Vector3& left); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator+=` +/// +/// This operator performs a member-wise addition of both vectors, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Reference to `left` +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator+=(Vector3& left, const Vector3& right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator-=` +/// +/// This operator performs a member-wise subtraction of both vectors, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Reference to `left` +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator-=(Vector3& left, const Vector3& right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator+` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Member-wise addition of both vectors +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector3 operator+(const Vector3& left, const Vector3& right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator-` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return Member-wise subtraction of both vectors +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector3 operator-(const Vector3& left, const Vector3& right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator*` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Member-wise multiplication by `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector3 operator*(const Vector3& left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator*` +/// +/// \param left Left operand (a scalar value) +/// \param right Right operand (a vector) +/// +/// \return Member-wise multiplication by `left` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector3 operator*(T left, const Vector3& right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator*=` +/// +/// This operator performs a member-wise multiplication by `right`, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Reference to `left` +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator*=(Vector3& left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator/` +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Member-wise division by `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr Vector3 operator/(const Vector3& left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator/=` +/// +/// This operator performs a member-wise division by `right`, +/// and assigns the result to `left`. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a scalar value) +/// +/// \return Reference to `left` +/// +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator/=(Vector3& left, T right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator==` +/// +/// This operator compares strict equality between two vectors. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return `true` if `left` is equal to `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr bool operator==(const Vector3& left, const Vector3& right); + +//////////////////////////////////////////////////////////// +/// \relates Vector3 +/// \brief Overload of binary `operator!=` +/// +/// This operator compares strict difference between two vectors. +/// +/// \param left Left operand (a vector) +/// \param right Right operand (a vector) +/// +/// \return `true` if `left` is not equal to `right` +/// +//////////////////////////////////////////////////////////// +template +[[nodiscard]] constexpr bool operator!=(const Vector3& left, const Vector3& right); + +// Aliases for the most common types +using Vector3i = Vector3; +using Vector3f = Vector3; + +} // namespace sf + +#include + + +//////////////////////////////////////////////////////////// +/// \class sf::Vector3 +/// \ingroup system +/// +/// `sf::Vector3` is a simple class that defines a mathematical +/// vector with three coordinates (x, y and z). It can be used to +/// represent anything that has three dimensions: a size, a point, +/// a velocity, etc. +/// +/// The template parameter T is the type of the coordinates. It +/// can be any type that supports arithmetic operations (+, -, /, *) +/// and comparisons (==, !=), for example int or float. +/// Note that some operations are only meaningful for vectors where T is +/// a floating point type (e.g. float or double), often because +/// results cannot be represented accurately with integers. +/// The method documentation mentions "(floating-point)" in those cases. +/// +/// You generally don't have to care about the templated form (`sf::Vector3`), +/// the most common specializations have special type aliases: +/// \li `sf::Vector3` is `sf::Vector3f` +/// \li `sf::Vector3` is `sf::Vector3i` +/// +/// The `sf::Vector3` class has a small and simple interface, its x, y and z members +/// can be accessed directly (there are no accessors like `setX()`, `getX()`). +/// +/// Usage example: +/// \code +/// sf::Vector3f v(16.5f, 24.f, -3.2f); +/// v.x = 18.2f; +/// float y = v.y; +/// +/// sf::Vector3f w = v * 5.f; +/// sf::Vector3f u; +/// u = v + w; +/// +/// float s = v.dot(w); +/// sf::Vector3f t = v.cross(w); +/// +/// bool different = (v != u); +/// \endcode +/// +/// Note: for 2-dimensional vectors, see `sf::Vector2`. +/// +//////////////////////////////////////////////////////////// diff --git a/third_party/sfml/include/SFML/System/Vector3.inl b/third_party/sfml/include/SFML/System/Vector3.inl new file mode 100644 index 00000000..e24b9a8f --- /dev/null +++ b/third_party/sfml/include/SFML/System/Vector3.inl @@ -0,0 +1,214 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include // NOLINT(misc-header-include-cycle) + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#endif +template +constexpr Vector3::Vector3(T x, T y, T z) : x(x), y(y), z(z) +{ +} +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + +//////////////////////////////////////////////////////////// +template +template +constexpr Vector3::operator Vector3() const +{ + return Vector3(static_cast(x), static_cast(y), static_cast(z)); +} + + +//////////////////////////////////////////////////////////// +template +constexpr T Vector3::lengthSquared() const +{ + return dot(*this); +} + + +//////////////////////////////////////////////////////////// +template +constexpr T Vector3::dot(const Vector3& rhs) const +{ + return x * rhs.x + y * rhs.y + z * rhs.z; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 Vector3::cross(const Vector3& rhs) const +{ + return Vector3((y * rhs.z) - (z * rhs.y), (z * rhs.x) - (x * rhs.z), (x * rhs.y) - (y * rhs.x)); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 Vector3::componentWiseMul(const Vector3& rhs) const +{ + return Vector3(x * rhs.x, y * rhs.y, z * rhs.z); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 Vector3::componentWiseDiv(const Vector3& rhs) const +{ + assert(rhs.x != 0 && "Vector3::componentWiseDiv() cannot divide by 0"); + assert(rhs.y != 0 && "Vector3::componentWiseDiv() cannot divide by 0"); + assert(rhs.z != 0 && "Vector3::componentWiseDiv() cannot divide by 0"); + return Vector3(x / rhs.x, y / rhs.y, z / rhs.z); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 operator-(const Vector3& left) +{ + return Vector3(-left.x, -left.y, -left.z); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator+=(Vector3& left, const Vector3& right) +{ + left.x += right.x; + left.y += right.y; + left.z += right.z; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator-=(Vector3& left, const Vector3& right) +{ + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 operator+(const Vector3& left, const Vector3& right) +{ + return Vector3(left.x + right.x, left.y + right.y, left.z + right.z); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 operator-(const Vector3& left, const Vector3& right) +{ + return Vector3(left.x - right.x, left.y - right.y, left.z - right.z); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 operator*(const Vector3& left, T right) +{ + return Vector3(left.x * right, left.y * right, left.z * right); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 operator*(T left, const Vector3& right) +{ + return Vector3(right.x * left, right.y * left, right.z * left); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator*=(Vector3& left, T right) +{ + left.x *= right; + left.y *= right; + left.z *= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3 operator/(const Vector3& left, T right) +{ + assert(right != 0 && "Vector3::operator/ cannot divide by 0"); + return Vector3(left.x / right, left.y / right, left.z / right); +} + + +//////////////////////////////////////////////////////////// +template +constexpr Vector3& operator/=(Vector3& left, T right) +{ + assert(right != 0 && "Vector3::operator/= cannot divide by 0"); + left.x /= right; + left.y /= right; + left.z /= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template +constexpr bool operator==(const Vector3& left, const Vector3& right) +{ + return (left.x == right.x) && (left.y == right.y) && (left.z == right.z); +} + + +//////////////////////////////////////////////////////////// +template +constexpr bool operator!=(const Vector3& left, const Vector3& right) +{ + return !(left == right); +} + +} // namespace sf diff --git a/third_party/sfml/license.md b/third_party/sfml/license.md new file mode 100644 index 00000000..8776d1e9 --- /dev/null +++ b/third_party/sfml/license.md @@ -0,0 +1,9 @@ +Copyright (C) 2007-2025 Laurent Gomila - laurent@sfml-dev.org + +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. diff --git a/third_party/sfml/src/SFML/Network/CMakeLists.txt b/third_party/sfml/src/SFML/Network/CMakeLists.txt new file mode 100644 index 00000000..02776016 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/CMakeLists.txt @@ -0,0 +1,59 @@ +set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +set(INCROOT ${PROJECT_SOURCE_DIR}/include/SFML/Network) +set(SRCROOT ${PROJECT_SOURCE_DIR}/src/SFML/Network) + +# all source files +set(SRC + ${INCROOT}/../Network.hpp + ${INCROOT}/Export.hpp + ${INCROOT}/Ftp.hpp + ${INCROOT}/Http.hpp + ${INCROOT}/IpAddress.hpp + ${INCROOT}/Packet.hpp + ${INCROOT}/SocketHandle.hpp + ${INCROOT}/Socket.hpp + ${INCROOT}/SocketSelector.hpp + ${INCROOT}/TcpListener.hpp + ${INCROOT}/TcpSocket.hpp + ${INCROOT}/UdpSocket.hpp + ${SRCROOT}/Ftp.cpp + ${SRCROOT}/Http.cpp + ${SRCROOT}/IpAddress.cpp + ${SRCROOT}/Packet.cpp + ${SRCROOT}/Socket.cpp + ${SRCROOT}/SocketImpl.hpp + ${SRCROOT}/SocketSelector.cpp + ${SRCROOT}/TcpListener.cpp + ${SRCROOT}/TcpSocket.cpp + ${SRCROOT}/UdpSocket.cpp +) + +# add platform specific sources +if(WIN32) + set(SRC + ${SRC} + ${SRCROOT}/Win32/SocketImpl.cpp + ) +else() + set(SRC + ${SRC} + ${SRCROOT}/Unix/SocketImpl.cpp + ) +endif() + +source_group("" FILES ${SRC}) + +# define the sfml-network target +add_library(sfml-network ${SRC}) + +target_include_directories(sfml-network PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_SOURCE_DIR}/../../../include) + +target_compile_definitions(sfml-network PRIVATE SFML_STATIC) +target_compile_definitions(sfml-network INTERFACE SFML_STATIC) + +# setup dependencies +target_link_libraries(sfml-network PUBLIC sfml-system) +if(WIN32) + target_link_libraries(sfml-network PRIVATE ws2_32) +endif() diff --git a/third_party/sfml/src/SFML/Network/Ftp.cpp b/third_party/sfml/src/SFML/Network/Ftp.cpp new file mode 100644 index 00000000..cdb0bc18 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/Ftp.cpp @@ -0,0 +1,643 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +class Ftp::DataChannel +{ +public: + //////////////////////////////////////////////////////////// + explicit DataChannel(Ftp& owner); + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + //////////////////////////////////////////////////////////// + DataChannel(const DataChannel&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy assignment + /// + //////////////////////////////////////////////////////////// + DataChannel& operator=(const DataChannel&) = delete; + + //////////////////////////////////////////////////////////// + Ftp::Response open(Ftp::TransferMode mode); + + //////////////////////////////////////////////////////////// + void send(std::istream& stream); + + //////////////////////////////////////////////////////////// + void receive(std::ostream& stream); + +private: + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Ftp& m_ftp; //!< Reference to the owner Ftp instance + TcpSocket m_dataSocket; //!< Socket used for data transfers +}; + + +//////////////////////////////////////////////////////////// +Ftp::Response::Response(Status code, std::string message) : m_status(code), m_message(std::move(message)) +{ +} + + +//////////////////////////////////////////////////////////// +bool Ftp::Response::isOk() const +{ + return static_cast(m_status) < 400; +} + + +//////////////////////////////////////////////////////////// +Ftp::Response::Status Ftp::Response::getStatus() const +{ + return m_status; +} + + +//////////////////////////////////////////////////////////// +const std::string& Ftp::Response::getMessage() const +{ + return m_message; +} + + +//////////////////////////////////////////////////////////// +Ftp::DirectoryResponse::DirectoryResponse(const Ftp::Response& response) : Ftp::Response(response) +{ + if (isOk()) + { + // Extract the directory from the server response + const std::string::size_type begin = getMessage().find('"', 0); + const std::string::size_type end = getMessage().find('"', begin + 1); + m_directory = getMessage().substr(begin + 1, end - begin - 1); + } +} + + +//////////////////////////////////////////////////////////// +const std::filesystem::path& Ftp::DirectoryResponse::getDirectory() const +{ + return m_directory; +} + + +//////////////////////////////////////////////////////////// +Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::string& data) : Ftp::Response(response) +{ + if (isOk()) + { + // Fill the array of strings + std::string::size_type lastPos = 0; + for (std::string::size_type pos = data.find("\r\n"); pos != std::string::npos; pos = data.find("\r\n", lastPos)) + { + m_listing.push_back(data.substr(lastPos, pos - lastPos)); + lastPos = pos + 2; + } + } +} + + +//////////////////////////////////////////////////////////// +const std::vector& Ftp::ListingResponse::getListing() const +{ + return m_listing; +} + + +//////////////////////////////////////////////////////////// +Ftp::~Ftp() +{ + (void)disconnect(); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::connect(IpAddress server, unsigned short port, Time timeout) +{ + // Connect to the server + if (m_commandSocket.connect(server, port, timeout) != Socket::Status::Done) + return Response(Response::Status::ConnectionFailed); + + // Get the response to the connection + return getResponse(); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::login() +{ + return login("anonymous", "user@sfml-dev.org"); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::login(const std::string& name, const std::string& password) +{ + Response response = sendCommand("USER", name); + if (response.isOk()) + response = sendCommand("PASS", password); + + return response; +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::disconnect() +{ + // Send the exit command + Response response = sendCommand("QUIT"); + if (response.isOk()) + m_commandSocket.disconnect(); + + return response; +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::keepAlive() +{ + return sendCommand("NOOP"); +} + + +//////////////////////////////////////////////////////////// +Ftp::DirectoryResponse Ftp::getWorkingDirectory() +{ + return {sendCommand("PWD")}; +} + + +//////////////////////////////////////////////////////////// +Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory) +{ + // Open a data channel on default port (20) using ASCII transfer mode + std::ostringstream directoryData; + DataChannel data(*this); + Response response = data.open(TransferMode::Ascii); + if (response.isOk()) + { + // Tell the server to send us the listing + response = sendCommand("NLST", directory); + if (response.isOk()) + { + // Receive the listing + data.receive(directoryData); + + // Get the response from the server + response = getResponse(); + } + } + + return {response, directoryData.str()}; +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::changeDirectory(const std::string& directory) +{ + return sendCommand("CWD", directory); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::parentDirectory() +{ + return sendCommand("CDUP"); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::createDirectory(const std::string& name) +{ + return sendCommand("MKD", name); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::deleteDirectory(const std::string& name) +{ + return sendCommand("RMD", name); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::renameFile(const std::filesystem::path& file, const std::filesystem::path& newName) +{ + Response response = sendCommand("RNFR", file.string()); + if (response.isOk()) + response = sendCommand("RNTO", newName.string()); + + return response; +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::deleteFile(const std::filesystem::path& name) +{ + return sendCommand("DELE", name.string()); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::download(const std::filesystem::path& remoteFile, const std::filesystem::path& localPath, TransferMode mode) +{ + // Open a data channel using the given transfer mode + DataChannel data(*this); + Response response = data.open(mode); + if (response.isOk()) + { + // Tell the server to start the transfer + response = sendCommand("RETR", remoteFile.string()); + if (response.isOk()) + { + // Create the file and truncate it if necessary + const std::filesystem::path filepath = localPath / remoteFile.filename(); + std::ofstream file(filepath, std::ios_base::binary | std::ios_base::trunc); + if (!file) + return Response(Response::Status::InvalidFile); + + // Receive the file data + data.receive(file); + + // Close the file + file.close(); + + // Get the response from the server + response = getResponse(); + + // If the download was unsuccessful, delete the partial file + if (!response.isOk()) + std::filesystem::remove(filepath); + } + } + + return response; +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::upload(const std::filesystem::path& localFile, + const std::filesystem::path& remotePath, + TransferMode mode, + bool append) +{ + // Get the contents of the file to send + std::ifstream file(localFile, std::ios_base::binary); + if (!file) + return Response(Response::Status::InvalidFile); + + // Open a data channel using the given transfer mode + DataChannel data(*this); + Response response = data.open(mode); + if (response.isOk()) + { + // Tell the server to start the transfer + response = sendCommand(append ? "APPE" : "STOR", (remotePath / localFile.filename()).string()); + if (response.isOk()) + { + // Send the file data + data.send(file); + + // Get the response from the server + response = getResponse(); + } + } + + return response; +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::sendCommand(const std::string& command, const std::string& parameter) +{ + // Build the command string + const std::string commandStr = parameter.empty() ? command + "\r\n" : command + " " + parameter + "\r\n"; + + // Send it to the server + if (m_commandSocket.send(commandStr.c_str(), commandStr.length()) != Socket::Status::Done) + return Response(Response::Status::ConnectionClosed); + + // Get the response + return getResponse(); +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::getResponse() +{ + // We'll use a variable to keep track of the last valid code. + // It is useful in case of multi-lines responses, because the end of such a response + // will start by the same code + unsigned int lastCode = 0; + bool isInsideMultiline = false; + std::string message; + + for (;;) + { + // Receive the response from the server + std::array buffer{}; + std::size_t length = 0; + + if (m_receiveBuffer.empty()) + { + if (m_commandSocket.receive(buffer.data(), buffer.size(), length) != Socket::Status::Done) + return Response(Response::Status::ConnectionClosed); + } + else + { + std::copy(m_receiveBuffer.begin(), m_receiveBuffer.end(), buffer.data()); + length = m_receiveBuffer.size(); + m_receiveBuffer.clear(); + } + + // There can be several lines inside the received buffer, extract them all + std::istringstream in(std::string(buffer.data(), length), std::ios_base::binary); + while (in) + { + // Try to extract the code + unsigned int code = 0; + if (in >> code) + { + // Extract the separator + char separator = 0; + in.get(separator); + + // The '-' character means a multiline response + if ((separator == '-') && !isInsideMultiline) + { + // Set the multiline flag + isInsideMultiline = true; + + // Keep track of the code + if (lastCode == 0) + lastCode = code; + + // Extract the line + std::getline(in, message); + + // Remove the ending '\r' (all lines are terminated by "\r\n") + message.erase(message.length() - 1); + message = separator + message + "\n"; + } + else + { + // We must make sure that the code is the same, otherwise it means + // we haven't reached the end of the multiline response + if ((separator != '-') && ((code == lastCode) || (lastCode == 0))) + { + // Extract the line + std::string line; + std::getline(in, line); + + // Remove the ending '\r' (all lines are terminated by "\r\n") + line.erase(line.length() - 1); + + // Append it to the message + if (code == lastCode) + { + std::ostringstream out; + out << code << separator << line; + message += out.str(); + } + else + { + message = separator + line; + } + + // Save the remaining data for the next time getResponse() is called + m_receiveBuffer.assign(buffer.data() + static_cast(in.tellg()), + length - static_cast(in.tellg())); + + // Return the response code and message + return Response(static_cast(code), message); + } + + // The line we just read was actually not a response, + // only a new part of the current multiline response + + // Extract the line + std::string line; + std::getline(in, line); + + if (!line.empty()) + { + // Remove the ending '\r' (all lines are terminated by "\r\n") + line.erase(line.length() - 1); + + // Append it to the current message + std::ostringstream out; + out << code << separator << line << '\n'; + message += out.str(); + } + } + } + else if (lastCode != 0) + { + // It seems we are in the middle of a multiline response + + // Clear the error bits of the stream + in.clear(); + + // Extract the line + std::string line; + std::getline(in, line); + + if (!line.empty()) + { + // Remove the ending '\r' (all lines are terminated by "\r\n") + line.erase(line.length() - 1); + + // Append it to the current message + message += line + "\n"; + } + } + else + { + // Error: cannot extract the code, and we are not in a multiline response + return Response(Response::Status::InvalidResponse); + } + } + } + + // We never reach there +} + + +//////////////////////////////////////////////////////////// +Ftp::DataChannel::DataChannel(Ftp& owner) : m_ftp(owner) +{ +} + + +//////////////////////////////////////////////////////////// +Ftp::Response Ftp::DataChannel::open(Ftp::TransferMode mode) +{ + // Open a data connection in active mode (we connect to the server) + Ftp::Response response = m_ftp.sendCommand("PASV"); + if (response.isOk()) + { + // Extract the connection address and port from the response + const std::string::size_type begin = response.getMessage().find_first_of("0123456789"); + if (begin != std::string::npos) + { + std::array data{}; + std::string str = response.getMessage().substr(begin); + std::size_t index = 0; + for (unsigned char& datum : data) + { + // Extract the current number + while (std::isdigit(str[index])) + { + datum = static_cast( + static_cast(datum * 10) + static_cast(str[index] - '0')); + ++index; + } + + // Skip separator + ++index; + } + + // Reconstruct connection port and address + const auto port = static_cast(data[4] * 256 + data[5]); + const IpAddress address(data[0], data[1], data[2], data[3]); + + // Connect the data channel to the server + if (m_dataSocket.connect(address, port) == Socket::Status::Done) + { + // Translate the transfer mode to the corresponding FTP parameter + std::string modeStr; + switch (mode) + { + case Ftp::TransferMode::Binary: + modeStr = "I"; + break; + case Ftp::TransferMode::Ascii: + modeStr = "A"; + break; + case Ftp::TransferMode::Ebcdic: + modeStr = "E"; + break; + } + + // Set the transfer mode + response = m_ftp.sendCommand("TYPE", modeStr); + } + else + { + // Failed to connect to the server + response = Ftp::Response(Ftp::Response::Status::ConnectionFailed); + } + } + } + + return response; +} + + +//////////////////////////////////////////////////////////// +void Ftp::DataChannel::receive(std::ostream& stream) +{ + // Receive data + std::array buffer{}; + std::size_t received = 0; + while (m_dataSocket.receive(buffer.data(), buffer.size(), received) == Socket::Status::Done) + { + stream.write(buffer.data(), static_cast(received)); + + if (!stream.good()) + { + err() << "FTP Error: Writing to the file has failed" << std::endl; + break; + } + } + + // Close the data socket + m_dataSocket.disconnect(); +} + + +//////////////////////////////////////////////////////////// +void Ftp::DataChannel::send(std::istream& stream) +{ + // Send data + std::array buffer{}; + std::size_t count = 0; + + for (;;) + { + // read some data from the stream + stream.read(buffer.data(), buffer.size()); + + if (!stream.good() && !stream.eof()) + { + err() << "FTP Error: Reading from the file has failed" << std::endl; + break; + } + + count = static_cast(stream.gcount()); + + if (count > 0) + { + // we could read more data from the stream: send them + if (m_dataSocket.send(buffer.data(), count) != Socket::Status::Done) + break; + } + else + { + // no more data: exit the loop + break; + } + } + + // Close the data socket + m_dataSocket.disconnect(); +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/Http.cpp b/third_party/sfml/src/SFML/Network/Http.cpp new file mode 100644 index 00000000..b0e132cc --- /dev/null +++ b/third_party/sfml/src/SFML/Network/Http.cpp @@ -0,0 +1,399 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +Http::Request::Request(const std::string& uri, Method method, const std::string& body) : m_method(method) +{ + setUri(uri); + setBody(body); +} + + +//////////////////////////////////////////////////////////// +void Http::Request::setField(const std::string& field, const std::string& value) +{ + m_fields[toLower(field)] = value; +} + + +//////////////////////////////////////////////////////////// +void Http::Request::setMethod(Http::Request::Method method) +{ + m_method = method; +} + + +//////////////////////////////////////////////////////////// +void Http::Request::setUri(const std::string& uri) +{ + m_uri = uri; + + // Make sure it starts with a '/' + if (m_uri.empty() || (m_uri[0] != '/')) + m_uri.insert(m_uri.begin(), '/'); +} + + +//////////////////////////////////////////////////////////// +void Http::Request::setHttpVersion(unsigned int major, unsigned int minor) +{ + m_majorVersion = major; + m_minorVersion = minor; +} + + +//////////////////////////////////////////////////////////// +void Http::Request::setBody(const std::string& body) +{ + m_body = body; +} + + +//////////////////////////////////////////////////////////// +std::string Http::Request::prepare() const +{ + std::ostringstream out; + + // Convert the method to its string representation + std::string method; + switch (m_method) + { + case Method::Get: + method = "GET"; + break; + case Method::Post: + method = "POST"; + break; + case Method::Head: + method = "HEAD"; + break; + case Method::Put: + method = "PUT"; + break; + case Method::Delete: + method = "DELETE"; + break; + } + + // Write the first line containing the request type + out << method << " " << m_uri << " "; + out << "HTTP/" << m_majorVersion << "." << m_minorVersion << "\r\n"; + + // Write fields + for (const auto& [fieldKey, fieldValue] : m_fields) + { + out << fieldKey << ": " << fieldValue << "\r\n"; + } + + // Use an extra \r\n to separate the header from the body + out << "\r\n"; + + // Add the body + out << m_body; + + return out.str(); +} + + +//////////////////////////////////////////////////////////// +bool Http::Request::hasField(const std::string& field) const +{ + return m_fields.find(toLower(field)) != m_fields.end(); +} + + +//////////////////////////////////////////////////////////// +const std::string& Http::Response::getField(const std::string& field) const +{ + if (const auto it = m_fields.find(toLower(field)); it != m_fields.end()) + { + return it->second; + } + + static const std::string empty; + return empty; +} + + +//////////////////////////////////////////////////////////// +Http::Response::Status Http::Response::getStatus() const +{ + return m_status; +} + + +//////////////////////////////////////////////////////////// +unsigned int Http::Response::getMajorHttpVersion() const +{ + return m_majorVersion; +} + + +//////////////////////////////////////////////////////////// +unsigned int Http::Response::getMinorHttpVersion() const +{ + return m_minorVersion; +} + + +//////////////////////////////////////////////////////////// +const std::string& Http::Response::getBody() const +{ + return m_body; +} + + +//////////////////////////////////////////////////////////// +void Http::Response::parse(const std::string& data) +{ + std::istringstream in(data); + + // Extract the HTTP version from the first line + std::string version; + if (in >> version) + { + if ((version.size() >= 8) && (version[6] == '.') && (toLower(version.substr(0, 5)) == "http/") && + std::isdigit(version[5]) && std::isdigit(version[7])) + { + m_majorVersion = static_cast(version[5] - '0'); + m_minorVersion = static_cast(version[7] - '0'); + } + else + { + // Invalid HTTP version + m_status = Status::InvalidResponse; + return; + } + } + + // Extract the status code from the first line + int status = 0; + if (in >> status) + { + m_status = static_cast(status); + } + else + { + // Invalid status code + m_status = Status::InvalidResponse; + return; + } + + // Ignore the end of the first line + in.ignore(std::numeric_limits::max(), '\n'); + + // Parse the other lines, which contain fields, one by one + parseFields(in); + + m_body.clear(); + + // Determine whether the transfer is chunked + if (toLower(getField("transfer-encoding")) != "chunked") + { + // Not chunked - just read everything at once + std::copy(std::istreambuf_iterator(in), std::istreambuf_iterator(), std::back_inserter(m_body)); + } + else + { + // Chunked - have to read chunk by chunk + std::size_t length = 0; + + // Read all chunks, identified by a chunk-size not being 0 + while (in >> std::hex >> length) + { + // Drop the rest of the line (chunk-extension) + in.ignore(std::numeric_limits::max(), '\n'); + + // Copy the actual content data + std::istreambuf_iterator it(in); + const std::istreambuf_iterator itEnd; + for (std::size_t i = 0; ((i < length) && (it != itEnd)); ++i) + { + m_body.push_back(*it); + ++it; // Iterate in separate expression to work around false positive -Wnull-dereference warning in GCC 12.1.0 + } + } + + // Drop the rest of the line (chunk-extension) + in.ignore(std::numeric_limits::max(), '\n'); + + // Read all trailers (if present) + parseFields(in); + } +} + + +//////////////////////////////////////////////////////////// +void Http::Response::parseFields(std::istream& in) +{ + std::string line; + while (std::getline(in, line) && (line.size() > 2)) + { + const std::string::size_type pos = line.find(": "); + if (pos != std::string::npos) + { + // Extract the field name and its value + const std::string field = line.substr(0, pos); + std::string value = line.substr(pos + 2); + + // Remove any trailing \r + if (!value.empty() && (*value.rbegin() == '\r')) + value.erase(value.size() - 1); + + // Add the field + m_fields[toLower(field)] = value; + } + } +} + + +//////////////////////////////////////////////////////////// +Http::Http(const std::string& host, unsigned short port) +{ + setHost(host, port); +} + + +//////////////////////////////////////////////////////////// +void Http::setHost(const std::string& host, unsigned short port) +{ + // Check the protocol + if (toLower(host.substr(0, 7)) == "http://") + { + // HTTP protocol + m_hostName = host.substr(7); + m_port = (port != 0 ? port : 80); + } + else if (toLower(host.substr(0, 8)) == "https://") + { + // HTTPS protocol -- unsupported (requires encryption and certificates and stuff...) + err() << "HTTPS protocol is not supported by sf::Http" << std::endl; + m_hostName.clear(); + m_port = 0; + } + else + { + // Undefined protocol - use HTTP + m_hostName = host; + m_port = (port != 0 ? port : 80); + } + + // Remove any trailing '/' from the host name + if (!m_hostName.empty() && (*m_hostName.rbegin() == '/')) + m_hostName.erase(m_hostName.size() - 1); + + m_host = IpAddress::resolve(m_hostName); +} + + +//////////////////////////////////////////////////////////// +Http::Response Http::sendRequest(const Http::Request& request, Time timeout) +{ + // First make sure that the request is valid -- add missing mandatory fields + Request toSend(request); + if (!toSend.hasField("From")) + { + toSend.setField("From", "user@sfml-dev.org"); + } + if (!toSend.hasField("User-Agent")) + { + toSend.setField("User-Agent", "libsfml-network/3.x"); + } + if (!toSend.hasField("Host")) + { + toSend.setField("Host", m_hostName); + } + if (!toSend.hasField("Content-Length")) + { + std::ostringstream out; + out << toSend.m_body.size(); + toSend.setField("Content-Length", out.str()); + } + if ((toSend.m_method == Request::Method::Post) && !toSend.hasField("Content-Type")) + { + toSend.setField("Content-Type", "application/x-www-form-urlencoded"); + } + if ((toSend.m_majorVersion * 10 + toSend.m_minorVersion >= 11) && !toSend.hasField("Connection")) + { + toSend.setField("Connection", "close"); + } + + // Prepare the response + Response received; + + // Connect the socket to the host + if (m_connection.connect(m_host.value(), m_port, timeout) == Socket::Status::Done) + { + // Convert the request to string and send it through the connected socket + const std::string requestStr = toSend.prepare(); + + if (!requestStr.empty()) + { + // Send it through the socket + if (m_connection.send(requestStr.c_str(), requestStr.size()) == Socket::Status::Done) + { + // Wait for the server's response + std::string receivedStr; + std::size_t size = 0; + std::array buffer{}; + while (m_connection.receive(buffer.data(), buffer.size(), size) == Socket::Status::Done) + { + receivedStr.append(buffer.data(), buffer.data() + size); + } + + // Build the Response object from the received data + received.parse(receivedStr); + } + } + + // Close the connection + m_connection.disconnect(); + } + + return received; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/IpAddress.cpp b/third_party/sfml/src/SFML/Network/IpAddress.cpp new file mode 100644 index 00000000..ed4664b2 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/IpAddress.cpp @@ -0,0 +1,253 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + +#include + +#include +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +const IpAddress IpAddress::Any(0, 0, 0, 0); +const IpAddress IpAddress::LocalHost(127, 0, 0, 1); +const IpAddress IpAddress::Broadcast(255, 255, 255, 255); + + +//////////////////////////////////////////////////////////// +std::optional IpAddress::resolve(std::string_view address) +{ + using namespace std::string_view_literals; + + if (address.empty()) + { + // Not generating en error message here as resolution failure is a valid outcome. + return std::nullopt; + } + + if (address == "255.255.255.255"sv) + { + // The broadcast address needs to be handled explicitly, + // because it is also the value returned by inet_addr on error + return Broadcast; + } + + if (address == "0.0.0.0"sv) + return Any; + + // Try to convert the address as a byte representation ("xxx.xxx.xxx.xxx") + if (const std::uint32_t ip = inet_addr(address.data()); ip != INADDR_NONE) + return IpAddress(ntohl(ip)); + + // Not a valid address, try to convert it as a host name + addrinfo hints{}; // Zero-initialize + hints.ai_family = AF_INET; + + addrinfo* result = nullptr; + if (getaddrinfo(address.data(), nullptr, &hints, &result) == 0 && result != nullptr) + { + sockaddr_in sin{}; + std::memcpy(&sin, result->ai_addr, sizeof(*result->ai_addr)); + + const std::uint32_t ip = sin.sin_addr.s_addr; + freeaddrinfo(result); + + return IpAddress(ntohl(ip)); + } + + // Not generating en error message here as resolution failure is a valid outcome. + return std::nullopt; +} + + +//////////////////////////////////////////////////////////// +IpAddress::IpAddress(std::uint8_t byte0, std::uint8_t byte1, std::uint8_t byte2, std::uint8_t byte3) : +m_address(static_cast((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3)) +{ +} + + +//////////////////////////////////////////////////////////// +IpAddress::IpAddress(std::uint32_t address) : m_address(address) +{ +} + + +//////////////////////////////////////////////////////////// +std::string IpAddress::toString() const +{ + in_addr address{}; + address.s_addr = htonl(m_address); + + return inet_ntoa(address); +} + + +//////////////////////////////////////////////////////////// +std::uint32_t IpAddress::toInteger() const +{ + return m_address; +} + + +//////////////////////////////////////////////////////////// +std::optional IpAddress::getLocalAddress() +{ + // The method here is to connect a UDP socket to a public ip, + // and get the local socket address with the getsockname function. + // UDP connection will not send anything to the network, so this function won't cause any overhead. + + // Create the socket + const SocketHandle sock = socket(PF_INET, SOCK_DGRAM, 0); + if (sock == priv::SocketImpl::invalidSocket()) + { + err() << "Failed to retrieve local address (invalid socket)" << std::endl; + return std::nullopt; + } + + // Connect the socket to a public ip (here 1.1.1.1) on any + // port. This will give the local address of the network interface + // used for default routing which is usually what we want. + sockaddr_in address = priv::SocketImpl::createAddress(0x01010101, 9); + if (connect(sock, reinterpret_cast(&address), sizeof(address)) == -1) + { + priv::SocketImpl::close(sock); + + err() << "Failed to retrieve local address (socket connection failure)" << std::endl; + return std::nullopt; + } + + // Get the local address of the socket connection + priv::SocketImpl::AddrLength size = sizeof(address); + if (getsockname(sock, reinterpret_cast(&address), &size) == -1) + { + priv::SocketImpl::close(sock); + + err() << "Failed to retrieve local address (socket local address retrieval failure)" << std::endl; + return std::nullopt; + } + + // Close the socket + priv::SocketImpl::close(sock); + + // Finally build the IP address + return IpAddress(ntohl(address.sin_addr.s_addr)); +} + + +//////////////////////////////////////////////////////////// +std::optional IpAddress::getPublicAddress(Time timeout) +{ + // The trick here is more complicated, because the only way + // to get our public IP address is to get it from a distant computer. + // Here we get the web page from http://www.sfml-dev.org/ip-provider.php + // and parse the result to extract our IP address + // (not very hard: the web page contains only our IP address). + + Http server("www.sfml-dev.org"); + const Http::Request request("/ip-provider.php", Http::Request::Method::Get); + const Http::Response page = server.sendRequest(request, timeout); + + const Http::Response::Status status = page.getStatus(); + + if (status == Http::Response::Status::Ok) + return IpAddress::resolve(page.getBody()); + + err() << "Failed to retrieve public address from external IP resolution server (HTTP response status " + << static_cast(status) << ")" << std::endl; + + return std::nullopt; +} + + +//////////////////////////////////////////////////////////// +bool operator==(IpAddress left, IpAddress right) +{ + return !(left < right) && !(right < left); +} + + +//////////////////////////////////////////////////////////// +bool operator!=(IpAddress left, IpAddress right) +{ + return !(left == right); +} + + +//////////////////////////////////////////////////////////// +bool operator<(IpAddress left, IpAddress right) +{ + return left.m_address < right.m_address; +} + + +//////////////////////////////////////////////////////////// +bool operator>(IpAddress left, IpAddress right) +{ + return right < left; +} + + +//////////////////////////////////////////////////////////// +bool operator<=(IpAddress left, IpAddress right) +{ + return !(right < left); +} + + +//////////////////////////////////////////////////////////// +bool operator>=(IpAddress left, IpAddress right) +{ + return !(left < right); +} + + +//////////////////////////////////////////////////////////// +std::istream& operator>>(std::istream& stream, std::optional& address) +{ + std::string str; + stream >> str; + address = IpAddress::resolve(str); + + return stream; +} + + +//////////////////////////////////////////////////////////// +std::ostream& operator<<(std::ostream& stream, IpAddress address) +{ + return stream << address.toString(); +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/Packet.cpp b/third_party/sfml/src/SFML/Network/Packet.cpp new file mode 100644 index 00000000..02703952 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/Packet.cpp @@ -0,0 +1,596 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +#include + +#include +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +void Packet::append(const void* data, std::size_t sizeInBytes) +{ + if (data && (sizeInBytes > 0)) + { + const auto* begin = reinterpret_cast(data); + const auto* end = begin + sizeInBytes; + m_data.insert(m_data.end(), begin, end); + } +} + + +//////////////////////////////////////////////////////////// +std::size_t Packet::getReadPosition() const +{ + return m_readPos; +} + + +//////////////////////////////////////////////////////////// +void Packet::clear() +{ + m_data.clear(); + m_readPos = 0; + m_isValid = true; +} + + +//////////////////////////////////////////////////////////// +const void* Packet::getData() const +{ + return !m_data.empty() ? m_data.data() : nullptr; +} + + +//////////////////////////////////////////////////////////// +std::size_t Packet::getDataSize() const +{ + return m_data.size(); +} + + +//////////////////////////////////////////////////////////// +bool Packet::endOfPacket() const +{ + return m_readPos >= m_data.size(); +} + + +//////////////////////////////////////////////////////////// +Packet::operator bool() const +{ + return m_isValid; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(bool& data) +{ + std::uint8_t value = 0; + if (*this >> value) + data = (value != 0); + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::int8_t& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::uint8_t& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::int16_t& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + data = static_cast(ntohs(static_cast(data))); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::uint16_t& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + data = ntohs(data); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::int32_t& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + data = static_cast(ntohl(static_cast(data))); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::uint32_t& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + data = ntohl(data); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::int64_t& data) +{ + if (checkSize(sizeof(data))) + { + // Since ntohll is not available everywhere, we have to convert + // to network byte order (big endian) manually + std::array bytes{}; + std::memcpy(bytes.data(), &m_data[m_readPos], bytes.size()); + + data = toInteger(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]); + + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::uint64_t& data) +{ + if (checkSize(sizeof(data))) + { + // Since ntohll is not available everywhere, we have to convert + // to network byte order (big endian) manually + std::array bytes{}; + std::memcpy(bytes.data(), &m_data[m_readPos], sizeof(data)); + + data = toInteger(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]); + + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(float& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(double& data) +{ + if (checkSize(sizeof(data))) + { + std::memcpy(&data, &m_data[m_readPos], sizeof(data)); + m_readPos += sizeof(data); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(char* data) +{ + assert(data && "Packet::operator>> Data must not be null"); + + // First extract string length + std::uint32_t length = 0; + *this >> length; + + if ((length > 0) && checkSize(length)) + { + // Then extract characters + std::memcpy(data, &m_data[m_readPos], length); + data[length] = '\0'; + + // Update reading position + m_readPos += length; + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::string& data) +{ + // First extract string length + std::uint32_t length = 0; + *this >> length; + + data.clear(); + if ((length > 0) && checkSize(length)) + { + // Then extract characters + data.assign(reinterpret_cast(&m_data[m_readPos]), length); + + // Update reading position + m_readPos += length; + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(wchar_t* data) +{ + assert(data && "Packet::operator>> Data must not be null"); + + // First extract string length + std::uint32_t length = 0; + *this >> length; + + if ((length > 0) && checkSize(length * sizeof(std::uint32_t))) + { + // Then extract characters + for (std::uint32_t i = 0; i < length; ++i) + { + std::uint32_t character = 0; + *this >> character; + data[i] = static_cast(character); + } + data[length] = L'\0'; + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(std::wstring& data) +{ + // First extract string length + std::uint32_t length = 0; + *this >> length; + + data.clear(); + if ((length > 0) && checkSize(length * sizeof(std::uint32_t))) + { + // Then extract characters + for (std::uint32_t i = 0; i < length; ++i) + { + std::uint32_t character = 0; + *this >> character; + data += static_cast(character); + } + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator>>(String& data) +{ + // First extract the string length + std::uint32_t length = 0; + *this >> length; + + data.clear(); + if ((length > 0) && checkSize(length * sizeof(std::uint32_t))) + { + // Then extract characters + for (std::uint32_t i = 0; i < length; ++i) + { + std::uint32_t character = 0; + *this >> character; + data += static_cast(character); + } + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(bool data) +{ + *this << static_cast(data); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::int8_t data) +{ + append(&data, sizeof(data)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::uint8_t data) +{ + append(&data, sizeof(data)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::int16_t data) +{ + const auto toWrite = static_cast(htons(static_cast(data))); + append(&toWrite, sizeof(toWrite)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::uint16_t data) +{ + const std::uint16_t toWrite = htons(data); + append(&toWrite, sizeof(toWrite)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::int32_t data) +{ + const auto toWrite = static_cast(htonl(static_cast(data))); + append(&toWrite, sizeof(toWrite)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::uint32_t data) +{ + const std::uint32_t toWrite = htonl(data); + append(&toWrite, sizeof(toWrite)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::int64_t data) +{ + // Since htonll is not available everywhere, we have to convert + // to network byte order (big endian) manually + + const std::array toWrite = {static_cast((data >> 56) & 0xFF), + static_cast((data >> 48) & 0xFF), + static_cast((data >> 40) & 0xFF), + static_cast((data >> 32) & 0xFF), + static_cast((data >> 24) & 0xFF), + static_cast((data >> 16) & 0xFF), + static_cast((data >> 8) & 0xFF), + static_cast((data) & 0xFF)}; + + append(toWrite.data(), toWrite.size()); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(std::uint64_t data) +{ + // Since htonll is not available everywhere, we have to convert + // to network byte order (big endian) manually + + const std::array toWrite = {static_cast((data >> 56) & 0xFF), + static_cast((data >> 48) & 0xFF), + static_cast((data >> 40) & 0xFF), + static_cast((data >> 32) & 0xFF), + static_cast((data >> 24) & 0xFF), + static_cast((data >> 16) & 0xFF), + static_cast((data >> 8) & 0xFF), + static_cast((data) & 0xFF)}; + + append(toWrite.data(), toWrite.size()); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(float data) +{ + append(&data, sizeof(data)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(double data) +{ + append(&data, sizeof(data)); + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(const char* data) +{ + assert(data && "Packet::operator<< Data must not be null"); + + // First insert string length + const auto length = static_cast(std::strlen(data)); + *this << length; + + // Then insert characters + append(data, length * sizeof(char)); + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(const std::string& data) +{ + // First insert string length + const auto length = static_cast(data.size()); + *this << length; + + // Then insert characters + if (length > 0) + append(data.c_str(), length * sizeof(std::string::value_type)); + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(const wchar_t* data) +{ + assert(data && "Packet::operator<< Data must not be null"); + + // First insert string length + const auto length = static_cast(std::wcslen(data)); + *this << length; + + // Then insert characters + for (const wchar_t* c = data; *c != L'\0'; ++c) + *this << static_cast(*c); + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(const std::wstring& data) +{ + // First insert string length + const auto length = static_cast(data.size()); + *this << length; + + // Then insert characters + if (length > 0) + { + for (const wchar_t c : data) + *this << static_cast(c); + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +Packet& Packet::operator<<(const String& data) +{ + // First insert the string length + const auto length = static_cast(data.getSize()); + *this << length; + + // Then insert characters + if (length > 0) + { + for (const unsigned int datum : data) + *this << datum; + } + + return *this; +} + + +//////////////////////////////////////////////////////////// +bool Packet::checkSize(std::size_t size) +{ + // Determine if size is big enough to trigger an overflow + const bool overflowDetected = m_readPos + size < m_readPos; + m_isValid = m_isValid && (m_readPos + size <= m_data.size()) && !overflowDetected; + + return m_isValid; +} + + +//////////////////////////////////////////////////////////// +const void* Packet::onSend(std::size_t& size) +{ + size = getDataSize(); + return getData(); +} + + +//////////////////////////////////////////////////////////// +void Packet::onReceive(const void* data, std::size_t size) +{ + append(data, size); +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/Socket.cpp b/third_party/sfml/src/SFML/Network/Socket.cpp new file mode 100644 index 00000000..c9daa77d --- /dev/null +++ b/third_party/sfml/src/SFML/Network/Socket.cpp @@ -0,0 +1,175 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include + +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +Socket::Socket(Type type) : m_type(type), m_socket(priv::SocketImpl::invalidSocket()) +{ +} + + +//////////////////////////////////////////////////////////// +Socket::~Socket() +{ + // Close the socket before it gets destructed + close(); +} + + +//////////////////////////////////////////////////////////// +Socket::Socket(Socket&& socket) noexcept : +m_type(socket.m_type), +m_socket(std::exchange(socket.m_socket, priv::SocketImpl::invalidSocket())), +m_isBlocking(socket.m_isBlocking) +{ +} + + +//////////////////////////////////////////////////////////// +Socket& Socket::operator=(Socket&& socket) noexcept +{ + if (&socket == this) + return *this; + + close(); + + m_type = socket.m_type; + m_socket = std::exchange(socket.m_socket, priv::SocketImpl::invalidSocket()); + m_isBlocking = socket.m_isBlocking; + return *this; +} + + +//////////////////////////////////////////////////////////// +void Socket::setBlocking(bool blocking) +{ + // Apply if the socket is already created + if (m_socket != priv::SocketImpl::invalidSocket()) + priv::SocketImpl::setBlocking(m_socket, blocking); + + m_isBlocking = blocking; +} + + +//////////////////////////////////////////////////////////// +bool Socket::isBlocking() const +{ + return m_isBlocking; +} + + +//////////////////////////////////////////////////////////// +SocketHandle Socket::getNativeHandle() const +{ + return m_socket; +} + + +//////////////////////////////////////////////////////////// +void Socket::create() +{ + // Don't create the socket if it already exists + if (m_socket == priv::SocketImpl::invalidSocket()) + { + const SocketHandle handle = socket(PF_INET, m_type == Type::Tcp ? SOCK_STREAM : SOCK_DGRAM, 0); + + if (handle == priv::SocketImpl::invalidSocket()) + { + err() << "Failed to create socket" << std::endl; + return; + } + + create(handle); + } +} + + +//////////////////////////////////////////////////////////// +void Socket::create(SocketHandle handle) +{ + // Don't create the socket if it already exists + if (m_socket == priv::SocketImpl::invalidSocket()) + { + // Assign the new handle + m_socket = handle; + + // Set the current blocking state + setBlocking(m_isBlocking); + + if (m_type == Type::Tcp) + { + // Disable the Nagle algorithm (i.e. removes buffering of TCP packets) + int yes = 1; + if (setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), sizeof(yes)) == -1) + { + err() << "Failed to set socket option \"TCP_NODELAY\" ; " + << "all your TCP packets will be buffered" << std::endl; + } + +// On macOS, disable the SIGPIPE signal on disconnection +#ifdef SFML_SYSTEM_MACOS + if (setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&yes), sizeof(yes)) == -1) + { + err() << "Failed to set socket option \"SO_NOSIGPIPE\"" << std::endl; + } +#endif + } + else + { + // Enable broadcast by default for UDP sockets + int yes = 1; + if (setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&yes), sizeof(yes)) == -1) + { + err() << "Failed to enable broadcast on UDP socket" << std::endl; + } + } + } +} + + +//////////////////////////////////////////////////////////// +void Socket::close() +{ + // Close the socket + if (m_socket != priv::SocketImpl::invalidSocket()) + { + priv::SocketImpl::close(m_socket); + m_socket = priv::SocketImpl::invalidSocket(); + } +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/SocketImpl.hpp b/third_party/sfml/src/SFML/Network/SocketImpl.hpp new file mode 100644 index 00000000..1ad06b6f --- /dev/null +++ b/third_party/sfml/src/SFML/Network/SocketImpl.hpp @@ -0,0 +1,123 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#if defined(SFML_SYSTEM_WINDOWS) + +#include + +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include + +#include + +#endif + +#include + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +/// \brief Helper class implementing all the non-portable +/// socket stuff +/// +//////////////////////////////////////////////////////////// +class SocketImpl +{ +public: + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// +#if defined(SFML_SYSTEM_WINDOWS) + using AddrLength = int; + using Size = int; +#else + using AddrLength = socklen_t; + using Size = std::size_t; +#endif + + //////////////////////////////////////////////////////////// + /// \brief Create an internal sockaddr_in address + /// + /// \param address Target address + /// \param port Target port + /// + /// \return sockaddr_in ready to be used by socket functions + /// + //////////////////////////////////////////////////////////// + static sockaddr_in createAddress(std::uint32_t address, unsigned short port); + + //////////////////////////////////////////////////////////// + /// \brief Return the value of the invalid socket + /// + /// \return Special value of the invalid socket + /// + //////////////////////////////////////////////////////////// + static SocketHandle invalidSocket(); + + //////////////////////////////////////////////////////////// + /// \brief Close and destroy a socket + /// + /// \param sock Handle of the socket to close + /// + //////////////////////////////////////////////////////////// + static void close(SocketHandle sock); + + //////////////////////////////////////////////////////////// + /// \brief Set a socket as blocking or non-blocking + /// + /// \param sock Handle of the socket + /// \param block New blocking state of the socket + /// + //////////////////////////////////////////////////////////// + static void setBlocking(SocketHandle sock, bool block); + + //////////////////////////////////////////////////////////// + /// Get the last socket error status + /// + /// \return Status corresponding to the last socket error + /// + //////////////////////////////////////////////////////////// + static Socket::Status getErrorStatus(); +}; + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/Network/SocketSelector.cpp b/third_party/sfml/src/SFML/Network/SocketSelector.cpp new file mode 100644 index 00000000..3394ee75 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/SocketSelector.cpp @@ -0,0 +1,209 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro +#endif + + +namespace sf +{ +//////////////////////////////////////////////////////////// +struct SocketSelector::SocketSelectorImpl +{ + fd_set allSockets{}; //!< Set containing all the sockets handles + fd_set socketsReady{}; //!< Set containing handles of the sockets that are ready + int maxSocket{}; //!< Maximum socket handle + int socketCount{}; //!< Number of socket handles +}; + + +//////////////////////////////////////////////////////////// +SocketSelector::SocketSelector() : m_impl(std::make_unique()) +{ + clear(); +} + + +//////////////////////////////////////////////////////////// +SocketSelector::~SocketSelector() = default; + + +//////////////////////////////////////////////////////////// +SocketSelector::SocketSelector(const SocketSelector& copy) : m_impl(std::make_unique(*copy.m_impl)) +{ +} + + +//////////////////////////////////////////////////////////// +SocketSelector& SocketSelector::operator=(const SocketSelector& right) +{ + SocketSelector temp(right); + std::swap(m_impl, temp.m_impl); + return *this; +} + + +//////////////////////////////////////////////////////////// +SocketSelector::SocketSelector(SocketSelector&&) noexcept = default; + + +////////////////////////////////////////////////////////////] +SocketSelector& SocketSelector::operator=(SocketSelector&&) noexcept = default; + + +//////////////////////////////////////////////////////////// +void SocketSelector::add(Socket& socket) +{ + const SocketHandle handle = socket.getNativeHandle(); + if (handle != priv::SocketImpl::invalidSocket()) + { + +#if defined(SFML_SYSTEM_WINDOWS) + + if (m_impl->socketCount >= FD_SETSIZE) + { + err() << "The socket can't be added to the selector because the " + << "selector is full. This is a limitation of your operating " + << "system's FD_SETSIZE setting."; + return; + } + + if (FD_ISSET(handle, &m_impl->allSockets)) + return; + + ++m_impl->socketCount; + +#else + + if (handle >= FD_SETSIZE) + { + err() << "The socket can't be added to the selector because its " + << "ID is too high. This is a limitation of your operating " + << "system's FD_SETSIZE setting."; + return; + } + + // SocketHandle is an int in POSIX + m_impl->maxSocket = std::max(m_impl->maxSocket, handle); + +#endif + + FD_SET(handle, &m_impl->allSockets); + } +} + + +//////////////////////////////////////////////////////////// +void SocketSelector::remove(Socket& socket) +{ + const SocketHandle handle = socket.getNativeHandle(); + if (handle != priv::SocketImpl::invalidSocket()) + { + +#if defined(SFML_SYSTEM_WINDOWS) + + if (!FD_ISSET(handle, &m_impl->allSockets)) + return; + + --m_impl->socketCount; + +#else + + if (handle >= FD_SETSIZE) + return; + +#endif + + FD_CLR(handle, &m_impl->allSockets); + FD_CLR(handle, &m_impl->socketsReady); + } +} + + +//////////////////////////////////////////////////////////// +void SocketSelector::clear() +{ + FD_ZERO(&m_impl->allSockets); + FD_ZERO(&m_impl->socketsReady); + + m_impl->maxSocket = 0; + m_impl->socketCount = 0; +} + + +//////////////////////////////////////////////////////////// +bool SocketSelector::wait(Time timeout) +{ + // Setup the timeout + timeval time{}; + time.tv_sec = static_cast(timeout.asMicroseconds() / 1'000'000); + time.tv_usec = static_cast(timeout.asMicroseconds() % 1'000'000); + + // Initialize the set that will contain the sockets that are ready + m_impl->socketsReady = m_impl->allSockets; + + // Wait until one of the sockets is ready for reading, or timeout is reached + // The first parameter is ignored on Windows + const int count = select(m_impl->maxSocket + 1, &m_impl->socketsReady, nullptr, nullptr, timeout != Time::Zero ? &time : nullptr); + + return count > 0; +} + + +//////////////////////////////////////////////////////////// +bool SocketSelector::isReady(Socket& socket) const +{ + const SocketHandle handle = socket.getNativeHandle(); + if (handle != priv::SocketImpl::invalidSocket()) + { + +#if !defined(SFML_SYSTEM_WINDOWS) + + if (handle >= FD_SETSIZE) + return false; + +#endif + + return FD_ISSET(handle, &m_impl->socketsReady) != 0; + } + + return false; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/TcpListener.cpp b/third_party/sfml/src/SFML/Network/TcpListener.cpp new file mode 100644 index 00000000..18ab76fe --- /dev/null +++ b/third_party/sfml/src/SFML/Network/TcpListener.cpp @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +TcpListener::TcpListener() : Socket(Type::Tcp) +{ +} + + +//////////////////////////////////////////////////////////// +unsigned short TcpListener::getLocalPort() const +{ + if (getNativeHandle() != priv::SocketImpl::invalidSocket()) + { + // Retrieve information about the local end of the socket + sockaddr_in address{}; + priv::SocketImpl::AddrLength size = sizeof(address); + if (getsockname(getNativeHandle(), reinterpret_cast(&address), &size) != -1) + { + return ntohs(address.sin_port); + } + } + + // We failed to retrieve the port + return 0; +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpListener::listen(unsigned short port, IpAddress address) +{ + // Close the socket if it is already bound + close(); + + // Create the internal socket if it doesn't exist + create(); + + // Check if the address is valid + if (address == IpAddress::Broadcast) + return Status::Error; + + // Bind the socket to the specified port + sockaddr_in addr = priv::SocketImpl::createAddress(address.toInteger(), port); + if (bind(getNativeHandle(), reinterpret_cast(&addr), sizeof(addr)) == -1) + { + // Not likely to happen, but... + err() << "Failed to bind listener socket to port " << port << std::endl; + return Status::Error; + } + + // Listen to the bound port + if (::listen(getNativeHandle(), SOMAXCONN) == -1) + { + // Oops, socket is deaf + err() << "Failed to listen to port " << port << std::endl; + return Status::Error; + } + + return Status::Done; +} + + +//////////////////////////////////////////////////////////// +void TcpListener::close() +{ + // Simply close the socket + Socket::close(); +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpListener::accept(TcpSocket& socket) +{ + // Make sure that we're listening + if (getNativeHandle() == priv::SocketImpl::invalidSocket()) + { + err() << "Failed to accept a new connection, the socket is not listening" << std::endl; + return Status::Error; + } + + // Accept a new connection + sockaddr_in address{}; + priv::SocketImpl::AddrLength length = sizeof(address); + const SocketHandle remote = ::accept(getNativeHandle(), reinterpret_cast(&address), &length); + + // Check for errors + if (remote == priv::SocketImpl::invalidSocket()) + return priv::SocketImpl::getErrorStatus(); + + // Initialize the new connected socket + socket.close(); + socket.create(remote); + + return Status::Done; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/TcpSocket.cpp b/third_party/sfml/src/SFML/Network/TcpSocket.cpp new file mode 100644 index 00000000..bf6c4932 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/TcpSocket.cpp @@ -0,0 +1,429 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning(disable : 4127) // "conditional expression is constant" generated by the FD_SET macro +#endif + + +namespace +{ +// Low-level send/receive flags (OS-dependent) +#ifdef SFML_SYSTEM_LINUX +constexpr int flags = MSG_NOSIGNAL; +#else +constexpr int flags = 0; +#endif +} // namespace + +namespace sf +{ +//////////////////////////////////////////////////////////// +TcpSocket::TcpSocket() : Socket(Type::Tcp) +{ +} + + +//////////////////////////////////////////////////////////// +unsigned short TcpSocket::getLocalPort() const +{ + if (getNativeHandle() != priv::SocketImpl::invalidSocket()) + { + // Retrieve information about the local end of the socket + sockaddr_in address{}; + priv::SocketImpl::AddrLength size = sizeof(address); + if (getsockname(getNativeHandle(), reinterpret_cast(&address), &size) != -1) + { + return ntohs(address.sin_port); + } + } + + // We failed to retrieve the port + return 0; +} + + +//////////////////////////////////////////////////////////// +std::optional TcpSocket::getRemoteAddress() const +{ + if (getNativeHandle() != priv::SocketImpl::invalidSocket()) + { + // Retrieve information about the remote end of the socket + sockaddr_in address{}; + priv::SocketImpl::AddrLength size = sizeof(address); + if (getpeername(getNativeHandle(), reinterpret_cast(&address), &size) != -1) + { + return IpAddress(ntohl(address.sin_addr.s_addr)); + } + } + + // We failed to retrieve the address + return std::nullopt; +} + + +//////////////////////////////////////////////////////////// +unsigned short TcpSocket::getRemotePort() const +{ + if (getNativeHandle() != priv::SocketImpl::invalidSocket()) + { + // Retrieve information about the remote end of the socket + sockaddr_in address{}; + priv::SocketImpl::AddrLength size = sizeof(address); + if (getpeername(getNativeHandle(), reinterpret_cast(&address), &size) != -1) + { + return ntohs(address.sin_port); + } + } + + // We failed to retrieve the port + return 0; +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpSocket::connect(IpAddress remoteAddress, unsigned short remotePort, Time timeout) +{ + // Disconnect the socket if it is already connected + disconnect(); + + // Create the internal socket if it doesn't exist + create(); + + // Create the remote address + sockaddr_in address = priv::SocketImpl::createAddress(remoteAddress.toInteger(), remotePort); + + if (timeout <= Time::Zero) + { + // ----- We're not using a timeout: just try to connect ----- + + // Connect the socket + if (::connect(getNativeHandle(), reinterpret_cast(&address), sizeof(address)) == -1) + return priv::SocketImpl::getErrorStatus(); + + // Connection succeeded + return Status::Done; + } + + // ----- We're using a timeout: we'll need a few tricks to make it work ----- + + // Save the previous blocking state + const bool blocking = isBlocking(); + + // Switch to non-blocking to enable our connection timeout + if (blocking) + setBlocking(false); + + // Try to connect to the remote address + if (::connect(getNativeHandle(), reinterpret_cast(&address), sizeof(address)) >= 0) + { + // We got instantly connected! (it may no happen a lot...) + setBlocking(blocking); + return Status::Done; + } + + // Get the error status + Status status = priv::SocketImpl::getErrorStatus(); + + // If we were in non-blocking mode, return immediately + if (!blocking) + return status; + + // Otherwise, wait until something happens to our socket (success, timeout or error) + if (status == Socket::Status::NotReady) + { + // Setup the selector + fd_set selector; + FD_ZERO(&selector); + FD_SET(getNativeHandle(), &selector); + + // Setup the timeout + timeval time{}; + time.tv_sec = static_cast(timeout.asMicroseconds() / 1'000'000); + time.tv_usec = static_cast(timeout.asMicroseconds() % 1'000'000); + + // Wait for something to write on our socket (which means that the connection request has returned) + if (select(static_cast(getNativeHandle() + 1), nullptr, &selector, nullptr, &time) > 0) + { + // At this point the connection may have been either accepted or refused. + // To know whether it's a success or a failure, we must check the address of the connected peer + if (getRemoteAddress().has_value()) + { + // Connection accepted + status = Status::Done; + } + else + { + // Connection refused + status = priv::SocketImpl::getErrorStatus(); + } + } + else + { + // Failed to connect before timeout is over + status = priv::SocketImpl::getErrorStatus(); + } + } + + // Switch back to blocking mode + setBlocking(true); + + return status; +} + + +//////////////////////////////////////////////////////////// +void TcpSocket::disconnect() +{ + // Close the socket + close(); + + // Reset the pending packet data + m_pendingPacket = PendingPacket(); +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpSocket::send(const void* data, std::size_t size) +{ + if (!isBlocking()) + err() << "Warning: Partial sends might not be handled properly." << std::endl; + + std::size_t sent = 0; + + return send(data, size, sent); +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpSocket::send(const void* data, std::size_t size, std::size_t& sent) +{ + // Check the parameters + if (!data || (size == 0)) + { + err() << "Cannot send data over the network (no data to send)" << std::endl; + return Status::Error; + } + + // Loop until every byte has been sent + int result = 0; + for (sent = 0; sent < size; sent += static_cast(result)) + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuseless-cast" + // Send a chunk of data + result = static_cast(::send(getNativeHandle(), + static_cast(data) + sent, + static_cast(size - sent), + flags)); +#pragma GCC diagnostic pop + + // Check for errors + if (result < 0) + { + const Status status = priv::SocketImpl::getErrorStatus(); + + if ((status == Status::NotReady) && sent) + return Status::Partial; + + return status; + } + } + + return Status::Done; +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpSocket::receive(void* data, std::size_t size, std::size_t& received) +{ + // First clear the variables to fill + received = 0; + + // Check the destination buffer + if (!data) + { + err() << "Cannot receive data from the network (the destination buffer is invalid)" << std::endl; + return Status::Error; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuseless-cast" + // Receive a chunk of bytes + const int sizeReceived = static_cast( + recv(getNativeHandle(), static_cast(data), static_cast(size), flags)); +#pragma GCC diagnostic pop + + // Check the number of bytes received + if (sizeReceived > 0) + { + received = static_cast(sizeReceived); + return Status::Done; + } + if (sizeReceived == 0) + { + return Socket::Status::Disconnected; + } + + return priv::SocketImpl::getErrorStatus(); +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpSocket::send(Packet& packet) +{ + // TCP is a stream protocol, it doesn't preserve messages boundaries. + // This means that we have to send the packet size first, so that the + // receiver knows the actual end of the packet in the data stream. + + // We allocate an extra memory block so that the size can be sent + // together with the data in a single call. This may seem inefficient, + // but it is actually required to avoid partial send, which could cause + // data corruption on the receiving end. + + // Get the data to send from the packet + std::size_t size = 0; + const void* data = packet.onSend(size); + + // First convert the packet size to network byte order + std::uint32_t packetSize = htonl(static_cast(size)); + + // Allocate memory for the data block to send + m_blockToSendBuffer.resize(sizeof(packetSize) + size); + +// Copy the packet size and data into the block to send +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnull-dereference" // False positive. + std::memcpy(m_blockToSendBuffer.data(), &packetSize, sizeof(packetSize)); +#pragma GCC diagnostic pop + if (size > 0) + std::memcpy(m_blockToSendBuffer.data() + sizeof(packetSize), data, size); + +// These warnings are ignored here for portability, as even on Windows the +// signature of `send` might change depending on whether Win32 or MinGW is +// being used. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuseless-cast" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" + // Send the data block + std::size_t sent = 0; + const Status status = send(m_blockToSendBuffer.data() + packet.m_sendPos, + static_cast(m_blockToSendBuffer.size() - packet.m_sendPos), + sent); +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop + + // In the case of a partial send, record the location to resume from + if (status == Status::Partial) + { + packet.m_sendPos += sent; + } + else if (status == Status::Done) + { + packet.m_sendPos = 0; + } + + return status; +} + + +//////////////////////////////////////////////////////////// +Socket::Status TcpSocket::receive(Packet& packet) +{ + // First clear the variables to fill + packet.clear(); + + // We start by getting the size of the incoming packet + std::uint32_t packetSize = 0; + std::size_t received = 0; + if (m_pendingPacket.sizeReceived < sizeof(m_pendingPacket.size)) + { + // Loop until we've received the entire size of the packet + // (even a 4 byte variable may be received in more than one call) + while (m_pendingPacket.sizeReceived < sizeof(m_pendingPacket.size)) + { + char* data = reinterpret_cast(&m_pendingPacket.size) + m_pendingPacket.sizeReceived; + const Status status = receive(data, sizeof(m_pendingPacket.size) - m_pendingPacket.sizeReceived, received); + m_pendingPacket.sizeReceived += received; + + if (status != Status::Done) + return status; + } + + // The packet size has been fully received + packetSize = ntohl(m_pendingPacket.size); + } + else + { + // The packet size has already been received in a previous call + packetSize = ntohl(m_pendingPacket.size); + } + + // Loop until we receive all the packet data + std::array buffer{}; + while (m_pendingPacket.data.size() < packetSize) + { + // Receive a chunk of data + const std::size_t sizeToGet = std::min(packetSize - m_pendingPacket.data.size(), buffer.size()); + const Status status = receive(buffer.data(), sizeToGet, received); + if (status != Status::Done) + return status; + + // Append it into the packet + if (received > 0) + { + m_pendingPacket.data.resize(m_pendingPacket.data.size() + received); + std::byte* begin = m_pendingPacket.data.data() + m_pendingPacket.data.size() - received; + std::memcpy(begin, buffer.data(), received); + } + } + + // We have received all the packet data: we can copy it to the user packet + if (!m_pendingPacket.data.empty()) + packet.onReceive(m_pendingPacket.data.data(), m_pendingPacket.data.size()); + + // Clear the pending packet data + m_pendingPacket = PendingPacket(); + + return Status::Done; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/UdpSocket.cpp b/third_party/sfml/src/SFML/Network/UdpSocket.cpp new file mode 100644 index 00000000..e57e55d0 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/UdpSocket.cpp @@ -0,0 +1,223 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include + +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +UdpSocket::UdpSocket() : Socket(Type::Udp) +{ +} + + +//////////////////////////////////////////////////////////// +unsigned short UdpSocket::getLocalPort() const +{ + if (getNativeHandle() != priv::SocketImpl::invalidSocket()) + { + // Retrieve information about the local end of the socket + sockaddr_in address{}; + priv::SocketImpl::AddrLength size = sizeof(address); + if (getsockname(getNativeHandle(), reinterpret_cast(&address), &size) != -1) + { + return ntohs(address.sin_port); + } + } + + // We failed to retrieve the port + return 0; +} + + +//////////////////////////////////////////////////////////// +Socket::Status UdpSocket::bind(unsigned short port, IpAddress address) +{ + // Close the socket if it is already bound + close(); + + // Create the internal socket if it doesn't exist + create(); + + // Check if the address is valid + if (address == IpAddress::Broadcast) + return Status::Error; + + // Bind the socket + sockaddr_in addr = priv::SocketImpl::createAddress(address.toInteger(), port); + if (::bind(getNativeHandle(), reinterpret_cast(&addr), sizeof(addr)) == -1) + { + err() << "Failed to bind socket to port " << port << std::endl; + return Status::Error; + } + + return Status::Done; +} + + +//////////////////////////////////////////////////////////// +void UdpSocket::unbind() +{ + // Simply close the socket + close(); +} + + +//////////////////////////////////////////////////////////// +Socket::Status UdpSocket::send(const void* data, std::size_t size, IpAddress remoteAddress, unsigned short remotePort) +{ + // Create the internal socket if it doesn't exist + create(); + + // Make sure that all the data will fit in one datagram + if (size > MaxDatagramSize) + { + err() << "Cannot send data over the network " + << "(the number of bytes to send is greater than sf::UdpSocket::MaxDatagramSize)" << std::endl; + return Status::Error; + } + + // Build the target address + sockaddr_in address = priv::SocketImpl::createAddress(remoteAddress.toInteger(), remotePort); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuseless-cast" + // Send the data (unlike TCP, all the data is always sent in one call) + const int sent = static_cast( + sendto(getNativeHandle(), + static_cast(data), + static_cast(size), + 0, + reinterpret_cast(&address), + sizeof(address))); +#pragma GCC diagnostic pop + + // Check for errors + if (sent < 0) + return priv::SocketImpl::getErrorStatus(); + + return Status::Done; +} + + +//////////////////////////////////////////////////////////// +Socket::Status UdpSocket::receive(void* data, + std::size_t size, + std::size_t& received, + std::optional& remoteAddress, + unsigned short& remotePort) +{ + // First clear the variables to fill + received = 0; + remoteAddress = std::nullopt; + remotePort = 0; + + // Check the destination buffer + if (!data) + { + err() << "Cannot receive data from the network (the destination buffer is invalid)" << std::endl; + return Status::Error; + } + + // Data that will be filled with the other computer's address + sockaddr_in address = priv::SocketImpl::createAddress(INADDR_ANY, 0); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuseless-cast" + // Receive a chunk of bytes + priv::SocketImpl::AddrLength addressSize = sizeof(address); + const int sizeReceived = static_cast( + recvfrom(getNativeHandle(), + static_cast(data), + static_cast(size), + 0, + reinterpret_cast(&address), + &addressSize)); +#pragma GCC diagnostic pop + + // Check for errors + if (sizeReceived < 0) + return priv::SocketImpl::getErrorStatus(); + + // Fill the sender information + received = static_cast(sizeReceived); + remoteAddress = IpAddress(ntohl(address.sin_addr.s_addr)); + remotePort = ntohs(address.sin_port); + + return Status::Done; +} + + +//////////////////////////////////////////////////////////// +Socket::Status UdpSocket::send(Packet& packet, IpAddress remoteAddress, unsigned short remotePort) +{ + // UDP is a datagram-oriented protocol (as opposed to TCP which is a stream protocol). + // Sending one datagram is almost safe: it may be lost but if it's received, then its data + // is guaranteed to be ok. However, splitting a packet into multiple datagrams would be highly + // unreliable, since datagrams may be reordered, dropped or mixed between different sources. + // That's why SFML imposes a limit on packet size so that they can be sent in a single datagram. + // This also removes the overhead associated to packets -- there's no size to send in addition + // to the packet's data. + + // Get the data to send from the packet + std::size_t size = 0; + const void* data = packet.onSend(size); + + // Send it + return send(data, size, remoteAddress, remotePort); +} + + +//////////////////////////////////////////////////////////// +Socket::Status UdpSocket::receive(Packet& packet, std::optional& remoteAddress, unsigned short& remotePort) +{ + // See the detailed comment in send(Packet) above. + + // Receive the datagram + std::size_t received = 0; + const Status status = receive(m_buffer.data(), m_buffer.size(), received, remoteAddress, remotePort); + + // If we received valid data, we can copy it to the user packet + packet.clear(); + if ((status == Status::Done) && (received > 0)) + packet.onReceive(m_buffer.data(), received); + + return status; +} + + +} // namespace sf diff --git a/third_party/sfml/src/SFML/Network/Unix/SocketImpl.cpp b/third_party/sfml/src/SFML/Network/Unix/SocketImpl.cpp new file mode 100644 index 00000000..91e2b859 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/Unix/SocketImpl.cpp @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include +#include + +#include + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +sockaddr_in SocketImpl::createAddress(std::uint32_t address, unsigned short port) +{ + auto addr = sockaddr_in(); + addr.sin_addr.s_addr = htonl(address); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + +#if defined(SFML_SYSTEM_MACOS) + addr.sin_len = sizeof(addr); +#endif + + return addr; +} + + +//////////////////////////////////////////////////////////// +SocketHandle SocketImpl::invalidSocket() +{ + return -1; +} + + +//////////////////////////////////////////////////////////// +void SocketImpl::close(SocketHandle sock) +{ + ::close(sock); +} + + +//////////////////////////////////////////////////////////// +void SocketImpl::setBlocking(SocketHandle sock, bool block) +{ + const int status = fcntl(sock, F_GETFL); + if (block) + { + if (fcntl(sock, F_SETFL, status & ~O_NONBLOCK) == -1) + err() << "Failed to set file status flags: " << errno << std::endl; + } + else + { + if (fcntl(sock, F_SETFL, status | O_NONBLOCK) == -1) + err() << "Failed to set file status flags: " << errno << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +Socket::Status SocketImpl::getErrorStatus() +{ + // The following are sometimes equal to EWOULDBLOCK, + // so we have to make a special case for them in order + // to avoid having double values in the switch case + if ((errno == EAGAIN) || (errno == EINPROGRESS)) + return Socket::Status::NotReady; + + // clang-format off + switch (errno) + { + case EWOULDBLOCK: return Socket::Status::NotReady; + case ECONNABORTED: return Socket::Status::Disconnected; + case ECONNRESET: return Socket::Status::Disconnected; + case ETIMEDOUT: return Socket::Status::Disconnected; + case ENETRESET: return Socket::Status::Disconnected; + case ENOTCONN: return Socket::Status::Disconnected; + case EPIPE: return Socket::Status::Disconnected; + default: return Socket::Status::Error; + } + // clang-format on +} + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/Network/Win32/SocketImpl.cpp b/third_party/sfml/src/SFML/Network/Win32/SocketImpl.cpp new file mode 100644 index 00000000..c35dd1f8 --- /dev/null +++ b/third_party/sfml/src/SFML/Network/Win32/SocketImpl.cpp @@ -0,0 +1,110 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + + +namespace +{ +//////////////////////////////////////////////////////////// +// Windows needs some initialization and cleanup to get +// sockets working properly... so let's create a class that will +// do it automatically +//////////////////////////////////////////////////////////// +struct SocketInitializer +{ + SocketInitializer() + { + WSADATA init; + WSAStartup(MAKEWORD(2, 2), &init); + } + + ~SocketInitializer() + { + WSACleanup(); + } +} globalInitializer; +} // namespace + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +sockaddr_in SocketImpl::createAddress(std::uint32_t address, unsigned short port) +{ + auto addr = sockaddr_in(); + addr.sin_addr.s_addr = htonl(address); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + return addr; +} + + +//////////////////////////////////////////////////////////// +SocketHandle SocketImpl::invalidSocket() +{ + return INVALID_SOCKET; +} + + +//////////////////////////////////////////////////////////// +void SocketImpl::close(SocketHandle sock) +{ + closesocket(sock); +} + + +//////////////////////////////////////////////////////////// +void SocketImpl::setBlocking(SocketHandle sock, bool block) +{ + u_long blocking = block ? 0 : 1; + ioctlsocket(sock, static_cast(FIONBIO), &blocking); +} + + +//////////////////////////////////////////////////////////// +Socket::Status SocketImpl::getErrorStatus() +{ + // clang-format off + switch (WSAGetLastError()) + { + case WSAEWOULDBLOCK: return Socket::Status::NotReady; + case WSAEALREADY: return Socket::Status::NotReady; + case WSAECONNABORTED: return Socket::Status::Disconnected; + case WSAECONNRESET: return Socket::Status::Disconnected; + case WSAETIMEDOUT: return Socket::Status::Disconnected; + case WSAENETRESET: return Socket::Status::Disconnected; + case WSAENOTCONN: return Socket::Status::Disconnected; + case WSAEISCONN: return Socket::Status::Done; // when connecting a non-blocking socket + default: return Socket::Status::Error; + } + // clang-format on +} +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Android/Activity.cpp b/third_party/sfml/src/SFML/System/Android/Activity.cpp new file mode 100644 index 00000000..dff18e16 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Android/Activity.cpp @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com) +// +// 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. +// +//////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_INFO, "sfml-error", __VA_ARGS__)) + +std::streambuf::int_type LogcatStream::overflow(std::streambuf::int_type c) +{ + if (c == '\n') + { + m_message.push_back(static_cast(c)); + LOGE("%s", m_message.c_str()); + m_message.clear(); + } + + m_message.push_back(static_cast(c)); + + return traits_type::not_eof(c); +} + +namespace sf::priv +{ + +ActivityStates*& getActivityStatesPtr() +{ + static ActivityStates* states = nullptr; + return states; +} + +void resetActivity(ActivityStates* initializedStates) +{ + getActivityStatesPtr() = initializedStates; +} + +ActivityStates& getActivity() +{ + ActivityStates* const states = getActivityStatesPtr(); + assert(states != nullptr && + "Cannot dereference null activity states pointer. Call priv::resetActivity() to initialize it."); + return *states; +} + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Android/Activity.hpp b/third_party/sfml/src/SFML/System/Android/Activity.hpp new file mode 100644 index 00000000..f2650f8d --- /dev/null +++ b/third_party/sfml/src/SFML/System/Android/Activity.hpp @@ -0,0 +1,103 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +class SFML_SYSTEM_API LogcatStream : public std::streambuf +{ +public: + LogcatStream() = default; + + std::streambuf::int_type overflow(std::streambuf::int_type c) override; + +private: + std::string m_message; +}; + +namespace sf::priv +{ +struct ActivityStates +{ + ANativeActivity* activity{}; + ANativeWindow* window{}; + + ALooper* looper{}; + AInputQueue* inputQueue{}; + AConfiguration* config{}; + + EGLDisplay display{}; + EglContext* context{}; + + std::vector savedState; + + std::recursive_mutex mutex; + + void (*forwardEvent)(const Event& event){}; + int (*processEvent)(int fd, int events, void* data){}; + + std::unordered_map touchEvents; + Vector2i mousePosition; + EnumArray isButtonPressed{}; + + bool mainOver{}; + + Vector2i screenSize; + Vector2i fullScreenSize; + + bool initialized{}; + bool terminated{}; + + bool fullscreen{}; + + bool updated{}; + + LogcatStream logcat; +}; + +SFML_SYSTEM_API ActivityStates*& getActivityStatesPtr(); + +SFML_SYSTEM_API void resetActivity(ActivityStates* initializedStates); + +SFML_SYSTEM_API ActivityStates& getActivity(); + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Android/NativeActivity.cpp b/third_party/sfml/src/SFML/System/Android/NativeActivity.cpp new file mode 100644 index 00000000..c9cb36b5 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Android/NativeActivity.cpp @@ -0,0 +1,39 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +namespace sf +{ +//////////////////////////////////////////////////////////// +ANativeActivity* getNativeActivity() +{ + return priv::getActivity().activity; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/Android/ResourceStream.cpp b/third_party/sfml/src/SFML/System/Android/ResourceStream.cpp new file mode 100644 index 00000000..69d6db26 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Android/ResourceStream.cpp @@ -0,0 +1,91 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com) +// +// 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. +// +//////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +bool ResourceStream::open(const std::filesystem::path& filename) +{ + ActivityStates& states = getActivity(); + const std::lock_guard lock(states.mutex); + m_file.reset(AAssetManager_open(states.activity->assetManager, filename.c_str(), AASSET_MODE_UNKNOWN)); + return m_file != nullptr; +} + + +//////////////////////////////////////////////////////////// +std::optional ResourceStream::read(void* data, std::size_t size) +{ + assert(m_file && "ResourceStream::read() cannot be called when file is not initialized"); + const auto numBytesRead = AAsset_read(m_file.get(), data, size); + if (numBytesRead < 0) + return std::nullopt; + return numBytesRead; +} + + +//////////////////////////////////////////////////////////// +std::optional ResourceStream::seek(std::size_t position) +{ + assert(m_file && "ResourceStream::seek() cannot be called when file is not initialized"); + const auto newPosition = AAsset_seek(m_file.get(), static_cast(position), SEEK_SET); + if (newPosition < 0) + return std::nullopt; + return newPosition; +} + + +//////////////////////////////////////////////////////////// +std::optional ResourceStream::tell() +{ + assert(m_file && "ResourceStream::tell() cannot be called when file is not initialized"); + return getSize().value() - static_cast(AAsset_getRemainingLength(m_file.get())); +} + + +//////////////////////////////////////////////////////////// +std::optional ResourceStream::getSize() +{ + assert(m_file && "ResourceStream::getSize() cannot be called when file is not initialized"); + return AAsset_getLength(m_file.get()); +} + + +//////////////////////////////////////////////////////////// +void ResourceStream::AAssetDeleter::operator()(AAsset* file) +{ + AAsset_close(file); +} + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Android/ResourceStream.hpp b/third_party/sfml/src/SFML/System/Android/ResourceStream.hpp new file mode 100644 index 00000000..e263cb80 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Android/ResourceStream.hpp @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + +#include +#include + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +/// \brief Read from Android asset files +/// +//////////////////////////////////////////////////////////// +class SFML_SYSTEM_API ResourceStream : public InputStream +{ +public: + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// \param filename Filename of the asset + /// + //////////////////////////////////////////////////////////// + ResourceStream() = default; + + //////////////////////////////////////////////////////////// + /// \brief Open the stream from a file path + /// + /// \param filename Name of the file to open + /// + /// \return `true` on success, `false` on error + /// + //////////////////////////////////////////////////////////// + [[nodiscard]] bool open(const std::filesystem::path& filename); + + //////////////////////////////////////////////////////////// + /// \brief Read data from the asset + /// + /// \param data Buffer where the asset data is copied + /// \param size Number of bytes read + /// + /// \return The number of bytes actually read, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + std::optional read(void* data, std::size_t size) override; + + //////////////////////////////////////////////////////////// + /// \brief Change the current reading position in the asset file + /// + /// \param position The position to seek to, from the beginning + /// + /// \return The position actually sought to, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + std::optional seek(std::size_t position) override; + + //////////////////////////////////////////////////////////// + /// \brief Get the current reading position in the asset file + /// + /// \return The current position, or `std::nullopt` on error. + /// + //////////////////////////////////////////////////////////// + std::optional tell() override; + + //////////////////////////////////////////////////////////// + /// \brief Return the size of the asset file + /// + /// \return The total number of bytes available in the asset, or `std::nullopt` on error + /// + //////////////////////////////////////////////////////////// + std::optional getSize() override; + +private: + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// + struct AAssetDeleter + { + void operator()(AAsset*); + }; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + std::unique_ptr m_file; ///< The asset file to read +}; + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Android/SuspendAwareClock.cpp b/third_party/sfml/src/SFML/System/Android/SuspendAwareClock.cpp new file mode 100644 index 00000000..10d38390 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Android/SuspendAwareClock.cpp @@ -0,0 +1,46 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +namespace sf +{ + +SuspendAwareClock::time_point SuspendAwareClock::now() noexcept +{ + ::timespec ts{}; +#ifdef CLOCK_BOOTTIME + clock_gettime(CLOCK_BOOTTIME, &ts); +#else +#error "CLOCK_BOOTTIME is essential for SuspendAwareClock to work" +#endif // CLOCK_BOOTTIME + return time_point(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)); +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/CMakeLists.txt b/third_party/sfml/src/SFML/System/CMakeLists.txt new file mode 100644 index 00000000..98bba6c2 --- /dev/null +++ b/third_party/sfml/src/SFML/System/CMakeLists.txt @@ -0,0 +1,96 @@ +set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +set(INCROOT ${PROJECT_SOURCE_DIR}/include/SFML/System) +set(SRCROOT ${PROJECT_SOURCE_DIR}/src/SFML/System) + +# all source files +set(SRC + ${INCROOT}/../System.hpp + ${INCROOT}/Angle.hpp + ${INCROOT}/Angle.inl + ${INCROOT}/Clock.hpp + ${INCROOT}/Err.hpp + ${INCROOT}/Exception.hpp + ${INCROOT}/Export.hpp + ${INCROOT}/FileInputStream.hpp + ${INCROOT}/InputStream.hpp + ${INCROOT}/MemoryInputStream.hpp + ${INCROOT}/NativeActivity.hpp + ${INCROOT}/Sleep.hpp + ${INCROOT}/String.hpp + ${INCROOT}/String.inl + ${INCROOT}/SuspendAwareClock.hpp + ${INCROOT}/Time.hpp + ${INCROOT}/Time.inl + ${INCROOT}/Utf.hpp + ${INCROOT}/Utf.inl + ${INCROOT}/Vector2.hpp + ${INCROOT}/Vector2.inl + ${INCROOT}/Vector3.hpp + ${INCROOT}/Vector3.inl + ${SRCROOT}/Clock.cpp + ${SRCROOT}/EnumArray.hpp + ${SRCROOT}/Err.cpp + ${SRCROOT}/FileInputStream.cpp + ${SRCROOT}/MemoryInputStream.cpp + ${SRCROOT}/Sleep.cpp + ${SRCROOT}/String.cpp + ${SRCROOT}/Utils.cpp + ${SRCROOT}/Utils.hpp + ${SRCROOT}/Vector2.cpp + ${SRCROOT}/Vector3.cpp +) +source_group("" FILES ${SRC}) + +# add platform specific sources +if(WIN32) + set(PLATFORM_SRC + ${SRCROOT}/Win32/SleepImpl.cpp + ${SRCROOT}/Win32/SleepImpl.hpp + ${SRCROOT}/Win32/WindowsHeader.hpp + ) + source_group("windows" FILES ${PLATFORM_SRC}) +else() + set(PLATFORM_SRC + ${SRCROOT}/Unix/SleepImpl.cpp + ${SRCROOT}/Unix/SleepImpl.hpp + ) + + if(ANDROID) + set(PLATFORM_SRC ${PLATFORM_SRC} + ${SRCROOT}/Android/Activity.cpp + ${SRCROOT}/Android/Activity.hpp + ${SRCROOT}/Android/NativeActivity.cpp + ${SRCROOT}/Android/ResourceStream.cpp + ${SRCROOT}/Android/ResourceStream.hpp + ${SRCROOT}/Android/SuspendAwareClock.cpp + ) + endif() + + source_group("unix" FILES ${PLATFORM_SRC}) +endif() + +# define the sfml-system target +add_library(sfml-system STATIC ${SRC} ${PLATFORM_SRC}) + +target_include_directories(sfml-system PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_SOURCE_DIR}/../../../include) + +target_compile_definitions(sfml-system PRIVATE SFML_STATIC) +target_compile_definitions(sfml-system INTERFACE SFML_STATIC) + +if(ANDROID) + # glad sources + target_include_directories(sfml-system PRIVATE "${PROJECT_SOURCE_DIR}/extlibs/headers/glad/include") +endif() + +# setup dependencies +if(UNIX OR APPLE) + target_link_libraries(sfml-system PRIVATE pthread) +endif() +if(UNIX AND NOT APPLE) + target_link_libraries(sfml-system PRIVATE rt) +elseif(WIN32) + target_link_libraries(sfml-system PRIVATE winmm) +elseif(ANDROID) + target_link_libraries(sfml-system PRIVATE android log) +endif() diff --git a/third_party/sfml/src/SFML/System/Clock.cpp b/third_party/sfml/src/SFML/System/Clock.cpp new file mode 100644 index 00000000..3f07b8cd --- /dev/null +++ b/third_party/sfml/src/SFML/System/Clock.cpp @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +Time Clock::getElapsedTime() const +{ + if (isRunning()) + return std::chrono::duration_cast(priv::ClockImpl::now() - m_refPoint); + return std::chrono::duration_cast(m_stopPoint - m_refPoint); +} + + +//////////////////////////////////////////////////////////// +bool Clock::isRunning() const +{ + return m_stopPoint == priv::ClockImpl::time_point(); +} + + +//////////////////////////////////////////////////////////// +void Clock::start() +{ + if (!isRunning()) + { + m_refPoint += priv::ClockImpl::now() - m_stopPoint; + m_stopPoint = {}; + } +} + + +//////////////////////////////////////////////////////////// +void Clock::stop() +{ + if (isRunning()) + m_stopPoint = priv::ClockImpl::now(); +} + + +//////////////////////////////////////////////////////////// +Time Clock::restart() +{ + const Time elapsed = getElapsedTime(); + m_refPoint = priv::ClockImpl::now(); + m_stopPoint = {}; + return elapsed; +} + + +//////////////////////////////////////////////////////////// +Time Clock::reset() +{ + const Time elapsed = getElapsedTime(); + m_refPoint = priv::ClockImpl::now(); + m_stopPoint = m_refPoint; + return elapsed; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/Dependencies.cmake.in b/third_party/sfml/src/SFML/System/Dependencies.cmake.in new file mode 100644 index 00000000..1a02eba8 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Dependencies.cmake.in @@ -0,0 +1,13 @@ +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +include(CMakeFindDependencyMacro) + +# start with an empty list +set(FIND_SFML_DEPENDENCIES_NOTFOUND) + +find_dependency(Threads) + +if(FIND_SFML_DEPENDENCIES_NOTFOUND) + set(FIND_SFML_ERROR "SFML found but some of its dependencies are missing (${FIND_SFML_DEPENDENCIES_NOTFOUND})") + set(SFML_FOUND OFF) +endif() diff --git a/third_party/sfml/src/SFML/System/EnumArray.hpp b/third_party/sfml/src/SFML/System/EnumArray.hpp new file mode 100644 index 00000000..25f65b25 --- /dev/null +++ b/third_party/sfml/src/SFML/System/EnumArray.hpp @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include +#include + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +/// \brief Fixed-size array container indexed by an enumeration +/// +//////////////////////////////////////////////////////////// +template +struct EnumArray : public std::array +{ + static_assert(std::is_enum_v, "Enum type parameter must be an enumeration"); + + //////////////////////////////////////////////////////////// + /// \brief Returns a reference to the element associated to specified \a key + /// + /// No bounds checking is performed in release builds. + /// + //////////////////////////////////////////////////////////// + constexpr Value& operator[](Enum key) + { + const auto index = static_cast(key); + assert(index < Count && "Index is out of bounds"); + return std::array::operator[](index); + } + + //////////////////////////////////////////////////////////// + /// \brief Returns a reference to the element associated to specified \a key + /// + /// No bounds checking is performed in release builds. + /// + //////////////////////////////////////////////////////////// + constexpr const Value& operator[](Enum key) const + { + const auto index = static_cast(key); + assert(index < Count && "Index is out of bounds"); + return std::array::operator[](index); + } +}; + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Err.cpp b/third_party/sfml/src/SFML/System/Err.cpp new file mode 100644 index 00000000..f4136661 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Err.cpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include + +#include + + +namespace +{ +// This class will be used as the default streambuf of sf::Err, +// it outputs to stderr by default (to keep the default behavior) +class DefaultErrStreamBuf : public std::streambuf +{ +public: + DefaultErrStreamBuf() + { + // Allocate the write buffer + constexpr int size = 64; + char* buffer = new char[size]; + setp(buffer, buffer + size); + } + + ~DefaultErrStreamBuf() override + { + // Synchronize + sync(); + + // Delete the write buffer + delete[] pbase(); + } + +private: + int overflow(int character) override + { + if ((character != EOF) && (pptr() != epptr())) + { + // Valid character + return sputc(static_cast(character)); + } + if (character != EOF) + { + // Not enough space in the buffer: synchronize output and try again + sync(); + return overflow(character); + } + + // Invalid character: synchronize output + return sync(); + } + + int sync() override + { + // Check if there is something into the write buffer + if (pbase() != pptr()) + { + // Print the contents of the write buffer into the standard error output + const auto size = static_cast(pptr() - pbase()); + std::fwrite(pbase(), 1, size, stderr); + + // Reset the pointer position to the beginning of the write buffer + setp(pbase(), epptr()); + } + + return 0; + } +}; +} // namespace + +namespace sf +{ +//////////////////////////////////////////////////////////// +std::ostream& err() +{ + static DefaultErrStreamBuf buffer; + static std::ostream stream(&buffer); + + return stream; +} + + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/FileInputStream.cpp b/third_party/sfml/src/SFML/System/FileInputStream.cpp new file mode 100644 index 00000000..70e18fdb --- /dev/null +++ b/third_party/sfml/src/SFML/System/FileInputStream.cpp @@ -0,0 +1,167 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#ifdef SFML_SYSTEM_ANDROID +#include +#include +#endif +#include + +#include + +namespace sf +{ +//////////////////////////////////////////////////////////// +void FileInputStream::FileCloser::operator()(std::FILE* file) +{ + std::fclose(file); +} + + +//////////////////////////////////////////////////////////// +FileInputStream::FileInputStream() = default; + + +//////////////////////////////////////////////////////////// +FileInputStream::FileInputStream(const std::filesystem::path& filename) +{ + if (!open(filename)) + throw Exception("Failed to open file input stream"); +} + + +//////////////////////////////////////////////////////////// +FileInputStream::~FileInputStream() = default; + + +//////////////////////////////////////////////////////////// +FileInputStream::FileInputStream(FileInputStream&&) noexcept = default; + + +//////////////////////////////////////////////////////////// +FileInputStream& FileInputStream::operator=(FileInputStream&&) noexcept = default; + + +//////////////////////////////////////////////////////////// +bool FileInputStream::open(const std::filesystem::path& filename) +{ +#ifdef SFML_SYSTEM_ANDROID + if (priv::getActivityStatesPtr() != nullptr) + { + m_androidFile = std::make_unique(); + if (!m_androidFile->open(filename)) + return false; + return m_androidFile->tell().has_value(); + } +#endif + m_file.reset(openFile(filename, "rb")); + return m_file != nullptr; +} + + +//////////////////////////////////////////////////////////// +std::optional FileInputStream::read(void* data, std::size_t size) +{ +#ifdef SFML_SYSTEM_ANDROID + if (priv::getActivityStatesPtr() != nullptr) + { + if (!m_androidFile) + return std::nullopt; + return m_androidFile->read(data, size); + } +#endif + if (!m_file) + return std::nullopt; + return std::fread(data, 1, size, m_file.get()); +} + + +//////////////////////////////////////////////////////////// +std::optional FileInputStream::seek(std::size_t position) +{ +#ifdef SFML_SYSTEM_ANDROID + if (priv::getActivityStatesPtr() != nullptr) + { + if (!m_androidFile) + return std::nullopt; + return m_androidFile->seek(position); + } +#endif + if (!m_file) + return std::nullopt; + if (std::fseek(m_file.get(), static_cast(position), SEEK_SET)) + return std::nullopt; + + return tell(); +} + + +//////////////////////////////////////////////////////////// +std::optional FileInputStream::tell() +{ +#ifdef SFML_SYSTEM_ANDROID + if (priv::getActivityStatesPtr() != nullptr) + { + if (!m_androidFile) + return std::nullopt; + return m_androidFile->tell(); + } +#endif + if (!m_file) + return std::nullopt; + const auto position = std::ftell(m_file.get()); + return position < 0 ? std::nullopt : std::optional(position); +} + + +//////////////////////////////////////////////////////////// +std::optional FileInputStream::getSize() +{ +#ifdef SFML_SYSTEM_ANDROID + if (priv::getActivityStatesPtr() != nullptr) + { + if (!m_androidFile) + return std::nullopt; + return m_androidFile->getSize(); + } +#endif + if (!m_file) + return std::nullopt; + const auto position = tell().value(); + std::fseek(m_file.get(), 0, SEEK_END); + const std::optional size = tell(); + + if (!seek(position).has_value()) + return std::nullopt; + + return size; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/MemoryInputStream.cpp b/third_party/sfml/src/SFML/System/MemoryInputStream.cpp new file mode 100644 index 00000000..72eb5561 --- /dev/null +++ b/third_party/sfml/src/SFML/System/MemoryInputStream.cpp @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +MemoryInputStream::MemoryInputStream(const void* data, std::size_t sizeInBytes) : +m_data(static_cast(data)), +m_size(sizeInBytes) +{ +} + + +//////////////////////////////////////////////////////////// +std::optional MemoryInputStream::read(void* data, std::size_t size) +{ + if (!m_data) + return std::nullopt; + + const std::size_t count = std::min(size, m_size - m_offset); + if (count > 0) + { + std::memcpy(data, m_data + m_offset, static_cast(count)); + m_offset += count; + } + + return count; +} + + +//////////////////////////////////////////////////////////// +std::optional MemoryInputStream::seek(std::size_t position) +{ + if (!m_data) + return std::nullopt; + + m_offset = position < m_size ? position : m_size; + return m_offset; +} + + +//////////////////////////////////////////////////////////// +std::optional MemoryInputStream::tell() +{ + if (!m_data) + return std::nullopt; + + return m_offset; +} + + +//////////////////////////////////////////////////////////// +std::optional MemoryInputStream::getSize() +{ + if (!m_data) + return std::nullopt; + + return m_size; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/Sleep.cpp b/third_party/sfml/src/SFML/System/Sleep.cpp new file mode 100644 index 00000000..0063bd94 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Sleep.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#if defined(SFML_SYSTEM_WINDOWS) +#include +#else +#include +#endif + + +namespace sf +{ +//////////////////////////////////////////////////////////// +void sleep(Time duration) +{ + // Note that 'std::this_thread::sleep_for' is intentionally not used here + // as it results in inconsistent sleeping times under MinGW-w64. + + if (duration >= Time::Zero) + priv::sleepImpl(duration); +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/String.cpp b/third_party/sfml/src/SFML/System/String.cpp new file mode 100644 index 00000000..a3188d5f --- /dev/null +++ b/third_party/sfml/src/SFML/System/String.cpp @@ -0,0 +1,477 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +#include +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +void U8StringCharTraits::assign(char_type& c1, char_type c2) noexcept +{ + c1 = c2; +} + + +//////////////////////////////////////////////////////////// +U8StringCharTraits::char_type* U8StringCharTraits::assign(char_type* s, std::size_t n, char_type c) +{ + return reinterpret_cast(std::char_traits::assign(reinterpret_cast(s), n, static_cast(c))); +} + + +//////////////////////////////////////////////////////////// +bool U8StringCharTraits::eq(char_type c1, char_type c2) noexcept +{ + return c1 == c2; +} + + +//////////////////////////////////////////////////////////// +bool U8StringCharTraits::lt(char_type c1, char_type c2) noexcept +{ + return c1 < c2; +} + + +//////////////////////////////////////////////////////////// +U8StringCharTraits::char_type* U8StringCharTraits::move(char_type* s1, const char_type* s2, std::size_t n) +{ + std::memmove(s1, s2, n); + return s1; +} + + +//////////////////////////////////////////////////////////// +U8StringCharTraits::char_type* U8StringCharTraits::copy(char_type* s1, const char_type* s2, std::size_t n) +{ + std::memcpy(s1, s2, n); + return s1; +} + + +//////////////////////////////////////////////////////////// +int U8StringCharTraits::compare(const char_type* s1, const char_type* s2, std::size_t n) +{ + return std::memcmp(s1, s2, n); +} + + +//////////////////////////////////////////////////////////// +std::size_t U8StringCharTraits::length(const char_type* s) +{ + return std::strlen(reinterpret_cast(s)); +} + + +//////////////////////////////////////////////////////////// +const U8StringCharTraits::char_type* U8StringCharTraits::find(const char_type* s, std::size_t n, const char_type& c) +{ + return reinterpret_cast( + std::char_traits::find(reinterpret_cast(s), n, static_cast(c))); +} + + +//////////////////////////////////////////////////////////// +U8StringCharTraits::char_type U8StringCharTraits::to_char_type(int_type i) noexcept +{ + return static_cast(std::char_traits::to_char_type(i)); +} + + +//////////////////////////////////////////////////////////// +U8StringCharTraits::int_type U8StringCharTraits::to_int_type(char_type c) noexcept +{ + return std::char_traits::to_int_type(static_cast(c)); +} + + +//////////////////////////////////////////////////////////// +bool U8StringCharTraits::eq_int_type(int_type i1, int_type i2) noexcept +{ + return i1 == i2; +} + + +//////////////////////////////////////////////////////////// +U8StringCharTraits::int_type U8StringCharTraits::eof() noexcept +{ + return std::char_traits::eof(); +} + + +//////////////////////////////////////////////////////////// +U8StringCharTraits::int_type U8StringCharTraits::not_eof(int_type i) noexcept +{ + return std::char_traits::not_eof(i); +} + + +//////////////////////////////////////////////////////////// +String::String(char ansiChar, const std::locale& locale) : m_string(1, Utf32::decodeAnsi(ansiChar, locale)) +{ +} + + +//////////////////////////////////////////////////////////// +String::String(wchar_t wideChar) : m_string(1, Utf32::decodeWide(wideChar)) +{ +} + + +//////////////////////////////////////////////////////////// +String::String(char32_t utf32Char) : m_string(1, utf32Char) +{ +} + + +//////////////////////////////////////////////////////////// +String::String(const char* ansiString, const std::locale& locale) +{ + if (ansiString) + { + const std::size_t length = std::strlen(ansiString); + if (length > 0) + { + m_string.reserve(length + 1); + Utf32::fromAnsi(ansiString, ansiString + length, std::back_inserter(m_string), locale); + } + } +} + + +//////////////////////////////////////////////////////////// +String::String(const std::string& ansiString, const std::locale& locale) +{ + m_string.reserve(ansiString.length() + 1); + Utf32::fromAnsi(ansiString.begin(), ansiString.end(), std::back_inserter(m_string), locale); +} + + +//////////////////////////////////////////////////////////// +String::String(const wchar_t* wideString) +{ + if (wideString) + { + const std::size_t length = std::wcslen(wideString); + if (length > 0) + { + m_string.reserve(length + 1); + Utf32::fromWide(wideString, wideString + length, std::back_inserter(m_string)); + } + } +} + + +//////////////////////////////////////////////////////////// +String::String(const std::wstring& wideString) +{ + m_string.reserve(wideString.length() + 1); + Utf32::fromWide(wideString.begin(), wideString.end(), std::back_inserter(m_string)); +} + + +//////////////////////////////////////////////////////////// +String::String(const char32_t* utf32String) : m_string(utf32String ? utf32String : U"") +{ +} + + +//////////////////////////////////////////////////////////// +String::String(std::u32string utf32String) : m_string(std::move(utf32String)) +{ +} + + +//////////////////////////////////////////////////////////// +String::operator std::string() const +{ + return toAnsiString(); +} + + +//////////////////////////////////////////////////////////// +String::operator std::wstring() const +{ + return toWideString(); +} + + +//////////////////////////////////////////////////////////// +std::string String::toAnsiString(const std::locale& locale) const +{ + // Prepare the output string + std::string output; + output.reserve(m_string.length() + 1); + + // Convert + Utf32::toAnsi(m_string.begin(), m_string.end(), std::back_inserter(output), 0, locale); + + return output; +} + + +//////////////////////////////////////////////////////////// +std::wstring String::toWideString() const +{ + // Prepare the output string + std::wstring output; + output.reserve(m_string.length() + 1); + + // Convert + Utf32::toWide(m_string.begin(), m_string.end(), std::back_inserter(output), 0); + + return output; +} + + +//////////////////////////////////////////////////////////// +U8String String::toUtf8() const +{ + // Prepare the output string + U8String output; + output.reserve(m_string.length()); + + // Convert + Utf32::toUtf8(m_string.begin(), m_string.end(), std::back_inserter(output)); + + return output; +} + + +//////////////////////////////////////////////////////////// +std::u16string String::toUtf16() const +{ + // Prepare the output string + std::u16string output; + output.reserve(m_string.length()); + + // Convert + Utf32::toUtf16(m_string.begin(), m_string.end(), std::back_inserter(output)); + + return output; +} + + +//////////////////////////////////////////////////////////// +std::u32string String::toUtf32() const +{ + return m_string; +} + + +//////////////////////////////////////////////////////////// +String& String::operator+=(const String& right) +{ + m_string += right.m_string; + return *this; +} + + +//////////////////////////////////////////////////////////// +char32_t String::operator[](std::size_t index) const +{ + assert(index < m_string.size() && "Index is out of bounds"); + return m_string[index]; +} + + +//////////////////////////////////////////////////////////// +char32_t& String::operator[](std::size_t index) +{ + assert(index < m_string.size() && "Index is out of bounds"); + return m_string[index]; +} + + +//////////////////////////////////////////////////////////// +void String::clear() +{ + m_string.clear(); +} + + +//////////////////////////////////////////////////////////// +std::size_t String::getSize() const +{ + return m_string.size(); +} + + +//////////////////////////////////////////////////////////// +bool String::isEmpty() const +{ + return m_string.empty(); +} + + +//////////////////////////////////////////////////////////// +void String::erase(std::size_t position, std::size_t count) +{ + m_string.erase(position, count); +} + + +//////////////////////////////////////////////////////////// +void String::insert(std::size_t position, const String& str) +{ + m_string.insert(position, str.m_string); +} + + +//////////////////////////////////////////////////////////// +std::size_t String::find(const String& str, std::size_t start) const +{ + return m_string.find(str.m_string, start); +} + + +//////////////////////////////////////////////////////////// +void String::replace(std::size_t position, std::size_t length, const String& replaceWith) +{ + m_string.replace(position, length, replaceWith.m_string); +} + + +//////////////////////////////////////////////////////////// +void String::replace(const String& searchFor, const String& replaceWith) +{ + const std::size_t step = replaceWith.getSize(); + const std::size_t len = searchFor.getSize(); + std::size_t pos = find(searchFor); + + // Replace each occurrence of search + while (pos != InvalidPos) + { + replace(pos, len, replaceWith); + pos = find(searchFor, pos + step); + } +} + + +//////////////////////////////////////////////////////////// +String String::substring(std::size_t position, std::size_t length) const +{ + return m_string.substr(position, length); +} + + +//////////////////////////////////////////////////////////// +const char32_t* String::getData() const +{ + return m_string.c_str(); +} + + +//////////////////////////////////////////////////////////// +String::Iterator String::begin() +{ + return m_string.begin(); +} + + +//////////////////////////////////////////////////////////// +String::ConstIterator String::begin() const +{ + return m_string.begin(); +} + + +//////////////////////////////////////////////////////////// +String::Iterator String::end() +{ + return m_string.end(); +} + + +//////////////////////////////////////////////////////////// +String::ConstIterator String::end() const +{ + return m_string.end(); +} + + +//////////////////////////////////////////////////////////// +bool operator==(const String& left, const String& right) +{ + return left.m_string == right.m_string; +} + + +//////////////////////////////////////////////////////////// +bool operator!=(const String& left, const String& right) +{ + return !(left == right); +} + + +//////////////////////////////////////////////////////////// +bool operator<(const String& left, const String& right) +{ + return left.m_string < right.m_string; +} + + +//////////////////////////////////////////////////////////// +bool operator>(const String& left, const String& right) +{ + return right < left; +} + + +//////////////////////////////////////////////////////////// +bool operator<=(const String& left, const String& right) +{ + return !(right < left); +} + + +//////////////////////////////////////////////////////////// +bool operator>=(const String& left, const String& right) +{ + return !(left < right); +} + + +//////////////////////////////////////////////////////////// +String operator+(const String& left, const String& right) +{ + String string = left; + string += right; + + return string; +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/Unix/SleepImpl.cpp b/third_party/sfml/src/SFML/System/Unix/SleepImpl.cpp new file mode 100644 index 00000000..dea2e24d --- /dev/null +++ b/third_party/sfml/src/SFML/System/Unix/SleepImpl.cpp @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +#include +#include + + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +void sleepImpl(Time time) +{ + const std::int64_t usecs = time.asMicroseconds(); + + // Construct the time to wait + timespec ti{}; + ti.tv_sec = static_cast(usecs / 1'000'000); + ti.tv_nsec = static_cast((usecs % 1'000'000) * 1'000); + + // Wait... + // If nanosleep returns -1, we check errno. If it is EINTR + // nanosleep was interrupted and has set ti to the remaining + // duration. We continue sleeping until the complete duration + // has passed. We stop sleeping if it was due to an error. + while ((nanosleep(&ti, &ti) == -1) && (errno == EINTR)) + { + } +} + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Unix/SleepImpl.hpp b/third_party/sfml/src/SFML/System/Unix/SleepImpl.hpp new file mode 100644 index 00000000..40427edb --- /dev/null +++ b/third_party/sfml/src/SFML/System/Unix/SleepImpl.hpp @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +class Time; +} + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +/// \brief Unix implementation of sf::Sleep +/// +/// \param time Time to sleep +/// +//////////////////////////////////////////////////////////// +void sleepImpl(Time time); + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Utils.cpp b/third_party/sfml/src/SFML/System/Utils.cpp new file mode 100644 index 00000000..ee5d0662 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Utils.cpp @@ -0,0 +1,64 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include + +#include + + +namespace sf +{ +std::string toLower(std::string str) +{ + for (char& c : str) + c = static_cast(std::tolower(static_cast(c))); + return str; +} + +std::string formatDebugPathInfo(const std::filesystem::path& path) +{ + std::ostringstream oss; + // convert to UTF-8 to handle non-ascii/non-latin1 filenames on windows + // cast is required to work in C++20 where u8string is char8_t which can't be printed to char stream + oss << " Provided path: " << reinterpret_cast(path.u8string().c_str()) << '\n' // + << " Absolute path: " << reinterpret_cast(std::filesystem::absolute(path).u8string().c_str()); + return oss.str(); +} + +std::FILE* openFile(const std::filesystem::path& filename, std::string_view mode) +{ +#ifdef SFML_SYSTEM_WINDOWS + const std::wstring wmode(mode.begin(), mode.end()); + return _wfopen(filename.c_str(), wmode.data()); +#else + return std::fopen(filename.c_str(), mode.data()); +#endif +} + +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/Utils.hpp b/third_party/sfml/src/SFML/System/Utils.hpp new file mode 100644 index 00000000..c005afbe --- /dev/null +++ b/third_party/sfml/src/SFML/System/Utils.hpp @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +#include +#include +#include + +#include +#include + + +namespace sf +{ +[[nodiscard]] SFML_SYSTEM_API std::string toLower(std::string str); +[[nodiscard]] SFML_SYSTEM_API std::string formatDebugPathInfo(const std::filesystem::path& path); + +// Convert byte sequence into integer +// toInteger(0x12, 0x34, 0x56) == 0x563412 +template +[[nodiscard]] constexpr IntegerType toInteger(Bytes... byte) +{ + static_assert(sizeof(IntegerType) >= sizeof...(Bytes), "IntegerType not large enough to contain bytes"); + + IntegerType integer = 0; + std::size_t index = 0; + return ((integer |= static_cast(static_cast(byte) << 8 * index++)), ...); +} + +[[nodiscard]] SFML_SYSTEM_API std::FILE* openFile(const std::filesystem::path& filename, std::string_view mode); +} // namespace sf diff --git a/third_party/sfml/src/SFML/System/Vector2.cpp b/third_party/sfml/src/SFML/System/Vector2.cpp new file mode 100644 index 00000000..6087e385 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Vector2.cpp @@ -0,0 +1,124 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#include + +#include + +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +template +Vector2 Vector2::normalized() const +{ + static_assert(std::is_floating_point_v, "Vector2::normalized() is only supported for floating point types"); + + assert(*this != Vector2() && "Vector2::normalized() cannot normalize a zero vector"); + return (*this) / length(); +} + + +//////////////////////////////////////////////////////////// +template +Angle Vector2::angleTo(Vector2 rhs) const +{ + static_assert(std::is_floating_point_v, "Vector2::angleTo() is only supported for floating point types"); + + assert(*this != Vector2() && "Vector2::angleTo() cannot calculate angle from a zero vector"); + assert(rhs != Vector2() && "Vector2::angleTo() cannot calculate angle to a zero vector"); + return radians(static_cast(std::atan2(cross(rhs), dot(rhs)))); +} + + +//////////////////////////////////////////////////////////// +template +Angle Vector2::angle() const +{ + static_assert(std::is_floating_point_v, "Vector2::angle() is only supported for floating point types"); + + assert(*this != Vector2() && "Vector2::angle() cannot calculate angle from a zero vector"); + return radians(static_cast(std::atan2(y, x))); +} + + +//////////////////////////////////////////////////////////// +template +Vector2 Vector2::rotatedBy(Angle phi) const +{ + static_assert(std::is_floating_point_v, "Vector2::rotatedBy() is only supported for floating point types"); + + // No zero vector assert, because rotating a zero vector is well-defined (yields always itself) + T cos = std::cos(static_cast(phi.asRadians())); + T sin = std::sin(static_cast(phi.asRadians())); + + // Don't manipulate x and y separately, otherwise they're overwritten too early + return Vector2(cos * x - sin * y, sin * x + cos * y); +} + + +//////////////////////////////////////////////////////////// +template +Vector2 Vector2::projectedOnto(Vector2 axis) const +{ + static_assert(std::is_floating_point_v, "Vector2::projectedOnto() is only supported for floating point types"); + + assert(axis != Vector2() && "Vector2::projectedOnto() cannot project onto a zero vector"); + return dot(axis) / axis.lengthSquared() * axis; +} + + +//////////////////////////////////////////////////////////// +template +Vector2::Vector2(T r, Angle phi) : +x(r * static_cast(std::cos(phi.asRadians()))), +y(r * static_cast(std::sin(phi.asRadians()))) +{ + static_assert(std::is_floating_point_v, "Vector2::Vector2(T, Angle) is only supported for floating point types"); +} + + +//////////////////////////////////////////////////////////// +template +T Vector2::length() const +{ + static_assert(std::is_floating_point_v, "Vector2::length() is only supported for floating point types"); + + // don't use std::hypot because of slow performance + return std::sqrt(x * x + y * y); +} + +} // namespace sf + + +//////////////////////////////////////////////////////////// +// Explicit template instantiations +//////////////////////////////////////////////////////////// + +template class sf::Vector2; +template class sf::Vector2; +template class sf::Vector2; diff --git a/third_party/sfml/src/SFML/System/Vector3.cpp b/third_party/sfml/src/SFML/System/Vector3.cpp new file mode 100644 index 00000000..489957e9 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Vector3.cpp @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#include + +#include + +#include +#include + + +namespace sf +{ +//////////////////////////////////////////////////////////// +template +Vector3 Vector3::normalized() const +{ + static_assert(std::is_floating_point_v, "Vector3::normalized() is only supported for floating point types"); + + assert(*this != Vector3() && "Vector3::normalized() cannot normalize a zero vector"); + return (*this) / length(); +} + + +//////////////////////////////////////////////////////////// +template +T Vector3::length() const +{ + static_assert(std::is_floating_point_v, "Vector3::length() is only supported for floating point types"); + + // don't use std::hypot because of slow performance + return std::sqrt(x * x + y * y + z * z); +} + +} // namespace sf + + +//////////////////////////////////////////////////////////// +// Explicit template instantiations +//////////////////////////////////////////////////////////// + +template class sf::Vector3; +template class sf::Vector3; +template class sf::Vector3; diff --git a/third_party/sfml/src/SFML/System/Win32/SleepImpl.cpp b/third_party/sfml/src/SFML/System/Win32/SleepImpl.cpp new file mode 100644 index 00000000..5cd85c16 --- /dev/null +++ b/third_party/sfml/src/SFML/System/Win32/SleepImpl.cpp @@ -0,0 +1,57 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + +#include + +namespace sf::priv +{ +//////////////////////////////////////////////////////////// +void sleepImpl(Time time) +{ + // Get the minimum supported timer resolution on this system + static const UINT periodMin = [] + { + TIMECAPS tc; + timeGetDevCaps(&tc, sizeof(TIMECAPS)); + return tc.wPeriodMin; + }(); + + // Set the timer resolution to the minimum for the Sleep call + timeBeginPeriod(periodMin); + + // Wait... + ::Sleep(static_cast(time.asMilliseconds())); + + // Reset the timer resolution back to the system default + timeEndPeriod(periodMin); +} + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Win32/SleepImpl.hpp b/third_party/sfml/src/SFML/System/Win32/SleepImpl.hpp new file mode 100644 index 00000000..e232c0de --- /dev/null +++ b/third_party/sfml/src/SFML/System/Win32/SleepImpl.hpp @@ -0,0 +1,48 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + +namespace sf +{ +class Time; +} + +namespace sf::priv +{ + +//////////////////////////////////////////////////////////// +/// \brief Windows implementation of sf::Sleep +/// +/// \param time Time to sleep +/// +//////////////////////////////////////////////////////////// +void sleepImpl(Time time); + +} // namespace sf::priv diff --git a/third_party/sfml/src/SFML/System/Win32/WindowsHeader.hpp b/third_party/sfml/src/SFML/System/Win32/WindowsHeader.hpp new file mode 100644 index 00000000..0e6c5ddf --- /dev/null +++ b/third_party/sfml/src/SFML/System/Win32/WindowsHeader.hpp @@ -0,0 +1,55 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org) +// +// 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. +// +//////////////////////////////////////////////////////////// + +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifndef _WIN32_WINDOWS +#define _WIN32_WINDOWS 0x0501 // NOLINT(bugprone-reserved-identifier) +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0501 +#endif + +#ifndef WINVER +#define WINVER 0x0501 +#endif + +#ifndef UNICODE +#define UNICODE 1 +#endif + +#ifndef _UNICODE +#define _UNICODE 1 // NOLINT(bugprone-reserved-identifier) +#endif + +#include diff --git a/tools/Windows/msys2-builder b/tools/Windows/msys2-builder index 8574c6ff..30c6f83c 100755 --- a/tools/Windows/msys2-builder +++ b/tools/Windows/msys2-builder @@ -107,7 +107,6 @@ export BUILD_ENV DIST_TAR_ARGS=" help2man --exclude ChangeLog - sfml --exclude libs-osx expat --exclude README.md libsoxr --exclude inst-check-soxr-lsr graphviz --exclude COPYING --exclude graphviz.spec diff --git a/tools/builder/MINGW.sh b/tools/builder/MINGW.sh index de2e6f17..df1db2ec 100644 --- a/tools/builder/MINGW.sh +++ b/tools/builder/MINGW.sh @@ -321,7 +321,7 @@ table_line_append DIST_PATCHES fontconfig-target " \ table_line_append DIST_PATCHES libgd 'https://gist.githubusercontent.com/rkitover/c64ea5b83ddea94ace58c40c7de42879/raw/fbaf4885fbefb302116b56626c0e191df514e8c6/libgd-2.2.4-mingw-static.patch' -table_insert_before DISTS sfml ' +table_insert_before DISTS wxwidgets ' openal https://github.com/kcat/openal-soft/archive/openal-soft-1.19.1.tar.gz lib/libOpenAL32.a ' diff --git a/tools/builder/UNIX.sh b/tools/builder/UNIX.sh index a76bcf0c..6bcde9d8 100644 --- a/tools/builder/UNIX.sh +++ b/tools/builder/UNIX.sh @@ -14,7 +14,7 @@ export BUILD_ENV . "$(dirname "$0")/../builder/core.sh" # on mac openal is part of the system, on most unixes we need openal-soft -table_insert_before DISTS sfml ' +table_insert_before DISTS wxwidgets ' openal http://kcat.strangesoft.net/openal-releases/openal-soft-1.18.2.tar.bz2 lib/libopenal.a ' @@ -27,7 +27,7 @@ libXrender libXrandr libXfixes libXdamage libxshmfence libXi libXtst libXxf86vm" # have to build a large chunk of X11 on *nix -table_insert_before DISTS sfml ' +table_insert_before DISTS wxwidgets ' xproto https://www.x.org/archive/individual/proto/xproto-7.0.31.tar.bz2 include/X11/X.h xcb-proto https://www.x.org/archive/individual/xcb/xcb-proto-1.13.tar.bz2 lib/pkgconfig/xcb-proto.pc inputproto https://www.x.org/archive/individual/proto/inputproto-2.3.2.tar.bz2 include/X11/extensions/XI.h @@ -65,7 +65,7 @@ for dist in $XORG_DISTS; do done # and Wayland now that that's a thing -table_insert_before DISTS sfml ' +table_insert_before DISTS wxwidgets ' wayland https://wayland.freedesktop.org/releases/wayland-1.16.0.tar.xz lib/libwayland-client.so wayland-protocols https://wayland.freedesktop.org/releases/wayland-protocols-1.15.tar.xz share/pkgconfig/wayland-protocols.pc ' @@ -76,7 +76,7 @@ for dist in wayland; do done # and mesa OpenGL (the Gallium drivers in mesa require llvm) -table_insert_before DISTS sfml ' +table_insert_before DISTS wxwidgets ' libpciaccess https://www.x.org/archive//individual/lib/libpciaccess-0.14.tar.bz2 lib/libpciaccess.a libdrm https://dri.freedesktop.org/libdrm/libdrm-2.4.88.tar.bz2 lib/libdrm.a # llvm http://releases.llvm.org/5.0.0/llvm-5.0.0.src.tar.xz lib/libLLVMCore.a diff --git a/tools/builder/core.sh b/tools/builder/core.sh index 863d7c8a..3dde3588 100644 --- a/tools/builder/core.sh +++ b/tools/builder/core.sh @@ -189,7 +189,6 @@ DISTS=$DISTS' faudio https://github.com/FNA-XNA/FAudio/archive/refs/tags/24.09.tar.gz lib/libFAudio.a flac https://ftp.osuosl.org/pub/xiph/releases/flac/flac-1.3.4.tar.xz lib/libFLAC.a harfbuzz https://github.com/harfbuzz/harfbuzz/releases/download/10.0.1/harfbuzz-10.0.1.tar.xz lib/libharfbuzz.a - sfml https://github.com/SFML/SFML/releases/download/3.0.0/SFML-3.0.0-sources.zip lib/libsfml-system-s.a shared-mime-info https://gitlab.freedesktop.org/xdg/shared-mime-info/-/archive/2.2/shared-mime-info-2.2.tar.bz2 bin/update-mime-database wxwidgets https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.7/wxWidgets-3.2.7.tar.bz2 lib/libwx_baseu-3.*.a ffmpeg http://ffmpeg.org/releases/ffmpeg-7.0.2.tar.xz lib/libavformat.a @@ -360,7 +359,6 @@ DIST_ARGS="$DIST_ARGS python3 --with-ensurepip=install --with-system-expat XML-Parser EXPATINCPATH=\"\$BUILD_ROOT/root/include\" EXPATLIBPATH=\"\$BUILD_ROOT/root/lib\" doxygen -DICONV_ACCEPTS_NONCONST_INPUT:BOOL=FALSE -DICONV_ACCEPTS_CONST_INPUT:BOOL=TRUE - sfml -DSFML_USE_SYSTEM_DEPS=TRUE -DSFML_BUILD_AUDIO=FALSE libcroco --disable-Bsymbolic snappy -DSNAPPY_BUILD_TESTS=OFF -DSNAPPY_BUILD_BENCHMARKS=OFF libjpeg-turbo -DWITH_JPEG8=ON -DWITH_SIMD=OFF @@ -413,7 +411,6 @@ DIST_EXTRA_LDFLAGS="$DIST_EXTRA_LDFLAGS DIST_EXTRA_CXXFLAGS="$DIST_EXTRA_CXXFLAGS gperf -std=gnu++11 doxygen -std=gnu++11 - sfml -std=gnu++11 wxwidgets -std=gnu++11 libmodplug -std=gnu++11 libopencore-amrnb -std=gnu++11 diff --git a/tools/macOS/builder b/tools/macOS/builder index 968e0de0..bea2ac4a 100755 --- a/tools/macOS/builder +++ b/tools/macOS/builder @@ -195,12 +195,6 @@ if [ "$target_cpu" = i386 ]; then table_line_append DIST_ARGS python2 '--host= --build=' table_line_append DIST_ARGS python3 '--host= --build=' - table_line_append DIST_ARGS sfml '-DCMAKE_OSX_ARCHITECTURES=i386' - - table_line_append DIST_PRE_BUILD sfml " \ - sed -i.bak '/FATAL_ERROR \"Only 64-bit architecture is supported/d' CMakeLists.txt; \ - " - table_line_append DIST_ARGS libicu '--host= --build=' fi @@ -237,11 +231,6 @@ table_line_append DIST_CONFIGURE_OVERRIDES ffmpeg "--disable-videotoolbox --extr table_line_remove DISTS python2 -table_line_append DIST_PRE_BUILD sfml " \ - sed -E -i.bak '/OSX_DEPLOYMENT_TARGET/d' CMakeLists.txt; \ -" - - if [ -n "$APPLE_SILICON" ] && [ "$target_cpu" != ARM64 ]; then table_line_remove DISTS m4 table_line_remove DISTS XML-Parser