From 1a4a1ecd9c0fc8fa5d923c69fcc3d7be35ed577e Mon Sep 17 00:00:00 2001 From: karutoh Date: Wed, 31 Jan 2024 22:28:19 -0800 Subject: [PATCH] First commit. --- .gitea/workflows/Build_Release.yaml | 82 + .gitignore | 13 + CMakeLists.txt | 257 +++ README.md | 0 include/ehs/Anchor.h | 19 + include/ehs/Array.h | 426 ++++ include/ehs/Base64.h | 24 + include/ehs/BaseObj.h | 52 + include/ehs/Color3.h | 38 + include/ehs/Color4.h | 44 + include/ehs/Data.h | 45 + include/ehs/DataType.h | 29 + include/ehs/Dock.h | 10 + include/ehs/EHS.h | 63 + include/ehs/GarbageCollector.h | 63 + include/ehs/HRNG.h | 59 + include/ehs/HashMap.h | 146 ++ include/ehs/Link.h | 70 + include/ehs/LinkedList.h | 240 ++ include/ehs/Log.h | 116 + include/ehs/Mat2.h | 217 ++ include/ehs/Mat3.h | 322 +++ include/ehs/Mat4.h | 426 ++++ include/ehs/Math.h | 316 +++ include/ehs/PRNG.h | 60 + include/ehs/PtrData.h | 18 + include/ehs/Quat.h | 396 ++++ include/ehs/Range.h | 27 + include/ehs/Rect.h | 186 ++ include/ehs/SArray.h | 110 + include/ehs/Serializer.h | 1153 ++++++++++ include/ehs/ShdPtr.h | 121 + include/ehs/Str.h | 1875 ++++++++++++++++ include/ehs/Task.h | 50 + include/ehs/Type.h | 55 + include/ehs/Types.h | 55 + include/ehs/URI.h | 15 + include/ehs/UTF.h | 455 ++++ include/ehs/UniPtr.h | 85 + include/ehs/Util.h | 18 + include/ehs/Vec2.h | 386 ++++ include/ehs/Vec3.h | 445 ++++ include/ehs/Vec4.h | 345 +++ include/ehs/Vector.h | 637 ++++++ include/ehs/Version.h | 41 + include/ehs/WkPtr.h | 134 ++ include/ehs/database/DVar.h | 20 + include/ehs/io/BaseFile.h | 260 +++ include/ehs/io/BaseFileMonitor.h | 46 + include/ehs/io/BaseWindow.h | 114 + include/ehs/io/COM.h | 55 + include/ehs/io/Console.h | 115 + include/ehs/io/File.h | 9 + include/ehs/io/FileMonitor.h | 7 + include/ehs/io/FileMonitor_UNX.h | 37 + include/ehs/io/FileMonitor_W32.h | 37 + include/ehs/io/File_UNX.h | 73 + include/ehs/io/File_W32.h | 74 + include/ehs/io/FontAtlas.h | 69 + include/ehs/io/Glyph.h | 59 + include/ehs/io/RIFF.h | 38 + include/ehs/io/RIFF_Chunk.h | 33 + include/ehs/io/Resource.h | 45 + include/ehs/io/Window.h | 13 + include/ehs/io/Window_W32.h | 127 ++ include/ehs/io/Window_Way.h | 80 + include/ehs/io/Window_XCB.h | 94 + include/ehs/io/audio/Audio.h | 204 ++ include/ehs/io/audio/AudioCodec.h | 48 + include/ehs/io/audio/AudioDevice.h | 9 + include/ehs/io/audio/AudioDevice_ALSA.h | 46 + include/ehs/io/audio/AudioDevice_W32.h | 66 + include/ehs/io/audio/BaseAudioDevice.h | 105 + include/ehs/io/hid/Button.h | 31 + include/ehs/io/hid/ButtonState.h | 55 + include/ehs/io/hid/HID.h | 91 + include/ehs/io/hid/Input.h | 46 + include/ehs/io/hid/InputHandler.h | 58 + include/ehs/io/hid/Keyboard.h | 121 + include/ehs/io/hid/Mouse.h | 55 + include/ehs/io/img/Img.h | 188 ++ include/ehs/io/img/ImgCodec.h | 48 + include/ehs/io/img/PNG.h | 51 + include/ehs/io/img/PNG_Chunk.h | 34 + include/ehs/io/model/AnimBone.h | 40 + include/ehs/io/model/Animation.h | 49 + include/ehs/io/model/Bone.h | 73 + include/ehs/io/model/KeyFrame.h | 55 + include/ehs/io/model/Mesh.h | 80 + include/ehs/io/model/Model.h | 80 + include/ehs/io/model/PropertyChange.h | 32 + include/ehs/io/model/Vertex.h | 50 + include/ehs/io/socket/BaseTCP.h | 112 + include/ehs/io/socket/BaseUDP.h | 54 + include/ehs/io/socket/DNS.h | 14 + include/ehs/io/socket/Request.h | 164 ++ include/ehs/io/socket/Response.h | 127 ++ include/ehs/io/socket/SSL.h | 56 + include/ehs/io/socket/Socket.h | 51 + include/ehs/io/socket/TCP.h | 7 + include/ehs/io/socket/TCP_BSD.h | 94 + include/ehs/io/socket/TCP_W32.h | 94 + include/ehs/io/socket/UDP.h | 7 + include/ehs/io/socket/UDP_BSD.h | 82 + include/ehs/io/socket/UDP_W32.h | 82 + include/ehs/io/socket/rest/Spotify.h | 113 + include/ehs/io/socket/rest/Twitch.h | 39 + include/ehs/io/socket/rest/TwitchChat.h | 53 + include/ehs/io/xdg-shell-client-protocol.h | 2307 ++++++++++++++++++++ include/ehs/json/Json.h | 68 + include/ehs/json/JsonArray.h | 77 + include/ehs/json/JsonBase.h | 34 + include/ehs/json/JsonBool.h | 27 + include/ehs/json/JsonNum.h | 45 + include/ehs/json/JsonObj.h | 63 + include/ehs/json/JsonStr.h | 33 + include/ehs/json/JsonVar.h | 109 + include/ehs/system/Architecture.h | 11 + include/ehs/system/BaseMutex.h | 34 + include/ehs/system/BaseOpen.h | 35 + include/ehs/system/BaseSemaphore.h | 43 + include/ehs/system/BaseSystem.h | 13 + include/ehs/system/CPU.h | 220 ++ include/ehs/system/FileSystem.h | 17 + include/ehs/system/Mutex.h | 9 + include/ehs/system/Mutex_PT.h | 32 + include/ehs/system/Mutex_W32.h | 30 + include/ehs/system/OS.h | 27 + include/ehs/system/Open.h | 9 + include/ehs/system/Open_UNX.h | 35 + include/ehs/system/Open_W32.h | 16 + include/ehs/system/Semaphore.h | 7 + include/ehs/system/Semaphore_P.h | 44 + include/ehs/system/Semaphore_W32.h | 41 + include/ehs/system/System.h | 9 + include/ehs/system/System_LNX.h | 13 + include/ehs/system/System_W32.h | 12 + include/ehs/system/Thread.h | 148 ++ include/ehs/system/User.h | 15 + src/Base64.cpp | 104 + src/BaseObj.cpp | 154 ++ src/Color3.cpp | 100 + src/Color4.cpp | 121 + src/Data.cpp | 5 + src/DataType.cpp | 77 + src/EHS.cpp | 666 ++++++ src/GarbageCollector.cpp | 133 ++ src/HRNG_ARM64.cpp | 124 ++ src/HRNG_GCC.asm | 169 ++ src/HRNG_MSVC.asm | 169 ++ src/Log.cpp | 149 ++ src/Math.cpp | 91 + src/Math_GCC_AMD64.asm | 61 + src/Math_GCC_ARM64.s | 51 + src/Math_MSVC_AMD64.asm | 61 + src/PtrData.cpp | 64 + src/Range.cpp | 5 + src/StrToHash.cpp | 32 + src/Task.cpp | 216 ++ src/Type.cpp | 136 ++ src/URI.cpp | 57 + src/Util.cpp | 121 + src/Version.cpp | 71 + src/database/DVar.cpp | 5 + src/io/BaseFile.cpp | 607 +++++ src/io/BaseFileMonitor.cpp | 49 + src/io/BaseWindow.cpp | 72 + src/io/COM.cpp | 134 ++ src/io/Console.cpp | 799 +++++++ src/io/FileMonitor_UNX.cpp | 174 ++ src/io/FileMonitor_W32.cpp | 123 ++ src/io/File_UNX.cpp | 307 +++ src/io/File_W32.cpp | 339 +++ src/io/FontAtlas.cpp | 304 +++ src/io/Glyph.cpp | 115 + src/io/RIFF.cpp | 95 + src/io/RIFF_Chunk.cpp | 52 + src/io/Resource.cpp | 95 + src/io/Window_W32.cpp | 720 ++++++ src/io/Window_Way.cpp | 224 ++ src/io/Window_XCB.cpp | 735 +++++++ src/io/audio/Audio.cpp | 1951 +++++++++++++++++ src/io/audio/AudioCodec.cpp | 109 + src/io/audio/AudioDevice_ALSA.cpp | 383 ++++ src/io/audio/AudioDevice_W32.cpp | 581 +++++ src/io/audio/BaseAudioDevice.cpp | 171 ++ src/io/hid/Button.cpp | 50 + src/io/hid/ButtonState.cpp | 87 + src/io/hid/HID.cpp | 296 +++ src/io/hid/Input.cpp | 160 ++ src/io/hid/InputHandler.cpp | 160 ++ src/io/hid/Keyboard.cpp | 507 +++++ src/io/hid/Mouse.cpp | 88 + src/io/img/Img.cpp | 1488 +++++++++++++ src/io/img/ImgCodec.cpp | 110 + src/io/img/PNG.cpp | 232 ++ src/io/img/PNG_Chunk.cpp | 55 + src/io/model/AnimBone.cpp | 91 + src/io/model/Animation.cpp | 130 ++ src/io/model/Bone.cpp | 240 ++ src/io/model/KeyFrame.cpp | 102 + src/io/model/Mesh.cpp | 164 ++ src/io/model/Model.cpp | 275 +++ src/io/model/PropertyChange.cpp | 14 + src/io/socket/BaseTCP.cpp | 336 +++ src/io/socket/BaseUDP.cpp | 77 + src/io/socket/DNS.cpp | 75 + src/io/socket/Request.cpp | 337 +++ src/io/socket/Response.cpp | 403 ++++ src/io/socket/SSL.cpp | 236 ++ src/io/socket/Socket.cpp | 1316 +++++++++++ src/io/socket/TCP_BSD.cpp | 478 ++++ src/io/socket/TCP_W32.cpp | 500 +++++ src/io/socket/UDP_BSD.cpp | 362 +++ src/io/socket/UDP_W32.cpp | 380 ++++ src/io/socket/rest/Spotify.cpp | 653 ++++++ src/io/socket/rest/Twitch.cpp | 150 ++ src/io/socket/rest/TwitchChat.cpp | 177 ++ src/io/xdg-shell-protocol.c | 183 ++ src/json/Json.cpp | 487 +++++ src/json/JsonArray.cpp | 528 +++++ src/json/JsonBase.cpp | 24 + src/json/JsonBool.cpp | 39 + src/json/JsonNum.cpp | 93 + src/json/JsonObj.cpp | 201 ++ src/json/JsonStr.cpp | 62 + src/json/JsonVar.cpp | 369 ++++ src/system/BaseMutex.cpp | 55 + src/system/BaseOpen.cpp | 48 + src/system/BaseSemaphore.cpp | 61 + src/system/BaseSystem.cpp | 8 + src/system/CPU.cpp | 561 +++++ src/system/CPU_ARM64.cpp | 46 + src/system/CPU_GCC_AMD64.asm | 132 ++ src/system/CPU_MSVC_AMD64.asm | 127 ++ src/system/FileSystem.cpp | 32 + src/system/Mutex_PT.cpp | 76 + src/system/Mutex_W32.cpp | 88 + src/system/Open_UNX.cpp | 117 + src/system/Open_W32.cpp | 5 + src/system/Semaphore_P.cpp | 163 ++ src/system/Semaphore_W32.cpp | 136 ++ src/system/System_LNX.cpp | 22 + src/system/System_W32.cpp | 11 + src/system/Thread.cpp | 284 +++ src/system/User.cpp | 40 + 246 files changed, 42404 insertions(+) create mode 100644 .gitea/workflows/Build_Release.yaml create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 include/ehs/Anchor.h create mode 100644 include/ehs/Array.h create mode 100644 include/ehs/Base64.h create mode 100644 include/ehs/BaseObj.h create mode 100644 include/ehs/Color3.h create mode 100644 include/ehs/Color4.h create mode 100644 include/ehs/Data.h create mode 100644 include/ehs/DataType.h create mode 100644 include/ehs/Dock.h create mode 100644 include/ehs/EHS.h create mode 100644 include/ehs/GarbageCollector.h create mode 100644 include/ehs/HRNG.h create mode 100644 include/ehs/HashMap.h create mode 100644 include/ehs/Link.h create mode 100644 include/ehs/LinkedList.h create mode 100644 include/ehs/Log.h create mode 100644 include/ehs/Mat2.h create mode 100644 include/ehs/Mat3.h create mode 100644 include/ehs/Mat4.h create mode 100644 include/ehs/Math.h create mode 100644 include/ehs/PRNG.h create mode 100644 include/ehs/PtrData.h create mode 100644 include/ehs/Quat.h create mode 100644 include/ehs/Range.h create mode 100644 include/ehs/Rect.h create mode 100644 include/ehs/SArray.h create mode 100644 include/ehs/Serializer.h create mode 100644 include/ehs/ShdPtr.h create mode 100644 include/ehs/Str.h create mode 100644 include/ehs/Task.h create mode 100644 include/ehs/Type.h create mode 100644 include/ehs/Types.h create mode 100644 include/ehs/URI.h create mode 100644 include/ehs/UTF.h create mode 100644 include/ehs/UniPtr.h create mode 100644 include/ehs/Util.h create mode 100644 include/ehs/Vec2.h create mode 100644 include/ehs/Vec3.h create mode 100644 include/ehs/Vec4.h create mode 100644 include/ehs/Vector.h create mode 100644 include/ehs/Version.h create mode 100644 include/ehs/WkPtr.h create mode 100644 include/ehs/database/DVar.h create mode 100644 include/ehs/io/BaseFile.h create mode 100644 include/ehs/io/BaseFileMonitor.h create mode 100644 include/ehs/io/BaseWindow.h create mode 100644 include/ehs/io/COM.h create mode 100644 include/ehs/io/Console.h create mode 100644 include/ehs/io/File.h create mode 100644 include/ehs/io/FileMonitor.h create mode 100644 include/ehs/io/FileMonitor_UNX.h create mode 100644 include/ehs/io/FileMonitor_W32.h create mode 100644 include/ehs/io/File_UNX.h create mode 100644 include/ehs/io/File_W32.h create mode 100644 include/ehs/io/FontAtlas.h create mode 100644 include/ehs/io/Glyph.h create mode 100644 include/ehs/io/RIFF.h create mode 100644 include/ehs/io/RIFF_Chunk.h create mode 100644 include/ehs/io/Resource.h create mode 100644 include/ehs/io/Window.h create mode 100644 include/ehs/io/Window_W32.h create mode 100644 include/ehs/io/Window_Way.h create mode 100644 include/ehs/io/Window_XCB.h create mode 100644 include/ehs/io/audio/Audio.h create mode 100644 include/ehs/io/audio/AudioCodec.h create mode 100644 include/ehs/io/audio/AudioDevice.h create mode 100644 include/ehs/io/audio/AudioDevice_ALSA.h create mode 100644 include/ehs/io/audio/AudioDevice_W32.h create mode 100644 include/ehs/io/audio/BaseAudioDevice.h create mode 100644 include/ehs/io/hid/Button.h create mode 100644 include/ehs/io/hid/ButtonState.h create mode 100644 include/ehs/io/hid/HID.h create mode 100644 include/ehs/io/hid/Input.h create mode 100644 include/ehs/io/hid/InputHandler.h create mode 100644 include/ehs/io/hid/Keyboard.h create mode 100644 include/ehs/io/hid/Mouse.h create mode 100644 include/ehs/io/img/Img.h create mode 100644 include/ehs/io/img/ImgCodec.h create mode 100644 include/ehs/io/img/PNG.h create mode 100644 include/ehs/io/img/PNG_Chunk.h create mode 100644 include/ehs/io/model/AnimBone.h create mode 100644 include/ehs/io/model/Animation.h create mode 100644 include/ehs/io/model/Bone.h create mode 100644 include/ehs/io/model/KeyFrame.h create mode 100644 include/ehs/io/model/Mesh.h create mode 100644 include/ehs/io/model/Model.h create mode 100644 include/ehs/io/model/PropertyChange.h create mode 100644 include/ehs/io/model/Vertex.h create mode 100644 include/ehs/io/socket/BaseTCP.h create mode 100644 include/ehs/io/socket/BaseUDP.h create mode 100644 include/ehs/io/socket/DNS.h create mode 100644 include/ehs/io/socket/Request.h create mode 100644 include/ehs/io/socket/Response.h create mode 100644 include/ehs/io/socket/SSL.h create mode 100644 include/ehs/io/socket/Socket.h create mode 100644 include/ehs/io/socket/TCP.h create mode 100644 include/ehs/io/socket/TCP_BSD.h create mode 100644 include/ehs/io/socket/TCP_W32.h create mode 100644 include/ehs/io/socket/UDP.h create mode 100644 include/ehs/io/socket/UDP_BSD.h create mode 100644 include/ehs/io/socket/UDP_W32.h create mode 100644 include/ehs/io/socket/rest/Spotify.h create mode 100644 include/ehs/io/socket/rest/Twitch.h create mode 100644 include/ehs/io/socket/rest/TwitchChat.h create mode 100644 include/ehs/io/xdg-shell-client-protocol.h create mode 100644 include/ehs/json/Json.h create mode 100644 include/ehs/json/JsonArray.h create mode 100644 include/ehs/json/JsonBase.h create mode 100644 include/ehs/json/JsonBool.h create mode 100644 include/ehs/json/JsonNum.h create mode 100644 include/ehs/json/JsonObj.h create mode 100644 include/ehs/json/JsonStr.h create mode 100644 include/ehs/json/JsonVar.h create mode 100644 include/ehs/system/Architecture.h create mode 100644 include/ehs/system/BaseMutex.h create mode 100644 include/ehs/system/BaseOpen.h create mode 100644 include/ehs/system/BaseSemaphore.h create mode 100644 include/ehs/system/BaseSystem.h create mode 100644 include/ehs/system/CPU.h create mode 100644 include/ehs/system/FileSystem.h create mode 100644 include/ehs/system/Mutex.h create mode 100644 include/ehs/system/Mutex_PT.h create mode 100644 include/ehs/system/Mutex_W32.h create mode 100644 include/ehs/system/OS.h create mode 100644 include/ehs/system/Open.h create mode 100644 include/ehs/system/Open_UNX.h create mode 100644 include/ehs/system/Open_W32.h create mode 100644 include/ehs/system/Semaphore.h create mode 100644 include/ehs/system/Semaphore_P.h create mode 100644 include/ehs/system/Semaphore_W32.h create mode 100644 include/ehs/system/System.h create mode 100644 include/ehs/system/System_LNX.h create mode 100644 include/ehs/system/System_W32.h create mode 100644 include/ehs/system/Thread.h create mode 100644 include/ehs/system/User.h create mode 100644 src/Base64.cpp create mode 100644 src/BaseObj.cpp create mode 100644 src/Color3.cpp create mode 100644 src/Color4.cpp create mode 100644 src/Data.cpp create mode 100644 src/DataType.cpp create mode 100644 src/EHS.cpp create mode 100644 src/GarbageCollector.cpp create mode 100644 src/HRNG_ARM64.cpp create mode 100644 src/HRNG_GCC.asm create mode 100644 src/HRNG_MSVC.asm create mode 100644 src/Log.cpp create mode 100644 src/Math.cpp create mode 100644 src/Math_GCC_AMD64.asm create mode 100644 src/Math_GCC_ARM64.s create mode 100644 src/Math_MSVC_AMD64.asm create mode 100644 src/PtrData.cpp create mode 100644 src/Range.cpp create mode 100644 src/StrToHash.cpp create mode 100644 src/Task.cpp create mode 100644 src/Type.cpp create mode 100644 src/URI.cpp create mode 100644 src/Util.cpp create mode 100644 src/Version.cpp create mode 100644 src/database/DVar.cpp create mode 100644 src/io/BaseFile.cpp create mode 100644 src/io/BaseFileMonitor.cpp create mode 100644 src/io/BaseWindow.cpp create mode 100644 src/io/COM.cpp create mode 100644 src/io/Console.cpp create mode 100644 src/io/FileMonitor_UNX.cpp create mode 100644 src/io/FileMonitor_W32.cpp create mode 100644 src/io/File_UNX.cpp create mode 100644 src/io/File_W32.cpp create mode 100644 src/io/FontAtlas.cpp create mode 100644 src/io/Glyph.cpp create mode 100644 src/io/RIFF.cpp create mode 100644 src/io/RIFF_Chunk.cpp create mode 100644 src/io/Resource.cpp create mode 100644 src/io/Window_W32.cpp create mode 100644 src/io/Window_Way.cpp create mode 100644 src/io/Window_XCB.cpp create mode 100644 src/io/audio/Audio.cpp create mode 100644 src/io/audio/AudioCodec.cpp create mode 100644 src/io/audio/AudioDevice_ALSA.cpp create mode 100644 src/io/audio/AudioDevice_W32.cpp create mode 100644 src/io/audio/BaseAudioDevice.cpp create mode 100644 src/io/hid/Button.cpp create mode 100644 src/io/hid/ButtonState.cpp create mode 100644 src/io/hid/HID.cpp create mode 100644 src/io/hid/Input.cpp create mode 100644 src/io/hid/InputHandler.cpp create mode 100644 src/io/hid/Keyboard.cpp create mode 100644 src/io/hid/Mouse.cpp create mode 100644 src/io/img/Img.cpp create mode 100644 src/io/img/ImgCodec.cpp create mode 100644 src/io/img/PNG.cpp create mode 100644 src/io/img/PNG_Chunk.cpp create mode 100644 src/io/model/AnimBone.cpp create mode 100644 src/io/model/Animation.cpp create mode 100644 src/io/model/Bone.cpp create mode 100644 src/io/model/KeyFrame.cpp create mode 100644 src/io/model/Mesh.cpp create mode 100644 src/io/model/Model.cpp create mode 100644 src/io/model/PropertyChange.cpp create mode 100644 src/io/socket/BaseTCP.cpp create mode 100644 src/io/socket/BaseUDP.cpp create mode 100644 src/io/socket/DNS.cpp create mode 100644 src/io/socket/Request.cpp create mode 100644 src/io/socket/Response.cpp create mode 100644 src/io/socket/SSL.cpp create mode 100644 src/io/socket/Socket.cpp create mode 100644 src/io/socket/TCP_BSD.cpp create mode 100644 src/io/socket/TCP_W32.cpp create mode 100644 src/io/socket/UDP_BSD.cpp create mode 100644 src/io/socket/UDP_W32.cpp create mode 100644 src/io/socket/rest/Spotify.cpp create mode 100644 src/io/socket/rest/Twitch.cpp create mode 100644 src/io/socket/rest/TwitchChat.cpp create mode 100644 src/io/xdg-shell-protocol.c create mode 100644 src/json/Json.cpp create mode 100644 src/json/JsonArray.cpp create mode 100644 src/json/JsonBase.cpp create mode 100644 src/json/JsonBool.cpp create mode 100644 src/json/JsonNum.cpp create mode 100644 src/json/JsonObj.cpp create mode 100644 src/json/JsonStr.cpp create mode 100644 src/json/JsonVar.cpp create mode 100644 src/system/BaseMutex.cpp create mode 100644 src/system/BaseOpen.cpp create mode 100644 src/system/BaseSemaphore.cpp create mode 100644 src/system/BaseSystem.cpp create mode 100644 src/system/CPU.cpp create mode 100644 src/system/CPU_ARM64.cpp create mode 100644 src/system/CPU_GCC_AMD64.asm create mode 100644 src/system/CPU_MSVC_AMD64.asm create mode 100644 src/system/FileSystem.cpp create mode 100644 src/system/Mutex_PT.cpp create mode 100644 src/system/Mutex_W32.cpp create mode 100644 src/system/Open_UNX.cpp create mode 100644 src/system/Open_W32.cpp create mode 100644 src/system/Semaphore_P.cpp create mode 100644 src/system/Semaphore_W32.cpp create mode 100644 src/system/System_LNX.cpp create mode 100644 src/system/System_W32.cpp create mode 100644 src/system/Thread.cpp create mode 100644 src/system/User.cpp diff --git a/.gitea/workflows/Build_Release.yaml b/.gitea/workflows/Build_Release.yaml new file mode 100644 index 0000000..6848c82 --- /dev/null +++ b/.gitea/workflows/Build_Release.yaml @@ -0,0 +1,82 @@ +name: Build & Release +run-name: ${{ gitea.actor }} is testing out Gitea Actions +on: + push: + tags: + - "v*" + +jobs: + Linux-x86_64-Build: + runs-on: linux-x86_64 + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Installing Dependencies + run: | + sudo pacman -S --noconfirm zip alsa-lib libxcb xcb-util-cursor + + - name: Building/Compiling Project + run: | + cd ${{ gitea.workspace }} + cmake -DCMAKE_BUILD_TYPE=Debug -DLINUX_WINDOW_SYSTEM:STRING=XCB . + cmake --build . --config Debug + + - name: Creating Appropriate Directories + run: | + mkdir bin + mv StrToHash bin + mkdir lib + mv libEHS.a lib + + - name: Zipping Binaries + run: zip -r ehs-linux-amd64.zip include bin lib + + - uses: https://github.com/actions/setup-go@v4 + with: + go-version: '>=1.20.1' + + - name: Use Go Action + id: use-go-action + uses: https://gitea.com/actions/release-action@main + with: + files: |- + ehs-linux-amd64.zip + api_key: '${{secrets.RELEASE_TOKEN}}' + + Linux-AARCH64-Build: + runs-on: linux-aarch64 + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Installing Dependencies + run: sudo apt install -y zip libasound2-dev libxcb1-dev libxcb-xinput-dev libxcb-cursor-dev + + - name: Building/Compiling Project + run: | + cd ${{ gitea.workspace }} + cmake -DCMAKE_BUILD_TYPE=Debug -DLINUX_WINDOW_SYSTEM:STRING=XCB . + cmake --build . --config Debug + + - name: Creating Appropriate Directories + run: | + mkdir bin + mv StrToHash bin + mkdir lib + mv libEHS.a lib + + - name: Zipping Binaries + run: zip -r ehs-linux-aarch64.zip include bin lib + + - uses: https://github.com/actions/setup-go@v4 + with: + go-version: '>=1.20.1' + + - name: Use Go Action + id: use-go-action + uses: https://gitea.com/actions/release-action@main + with: + files: |- + ehs-linux-aarch64.zip + api_key: '${{secrets.RELEASE_TOKEN}}' \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebe792b --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.obj +*.cpp.obj +*.lib +*.exe +*.a +*.ninja_deps +*.ninja_log +*.ninja +*.cmake +*.log +/.idea/ +/cmake-build-release/ +/cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c6cc834 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,257 @@ +cmake_minimum_required(VERSION 3.25.1) + +set(IS_OS_WINDOWS FALSE) +set(IS_OS_LINUX FALSE) +set(IS_OS_MAC FALSE) + +set(IS_ARCH_AMD64 FALSE) +set(IS_ARCH_X86 FALSE) +set(IS_ARCH_ARM64 FALSE) +set(IS_ARCH_ARM FALSE) + +project(EHS CXX C) + +if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") + set(IS_OS_WINDOWS TRUE) + set(USER_HOME_DIRECTORY $ENV{USERPROFILE}) + message("Building for the Windows operating system.") +elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Linux") + set(IS_OS_LINUX TRUE) + set(USER_HOME_DIRECTORY $ENV{HOME}) + add_compile_options(-Wno-stringop-overflow) + message("Building for the Linux operating system.") +elseif (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") + set(IS_OS_MAC TRUE) + set(USER_HOME_DIRECTORY $ENV{HOME}) + message("Building for the Mac operating system.") +endif () + +if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") + set(IS_ARCH_AMD64 TRUE) + enable_language(ASM_NASM) + message("Building for the AMD64 architecture.") +elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") + set(IS_ARCH_ARM64 TRUE) + enable_language(ASM) + message("Building for the ARM64 architecture.") +endif () + +set(CMAKE_CXX_STANDARD 20) + +set(EHS_SOURCES + src/EHS.cpp include/ehs/EHS.h + src/Type.cpp include/ehs/Type.h + src/BaseObj.cpp include/ehs/BaseObj.h + src/GarbageCollector.cpp include/ehs/GarbageCollector.h + src/Log.cpp include/ehs/Log.h + src/URI.cpp include/ehs/URI.h + src/Math.cpp include/ehs/Math.h + src/Color4.cpp include/ehs/Color4.h + src/Color3.cpp include/ehs/Color3.h + src/Version.cpp include/ehs/Version.h + src/Base64.cpp include/ehs/Base64.h + src/Data.cpp include/ehs/Data.h + src/Range.cpp include/ehs/Range.h + src/Util.cpp include/ehs/Util.h + src/Task.cpp include/ehs/Task.h + src/DataType.cpp include/ehs/DataType.h + include/ehs/Anchor.h + include/ehs/Dock.h + include/ehs/HashMap.h + include/ehs/HRNG.h + include/ehs/Link.h + include/ehs/LinkedList.h + include/ehs/Mat2.h + include/ehs/Mat3.h + include/ehs/Mat4.h + include/ehs/PRNG.h + include/ehs/Quat.h + include/ehs/Rect.h + include/ehs/Str.h + include/ehs/Types.h + include/ehs/UTF.h + include/ehs/Vec2.h + include/ehs/Vec3.h + include/ehs/Vec4.h + include/ehs/Serializer.h + include/ehs/Array.h + include/ehs/Vector.h + include/ehs/SArray.h + src/PtrData.cpp include/ehs/PtrData.h + include/ehs/UniPtr.h + include/ehs/ShdPtr.h + include/ehs/WkPtr.h + + src/database/DVar.cpp include/ehs/database/DVar.h + + src/system/CPU.cpp include/ehs/system/CPU.h + src/system/Thread.cpp include/ehs/system/Thread.h + src/system/BaseMutex.cpp include/ehs/system/BaseMutex.h + src/system/BaseSemaphore.cpp include/ehs/system/BaseSemaphore.h + src/system/BaseSystem.cpp include/ehs/system/BaseSystem.h + src/system/BaseOpen.cpp include/ehs/system/BaseOpen.h + include/ehs/system/Architecture.h + include/ehs/system/Mutex.h + include/ehs/system/Open.h + include/ehs/system/OS.h + include/ehs/system/Semaphore.h + include/ehs/system/System.h + + src/json/Json.cpp include/ehs/json/Json.h + src/json/JsonBase.cpp include/ehs/json/JsonBase.h + src/json/JsonNum.cpp include/ehs/json/JsonNum.h + src/json/JsonBool.cpp include/ehs/json/JsonBool.h + src/json/JsonStr.cpp include/ehs/json/JsonStr.h + src/json/JsonObj.cpp include/ehs/json/JsonObj.h + src/json/JsonArray.cpp include/ehs/json/JsonArray.h + src/json/JsonVar.cpp include/ehs/json/JsonVar.h + + src/io/Resource.cpp include/ehs/io/Resource.h + src/io/Console.cpp include/ehs/io/Console.h + src/io/RIFF_Chunk.cpp include/ehs/io/RIFF_Chunk.h + src/io/RIFF.cpp include/ehs/io/RIFF.h + src/io/BaseWindow.cpp include/ehs/io/BaseWindow.h + src/io/BaseFile.cpp include/ehs/io/BaseFile.h + src/io/Glyph.cpp include/ehs/io/Glyph.h + src/io/FontAtlas.cpp include/ehs/io/FontAtlas.h + src/io/BaseFileMonitor.cpp include/ehs/io/BaseFileMonitor.h + include/ehs/io/COM.h + include/ehs/io/File.h + include/ehs/io/FileMonitor.h + include/ehs/io/Window.h + + src/io/socket/Request.cpp include/ehs/io/socket/Request.h + src/io/socket/Response.cpp include/ehs/io/socket/Response.h + src/io/socket/DNS.cpp include/ehs/io/socket/DNS.h + src/io/socket/BaseUDP.cpp include/ehs/io/socket/BaseUDP.h + src/io/socket/BaseTCP.cpp include/ehs/io/socket/BaseTCP.h + src/io/socket/SSL.cpp include/ehs/io/socket/SSL.h + + src/io/socket/rest/Twitch.cpp include/ehs/io/socket/rest/Twitch.h + src/io/socket/rest/TwitchChat.cpp include/ehs/io/socket/rest/TwitchChat.h + src/io/socket/rest/Spotify.cpp include/ehs/io/socket/rest/Spotify.h + include/ehs/io/socket/Socket.h + include/ehs/io/socket/TCP.h + include/ehs/io/socket/UDP.h + + src/io/audio/Audio.cpp include/ehs/io/audio/Audio.h + src/io/audio/BaseAudioDevice.cpp include/ehs/io/audio/BaseAudioDevice.h + src/io/audio/AudioCodec.cpp include/ehs/io/audio/AudioCodec.h + include/ehs/io/audio/AudioDevice.h + + src/io/img/PNG.cpp include/ehs/io/img/PNG.h + src/io/img/Img.cpp include/ehs/io/img/Img.h + src/io/img/PNG_Chunk.cpp include/ehs/io/img/PNG_Chunk.h + src/io/img/ImgCodec.cpp include/ehs/io/img/ImgCodec.h + + include/ehs/io/model/Vertex.h + src/io/model/Mesh.cpp include/ehs/io/model/Mesh.h + src/io/model/Bone.cpp include/ehs/io/model/Bone.h + src/io/model/Model.cpp include/ehs/io/model/Model.h + src/io/model/Animation.cpp include/ehs/io/model/Animation.h + src/io/model/AnimBone.cpp include/ehs/io/model/AnimBone.h + src/io/model/KeyFrame.cpp include/ehs/io/model/KeyFrame.h + src/io/model/PropertyChange.cpp include/ehs/io/model/PropertyChange.h + + src/io/hid/ButtonState.cpp include/ehs/io/hid/ButtonState.h + src/io/hid/Button.cpp include/ehs/io/hid/Button.h + src/io/hid/Mouse.cpp include/ehs/io/hid/Mouse.h + src/io/hid/Keyboard.cpp include/ehs/io/hid/Keyboard.h + src/io/hid/HID.cpp include/ehs/io/hid/HID.h + src/io/hid/InputHandler.cpp include/ehs/io/hid/InputHandler.h + src/io/hid/Input.cpp include/ehs/io/hid/Input.h +) + +if (IS_OS_WINDOWS) + list(APPEND EHS_SOURCES + src/io/socket/UDP_W32.cpp include/ehs/io/socket/UDP_W32.h + src/io/socket/TCP_W32.cpp include/ehs/io/socket/TCP_W32.h + src/system/Semaphore_W32.cpp include/ehs/system/Semaphore_W32.h + src/system/System_W32.cpp include/ehs/system/System_W32.h + src/system/Mutex_W32.cpp include/ehs/system/Mutex_W32.h + src/system/Open_W32.cpp include/ehs/system/Open_W32.h + src/io/audio/audioDevice_W32.cpp include/ehs/io/audio/audioDevice_W32.h + src/io/MsgBox.cpp include/ehs/io/MsgBox.h + src/io/File_W32.cpp include/ehs/io/File_W32.h + src/io/FileMonitor_W32.cpp include/ehs/io/FileMonitor_W32.h + src/io/Window_W32.cpp include/ehs/io/Window_W32.h + src/io/COM.cpp include/ehs/io/COM.h + src/system/CPU_MSVC_AMD64.asm src/HRNG_MSVC.asm src/Math_MSVC_AMD64.asm + ) +elseif (IS_OS_LINUX) + list(APPEND EHS_SOURCES + src/io/socket/UDP_BSD.cpp include/ehs/io/socket/UDP_BSD.h + src/io/socket/TCP_BSD.cpp include/ehs/io/socket/TCP_BSD.h + src/system/Semaphore_P.cpp include/ehs/system/Semaphore_P.h + src/system/System_LNX.cpp include/ehs/system/System_LNX.h + src/system/Open_UNX.cpp include/ehs/system/Open_UNX.h + src/io/File_UNX.cpp include/ehs/io/File_UNX.h + src/io/FileMonitor_UNX.cpp include/ehs/io/FileMonitor_UNX.h + src/system/Mutex_PT.cpp include/ehs/system/Mutex_PT.h + src/io/audio/AudioDevice_ALSA.cpp include/ehs/io/audio/AudioDevice_ALSA.h + src/system/FileSystem.cpp include/ehs/system/FileSystem.h + src/system/User.cpp include/ehs/system/User.h + ) + + set(LINUX_WINDOW_SYSTEM "Wayland" CACHE STRING "Linux Window System") + + if (LINUX_WINDOW_SYSTEM STREQUAL "Wayland") + add_compile_definitions(EHS_WS_WAYLAND) + list(APPEND EHS_SOURCES src/io/xdg-shell-protocol.c include/ehs/io/xdg-shell-client-protocol.h src/io/Window_Way.cpp include/ehs/io/Window_Way.h) + message("Building for Wayland.") + elseif (LINUX_WINDOW_SYSTEM STREQUAL "XCB") + add_compile_definitions(EHS_WS_XCB) + list(APPEND EHS_SOURCES src/io/Window_XCB.cpp include/ehs/io/Window_XCB.h) + message("Building for XCB.") + endif () + + if (IS_ARCH_AMD64) + list(APPEND EHS_SOURCES src/system/CPU_GCC_AMD64.asm src/HRNG_GCC.asm src/Math_GCC_AMD64.asm) + elseif (IS_ARCH_ARM64) + list(APPEND EHS_SOURCES src/system/CPU_ARM64.cpp src/HRNG_ARM64.cpp src/Math_GCC_ARM64.s) + endif () +endif() + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +#message("${CMAKE_CXX_FLAGS}") + +add_library(EHS ${EHS_SOURCES}) + +add_executable(StrToHash src/StrToHash.cpp) + +target_include_directories(EHS PUBLIC ${PROJECT_SOURCE_DIR}/include) + +set(CMAKE_INSTALL_PREFIX "${USER_HOME_DIRECTORY}/Libraries/EHS") +install(TARGETS EHS DESTINATION lib) + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION include) + +install(TARGETS StrToHash DESTINATION bin) + +find_package(ZLIB REQUIRED) +if (ZLIB_FOUND) + message(STATUS "ZLIB was found.") +else () + message(STATUS "ZLIB was not found.") +endif () + +find_package(OpenSSL REQUIRED) +if (OpenSSL_FOUND) + message(STATUS "OpenSSL was found.") +else () + message(STATUS "OpenSSL was not found.") +endif () + +target_link_libraries(EHS OpenSSL::SSL OpenSSL::Crypto ZLIB::ZLIB) + +if (IS_OS_WINDOWS) + target_link_libraries(StrToHash ws2_32 avrt EHS) +elseif (IS_OS_LINUX) + if (LINUX_WINDOW_SYSTEM STREQUAL "Wayland") + target_link_libraries(StrToHash wayland-client) + elseif (LINUX_WINDOW_SYSTEM STREQUAL "XCB") + target_link_libraries(StrToHash xcb xcb-cursor xcb-xfixes xcb-xinput) + endif () + + target_link_libraries(StrToHash z asound EHS) +endif () \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/include/ehs/Anchor.h b/include/ehs/Anchor.h new file mode 100644 index 0000000..7eda0ce --- /dev/null +++ b/include/ehs/Anchor.h @@ -0,0 +1,19 @@ +#pragma once + +#include "EHS.h" + +namespace ehs +{ + enum class Anchor : UInt_8 + { + TOP_LEFT, + TOP_RIGHT, + TOP_CENTER, + BOTTOM_LEFT, + BOTTOM_RIGHT, + BOTTOM_CENTER, + CENTER_LEFT, + CENTER_RIGHT, + CENTER + }; +} \ No newline at end of file diff --git a/include/ehs/Array.h b/include/ehs/Array.h new file mode 100644 index 0000000..72f38b4 --- /dev/null +++ b/include/ehs/Array.h @@ -0,0 +1,426 @@ +#pragma once + +#include "Types.h" +#include "BaseObj.h" + +#include +#include + +namespace ehs +{ + /// A helper class for C-style arrays. + /// @tparam T Array data type to use. + /// @tparam N Number data type to use. + template + class Array : public BaseObj + { + protected: + T* data; + N size; + + public: + /// Frees any data created on the heap. + ~Array() override + { + delete[] data; + } + + /// Default members initialization. + Array() + : data(nullptr), size(0) + { + AddType("Array"); + } + + /// Initializes an empty array with the given size. + /// @note Data must be assigned manually using an index. + explicit Array(const N size) + : data(new T[size]), size(size) + { + AddType("Array"); + } + + /// Initializes this array with an initializer list object. + /// @param [in] list The given initializer list. + Array(std::initializer_list list) + : data(new T[list.size()]), size(list.size()) + { + AddType("Array"); + + N i = 0; + for (auto v = list.begin(); v != list.end(); ++v) + data[i++] = std::move(*v); + } + + /// Initializes members with given C-style array. + /// @param [in] data The C-style array. + /// @param [in] size The size of the given C-style array. + Array(const T* const data, const N size) + : data(new T[size]), size(size) + { + AddType("Array"); + + for (N i = 0; i < size; ++i) + this->data[i] = data[i]; + } + + Array(Array&& array) noexcept + : BaseObj(array), data(array.data), size(array.size) + { + array.data = nullptr; + array.size = 0; + } + + /// Copies all members from the given array object. + /// @param [in] array The array object to copy from. + Array(const Array& array) + : BaseObj((BaseObj&&)array), data(new T[array.size]), size(array.size) + { + for (N i = 0; i < size; ++i) + data[i] = array.data[i]; + } + + Array& operator=(Array&& array) noexcept + { + if (this == &array) + return *this; + + BaseObj::operator=((BaseObj&&)array); + + delete[] data; + data = array.data; + size = array.size; + + array.data = nullptr; + array.size = 0; + + return *this; + } + + /// Copies all members from the given array object. + /// @param [in] array The array object to copy from. + /// @returns The array that has been assigned to. + Array& operator=(const Array& array) + { + if (this == &array) + return *this; + + BaseObj::operator=(array); + + delete[] data; + data = new T[array.size]; + for (N i = 0; i < array.size; ++i) + data[i] = array.data[i]; + + size = array.size; + + return *this; + } + + /// Copies all members from the given initializer list object. + /// @param [in] list The initializer list object to copy from. + /// @returns The array that has been assigned to. + Array& operator=(std::initializer_list list) + { + delete[] data; + data = new T[list.size]; + + N i = 0; + for (auto v = list.begin(); v != list.end(); ++v) + data[i++] = std::move(*v); + + size = list.size(); + + return *this; + } + + /// Adds a given array object at the end of the array. + /// @param [in] value The given array object to push to the end of the array. + Array& operator+=(Array value) + { + T* result = new T[size + value.size]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + for (N i = 0; i < value.size; ++i) + result[size + i] = std::move(value[i]); + + delete[] data; + + data = result; + size += value.size; + + return *this; + } + + bool operator==(const Array& in) const + { + if (size != in.size) + return false; + + return Util::Compare(data, in.data, size); + } + + bool operator!=(const Array& in) const + { + if (size != in.size) + return true; + + return !Util::Compare(data, in.data, size); + } + + /// Adds a given array object at the end of the array. + /// @param [in] value The given initializer list to push to the end of the array. + Array& operator+=(std::initializer_list value) + { + T* result = new T[size + value.size()]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + N i = 0; + for (auto v = value.begin(); v != value.end(); ++v) + result[size + i++] = std::move(*v); + + delete[] data; + + data = result; + size += value.size(); + + return *this; + } + + /// Adds a given value at the end of the array. + /// @param [in] value The given value to push to the end of the array. + Array& operator+=(const T value) + { + T* result = new T[size + 1]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + result[size] = std::move(value); + + delete[] data; + + data = result; + ++size; + + return *this; + } + + /// Retrieves the raw C-style array from casting an array object. + operator T* () const + { + return data; + } + + /// Swaps two values in the array. + /// @param a The first index to swap with. + /// @param b The second index to swap with. + void Swap(N a, N b) const + { + T tmp = std::move(data[a]); + + data[a] = std::move(data[b]); + data[b] = std::move(tmp); + } + + void Insert(const N index, const T value) + { + N newSize = 0; + if (index > size - 1) + newSize = size + ((index + 1) - size); + else + newSize = size + 1; + + T* result = new T[newSize]; + + for (N i = 0; i < index; ++i) + result[i] = std::move(data[i]); + + result[index] = std::move(value); + + for (N i = index; i < size; ++i) + result[i + 1] = std::move(data[i]); + + delete[] data; + data = result; + + size = newSize; + } + + T Remove(const N index) + { + T popped = {}; + + if (!size || index >= size) + return popped; + + popped = std::move(data[index]); + + N newSize = size - 1; + T* result = new T[newSize]; + + for (N i = 0; i < index; ++i) + result[i] = std::move(data[i]); + + for (N i = index + 1; i < size; ++i) + result[i - 1] = std::move(data[i]); + + delete[] data; + data = result; + size = newSize; + + return popped; + } + + /// Adds a given C-style array at the end of the array. + /// @param [in] value The given C-style array to push to the end of the array. + /// @param [in] size The size of the given C-style array. + void Push(const T* const value, const N size) + { + T* result = new T[this->size + size]; + + for (N i = 0; i < this->size; ++i) + result[i] = std::move(this->data[i]); + + for (N i = 0; i < size; ++i) + result[this->size + i] = value[i]; + + delete[] data; + + this->data = result; + this->size += size; + } + + /// Adds a given array object at the end of the array. + /// @param [in] value The given array object to push to the end of the array. + void Push(Array value) + { + T* result = new T[size + value.size]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + for (N i = 0; i < value.size; ++i) + result[size + i] = std::move(value[i]); + + delete[] data; + + data = result; + size += value.size; + } + + /// Adds a given array object at the end of the array. + /// @param [in] value The given initializer list to push to the end of the array. + void Push(std::initializer_list value) + { + T* result = new T[size + value.size()]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + N i = 0; + for (auto v = value.begin(); v != value.end(); ++v) + result[size + i++] = std::move(*v); + + delete[] data; + + data = result; + size += value.size(); + } + + /// Adds a given value at the end of the array. + /// @param [in] value The given value to push to the end of the array. + void Push(T value) + { + T* result = new T[size + 1]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + result[size] = std::move(value); + + delete[] data; + + data = result; + ++size; + } + + /// Removes a value at the end of the array. + /// @returns The value that was popped. + T Pop() + { + T* result = new T[--size]; + + T popped = (T&&)data[size]; + + for (N i = 0; i < size; ++i) + result[i] = (T&&)data[i]; + + delete[] data; + + data = result; + + return popped; + } + + /// Will swap the value at the given index with the value at the end of the array and pops it. + /// @param [in] index The index of the value to swap with. + /// @returns The removed value. + T Pop(const N index) + { + if (!size) + return {}; + + N lastIndex = size - 1; + + if (index < lastIndex) + Swap(index, lastIndex); + + return Pop(); + } + + void Clear() + { + if (!data) + return; + + delete[] data; + data = nullptr; + size = 0; + } + + /// Resizes the array. + /// @param [in] newSize The size to change to. + void Resize(const N newSize) + { + if (size == newSize) + return; + + T* result = new T[newSize]; + + for (N i = 0; i < newSize && i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + + data = result; + size = newSize; + } + + /// Retrieves the size of the array. + /// @returns The resulting size. + N Size() const + { + return size; + } + + N End() const + { + return size - 1; + } + }; +} \ No newline at end of file diff --git a/include/ehs/Base64.h b/include/ehs/Base64.h new file mode 100644 index 0000000..bbdb778 --- /dev/null +++ b/include/ehs/Base64.h @@ -0,0 +1,24 @@ +#pragma once + +#include "EHS.h" +#include "Str.h" + +namespace ehs +{ + class Base64 + { + private: + static const char ascii[]; + + public: + static Str_8 Encode(const Str_8 input); + + static Str_8 Decode(const Str_8 input); + + private: + static char Find(const char c); + + static bool IsBase64(const char c); + + }; +} \ No newline at end of file diff --git a/include/ehs/BaseObj.h b/include/ehs/BaseObj.h new file mode 100644 index 0000000..d87ac99 --- /dev/null +++ b/include/ehs/BaseObj.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Types.h" +#include "Type.h" + +namespace ehs +{ + class BaseObj + { + private: + Type* hierarchy; + UInt_64 hierarchySize; + + public: + virtual ~BaseObj(); + + BaseObj(); + + BaseObj(BaseObj&& base) noexcept; + + BaseObj(const BaseObj& base); + + BaseObj& operator=(BaseObj&& base) noexcept; + + BaseObj& operator=(const BaseObj& base); + + bool operator==(const BaseObj& base) const; + + bool operator!=(const BaseObj& base) const; + + const Type* GetHierarchy() const; + + UInt_64 GetHierarchySize() const; + + bool HasType(UInt_64 typeHashId) const; + + bool HasType(const Char_8* typeId) const; + + Type GetType() const; + + UInt_64 GetTypeIdSize() const; + + const Char_8* GetTypeId() const; + + UInt_64 GetTypeHashId() const; + + virtual BaseObj* Clone() const; + + protected: + void AddType(const Char_8* id); + }; +} \ No newline at end of file diff --git a/include/ehs/Color3.h b/include/ehs/Color3.h new file mode 100644 index 0000000..60d8e9f --- /dev/null +++ b/include/ehs/Color3.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Types.h" + +namespace ehs +{ + class Color3 + { + public: + float r; + float g; + float b; + + Color3(); + + Color3(const float scalar); + + Color3(const float r, const float g, const float b); + + Color3(const Color3& color); + + Color3& operator=(const float scalar); + + Color3& operator=(const Color3& color); + + bool operator==(const Color3& color) const; + + bool operator!=(const Color3& color) const; + + float operator[](const UInt_64 i) const; + + float& operator[](const UInt_64 i); + + Color3& operator*=(const Color3& color); + + Color3 operator*(const Color3& color) const; + }; +} \ No newline at end of file diff --git a/include/ehs/Color4.h b/include/ehs/Color4.h new file mode 100644 index 0000000..2ee80e9 --- /dev/null +++ b/include/ehs/Color4.h @@ -0,0 +1,44 @@ +#pragma once + +#include "Types.h" +#include "Color3.h" + +namespace ehs +{ + class Color4 + { + public: + float r; + float g; + float b; + float a; + + Color4(); + + Color4(const float scalar); + + explicit Color4(const Color3& color); + + Color4(const float r, const float g, const float b, const float a = 1.0f); + + Color4(const Color4& color); + + Color4& operator=(const float scalar); + + Color4& operator=(const Color3& color); + + Color4& operator=(const Color4& color); + + bool operator==(const Color4& color) const; + + bool operator!=(const Color4& color) const; + + float operator[](const UInt_64 i) const; + + float& operator[](const UInt_64 i); + + Color4& operator*=(const Color4& color); + + Color4 operator*(const Color4& color) const; + }; +} \ No newline at end of file diff --git a/include/ehs/Data.h b/include/ehs/Data.h new file mode 100644 index 0000000..1fe4392 --- /dev/null +++ b/include/ehs/Data.h @@ -0,0 +1,45 @@ +#pragma once + +#include "EHS.h" +#include "Serializer.h" + +namespace ehs +{ + class Data + { + public: + template + static void SwapEndianness(T* value) + { + Byte tmp = 0; + for (UInt_64 i = 0; i < sizeof(T) / 2; ++i) + { + tmp = ((Byte*)value)[i]; + ((Byte*)value)[i] = ((Byte*)value)[sizeof(T) - i - 1]; + ((Byte*)value)[sizeof(T) - i - 1] = tmp; + } + } + + template + static Array SwapEndianness(const T* const array, const UInt_64 size) + { + Array result(size); + + for (UInt_64 i = size; i; --i) + result[size - i] = array[i - 1]; + + return result; + } + + template + static Serializer SwapEndianness(const Serializer& data) + { + Serializer result(data.Size()); + + for (N i = 0; i < data.Size(); ++i) + result[i] = data[data.Size() - i - 1]; + + return result; + } + }; +} \ No newline at end of file diff --git a/include/ehs/DataType.h b/include/ehs/DataType.h new file mode 100644 index 0000000..111d45c --- /dev/null +++ b/include/ehs/DataType.h @@ -0,0 +1,29 @@ +#pragma once + +#include "EHS.h" + +namespace ehs +{ + enum class DataType : UInt_8 + { + LDOUBLE, + DOUBLE, + SINT_64, + UINT_64, + FLOAT, + SINT_32, + UINT_32, + SINT_24, + UINT_24, + SINT_16, + UINT_16, + SINT_8, + UINT_8 + }; + + DataType FromAudioBitDepth(const UInt_16 bitDepth); + + UInt_8 ToByteDepth(const DataType type); + + UInt_8 ToBitDepth(const DataType type); +} \ No newline at end of file diff --git a/include/ehs/Dock.h b/include/ehs/Dock.h new file mode 100644 index 0000000..925bf22 --- /dev/null +++ b/include/ehs/Dock.h @@ -0,0 +1,10 @@ +#pragma once + +namespace ehs +{ + enum class Dock + { + NONE, + FILL + }; +} \ No newline at end of file diff --git a/include/ehs/EHS.h b/include/ehs/EHS.h new file mode 100644 index 0000000..0f5ecce --- /dev/null +++ b/include/ehs/EHS.h @@ -0,0 +1,63 @@ +#pragma once + +#if defined(NDEBUG) + #define EHS_RELEASE +#else + #define EHS_DEBUG +#endif + +#include "Types.h" +#include "ehs/system/OS.h" +#include "Version.h" +#include "Str.h" + +namespace ehs +{ + enum class MemoryPattern + { + SPEED, + SIZE + }; + + /// Retrieves the UTF32 C-style string as "Event Horizon Standard" + /// @returns The result. + const Char_32* GetName_32(); + + /// Retrieves the UTF16 C-style string as "Event Horizon Standard" + /// @returns The result. + const Char_16* GetName_16(); + + /// Retrieves the UTF8 C-style string as "Event Horizon Standard" + /// @returns The result. + const Char_8* GetName_8(); + + Str_8 GetAppName_8(); + + const Char_32* GetAcronym_32(); + + const Char_16* GetAcronym_16(); + + const Char_8* GetAcronym_8(); + + /// Retrieves the version identifier in UTF32. + /// @returns The result. + const Char_32* GetVersionId_32(); + + /// Retrieves the version identifier in UTF16. + /// @returns The result. + const Char_16* GetVersionId_16(); + + /// Retrieves the version identifier in UTF8. + /// @returns The result. + const Char_8* GetVersionId_8(); + + Str_8 GetAppVersionId_8(); + + /// Retrieves the current Event Horizon Standard version. + /// @returns The result. + Version GetVersion(); + + Version GetAppVersion(); +}; + +extern ehs::SInt_32 Main(ehs::Str_8* appName, ehs::Str_8* appVerId, ehs::Version* appVer); \ No newline at end of file diff --git a/include/ehs/GarbageCollector.h b/include/ehs/GarbageCollector.h new file mode 100644 index 0000000..0b97777 --- /dev/null +++ b/include/ehs/GarbageCollector.h @@ -0,0 +1,63 @@ +#pragma once + +#include "EHS.h" +#include "Vector.h" +#include "BaseObj.h" +#include "ehs/system/Mutex.h" +#include "ehs/system/Thread.h" + +namespace ehs +{ + typedef bool (*GcLogic)(BaseObj*); + + class GarbageCollector + { + private: + static Vector* logic; + static Vector garbage; + static UInt_64 max; + static Thread thread; + static Mutex mutex; + static bool running; + + static bool Has(const BaseObj* obj); + + public: + static void Start(); + + static void Stop(); + + /// Adds an object to the garbage pile to be deleted. + /// @param[in] obj The object to be deleted. + static void Add(BaseObj* obj); + + /// Sets the maximum amount of garbage to delete per poll. + /// @param[in] newMax The new maximum. + static void SetMax(const UInt_64 newMax); + + /// Gets the maximum amount of garbage to delete per poll. + /// @returns The maximum. + static UInt_64 GetMax(); + + /// Sets a new amount for memory pre-allocation to save on memory operations. + /// @param[in] newStride The stride to pre-allocate. + static void SetStride(const UInt_64 newStride); + + /// The amount of data pre-allocated to save on memory operations. + /// @returns The stride. + static UInt_64 GetStride(); + + /// Gets the garbage count. + /// @returns Garbage count. + static UInt_64 Size(); + + /// Used to delete objects over time. + static void Poll(); + + /// Deletes all of the data at once. + /// @warning Use Poll instead to prevent stutter. + static void Dump(); + + static bool IsRunning(); + }; +} diff --git a/include/ehs/HRNG.h b/include/ehs/HRNG.h new file mode 100644 index 0000000..f95e38a --- /dev/null +++ b/include/ehs/HRNG.h @@ -0,0 +1,59 @@ +#pragma once + +#include "EHS.h" +#include "Types.h" + +namespace ehs +{ + class HRNG + { + public: + static UInt_64 GenerateSeed_u64(); + + static UInt_64 Generate_u64(const UInt_64 min, const UInt_64 max); + + static UInt_64 Generate_u64(); + + static SInt_64 GenerateSeed_s64(); + + static SInt_64 Generate_s64(const SInt_64 min, const SInt_64 max); + + static SInt_64 Generate_s64(); + + static UInt_32 GenerateSeed_u32(); + + static UInt_32 Generate_u32(const UInt_32 min, const UInt_32 max); + + static UInt_32 Generate_u32(); + + static SInt_32 GenerateSeed_s32(); + + static SInt_32 Generate_s32(const SInt_32 min, const SInt_32 max); + + static SInt_32 Generate_s32(); + + static UInt_32 GenerateSeed_u16(); + + static UInt_16 Generate_u16(const UInt_16 min, const UInt_16 max); + + static UInt_16 Generate_u16(); + + static SInt_16 GenerateSeed_s16(); + + static SInt_16 Generate_s16(const SInt_16 min, const SInt_16 max); + + static SInt_16 Generate_s16(); + + static UInt_8 GenerateSeed_u8(); + + static UInt_8 Generate_u8(const UInt_8 min, const UInt_8 max); + + static UInt_8 Generate_u8(); + + static SInt_8 GenerateSeed_s8(); + + static SInt_8 Generate_s8(const SInt_8 min, const SInt_8 max); + + static SInt_8 Generate_s8(); + }; +} \ No newline at end of file diff --git a/include/ehs/HashMap.h b/include/ehs/HashMap.h new file mode 100644 index 0000000..d694186 --- /dev/null +++ b/include/ehs/HashMap.h @@ -0,0 +1,146 @@ +/* +#pragma once + +#include "EHS.h" + +#include + +namespace ehs +{ + template + class HashNode + { + private: + V value; + HashNode child; + + public: + HashNode() + { + } + + HashNode(const V value) + : value(value) + { + } + + HashNode(const HashNode& node) + : value(node.value), child(node.child) + { + } + + HashNode& operator==(const HashNode& node) + { + value = node.value; + child = node.child; + + return *this; + } + + void SetValue(const V value) + { + this->value = value; + } + + V GetValue() const + { + return value; + } + + void SetChild(const HashNode child) + { + this->child = child; + } + + HashNode* GetChild() + { + return &child; + } + }; + + /// 1000 + template + class HashMap + { + private: + HashNode** data; + N size; + + public: + ~HashMap() + { + if (data) + { + delete[] data; + data = nullptr; + size = 0; + } + } + + HashMap() + : data(nullptr), size(0) + { + } + + HashMap(const N size) + : data(new HashNode(size)), size(size) + { + } + + HashMap(const HashMap& map) + : data(new HashNode*(map.size)), size(map.size) + { + for (N i = 0; i < map.size; ++i) + data[i] = map.data[i]; + } + + HashMap& operator=(const HashMap& map) + { + if (this == &map) + return *this; + + data = new HashNode*(map.size); + size = map.size; + + for (N i = 0; i < size; ++i) + data[i] = map.data[i]; + + return *this; + } + + template + void Insert(const K key, const V value) + { + + } + + template<> + void Insert(const Str_8& key, const V value) + { + N hash = 0; + + for (N i = 0; i < key.Size(); ++i) + hash += key[i]; + + hash %= size; + + if (data[hash]) + { + HashNode child = data[hash]->GetChild(); + if (child) + + } + else + { + data[hash] = new HashNode(value); + } + } + + private: + SetChildRecursive() + { + + } + }; +} +*/ \ No newline at end of file diff --git a/include/ehs/Link.h b/include/ehs/Link.h new file mode 100644 index 0000000..4910d40 --- /dev/null +++ b/include/ehs/Link.h @@ -0,0 +1,70 @@ +#pragma once + +namespace ehs +{ + /// Test + template + class Link + { + public: + T value; + Link *child; + + ~Link() + { + delete child; + } + + Link() + : value(), child(nullptr) + { + } + + Link(const T value, Link* child) + : value(value), child(child) + { + } + + Link(const T value) + : value(value), child(nullptr) + { + } + + Link(const Link& link) + : value(link.value), child(nullptr) + { + } + + Link(Link&& link) noexcept + : value(link.value), child(link.child) + { + link.value = 0; + link.child = nullptr; + } + + Link& operator=(const Link& link) + { + if (this == &link) + return *this; + + value = link.value; + child = nullptr; + + return *this; + } + + Link& operator=(Link&& link) noexcept + { + if (this == &link) + return *this; + + value = link.value; + child = link.child; + + link.value = 0; + link.child = nullptr; + + return *this; + } + }; +} diff --git a/include/ehs/LinkedList.h b/include/ehs/LinkedList.h new file mode 100644 index 0000000..ae20b73 --- /dev/null +++ b/include/ehs/LinkedList.h @@ -0,0 +1,240 @@ +#pragma once + +#include "EHS.h" +#include "Log.h" +#include "Link.h" + +namespace ehs +{ + template + class LinkedList + { + private: + Link* start; + Link* end; + N size; + + public: + ~LinkedList() + { + delete start; + } + + LinkedList() + : size(0), start(nullptr), end(nullptr) + { + } + + LinkedList(const LinkedList& list) + : start(nullptr), end(nullptr), size(list.size) + { + const Link* rLast = list.start; + Link* last = new Link(rLast->value); + start = last; + + while (rLast->child) + { + last->child = new Link(rLast->child->value); + last = last->child; + rLast = rLast->child; + } + + end = last; + } + + LinkedList(LinkedList&& list) noexcept + : start(list.start), end(list.end), size(list.size) + { + list.start = nullptr; + list.end = nullptr; + list.size = {}; + } + + LinkedList& operator=(const LinkedList& list) + { + if (this == &list) + return *this; + + const Link* rLast = list.start; + Link* last = new Link(rLast->value); + start = last; + + while (rLast->child) + { + last->child = new Link(rLast->child->value); + last = last->child; + rLast = rLast->child; + } + + end = last; + size = list.size; + + return *this; + } + + LinkedList& operator=(LinkedList&& list) noexcept + { + if (this == &list) + return *this; + + start = list.start; + end = list.end; + size = list.size; + + list.start = nullptr; + list.end = nullptr; + list.size = {}; + + return *this; + } + + const Link* operator[](const N index) const + { + const Link* result = start; + + for (N i = 0; i != index; ++i) + result = result->child; + + return result; + } + + Link* operator[](const N index) + { + Link* result = start; + + for (N i = 0; i != index; ++i) + result = result->child; + + return result; + } + + T& Insert(const N index, const T value) + { + if (index && index == size - 1) + { + end->child = new Link(value); + end = end->child; + ++size; + return end->value; + } + else if (index) + { + Link* hierarchy = start; + for (N i = 0; i != index - 1; ++i) + hierarchy = hierarchy->child; + + hierarchy->child = new Link(value, hierarchy->child); + ++size; + return hierarchy->child->value; + } + else + { + start = new Link(value, start); + ++size; + return start->value; + } + } + + T Remove(const N index) + { + if (index && index == size - 1) + { + Link* hierarchy = start; + while (hierarchy->child->child) + hierarchy = hierarchy->child; + + T result = end->value; + delete end; + end = hierarchy; + --size; + return result; + } + else if (index) + { + Link* hierarchy = start; + for (N i = 0; i != index - 1; ++i) + hierarchy = hierarchy->child; + + Link* tmp = hierarchy->child; + T result = tmp->value; + hierarchy->child = hierarchy->child->child; + tmp->child = nullptr; + delete tmp; + return result; + } + else + { + Link* tmp = start; + T result = tmp->value; + start = start->child; + + if (--size) + tmp->child = nullptr; + else + end = nullptr; + + delete tmp; + + return result; + } + } + + T& Push(const T value) + { + if (size) + { + end->child = new Link(value); + end = end->child; + ++size; + return end->value; + } + else + { + start = new Link(value); + end = start; + size = 1; + return start->value; + } + } + + T Pop() + { + if (size == 1) + { + T result = start->value; + delete start; + start = nullptr; + end = nullptr; + size = 0; + return result; + } + if (size > 1) + { + Link* hierarchy = start; + while (hierarchy->child->child) + hierarchy = hierarchy->child; + + T result = hierarchy->child->value; + delete hierarchy->child; + hierarchy->child = nullptr; + end = hierarchy; + return result; + } + else + return {}; + } + + void Clear() + { + delete start; + start = nullptr; + end = nullptr; + size = 0; + } + + N Size() const + { + return size; + } + }; +} \ No newline at end of file diff --git a/include/ehs/Log.h b/include/ehs/Log.h new file mode 100644 index 0000000..a33620f --- /dev/null +++ b/include/ehs/Log.h @@ -0,0 +1,116 @@ +#pragma once + +#include + +#include "Types.h" +#include "Array.h" +#include "UTF.h" +#include "Str.h" + +namespace ehs +{ + /// A helper class for holding error information and handling them. + /// @tparam T The character data type to use. + /// @tparam N The number data type to use. + class Log + { + private: + static void (*logCb)(const Log&); + static Log lastLog; + Array tags; + UInt_64 code; + Str_8 msg; + + public: + static void SetCallback(void (*newLogCb)(const Log&)); + + static void Raise(const Log& log); + + /// Retrieves the last log raised. + static Log GetLastLog(); + + /// Default members initialization. + Log(); + + /// Initializes members with the given information. + /// @param [in] tags The tags to associate this log with. + /// @param [in] code The unique code to use. + /// @param [in] msg Detailed information about what happened. + Log(std::initializer_list tags, const UInt_64 code, const Str_8& msg); + + /// Initializes members with the given information. + /// @param [in] tags The tags to associate this log with. + /// @param [in] code The unique code to use. + /// @param [in] msg Detailed information about what happened. + Log(Array& tags, const UInt_64 code, const Str_8& msg); + + /// Copies all members from the given log. + /// @param [in] log The log to copy from. + Log(const Log& log); + + /// Copies all members from the given log. + /// @param [in] log The log to copy from. + /// @returns The log that has been assigned to. + Log& operator=(const Log& log); + + /* + /// Compares with another given log. + /// @param [in] log The log to compare with. + /// @returns Whether or not they are equal. + bool operator==(const Log log); + + /// Compares with another given log. + /// @param [in] log The log to compare with. + /// @returns Whether or not they are equal. + bool operator!=(const Log log); + */ + + /// Checks whether or not this log has the given tags. + /// @param [in] tags The tags to look for. + /// @returns True if all tags were found, otherwise false. + bool HasTags(const std::initializer_list tags) const; + + /// Checks whether or not this log has the given tags. + /// @param [in] tags The tags to look for. + /// @returns True if all tags were found, otherwise false. + bool HasTags(const Array& tags) const; + + /// Checks whether or not this log has the given tag. + /// @param [in] tag The tag to look for. + /// @returns True if tag was found, otherwise false. + bool HasTag(const Str_8& tag) const; + + /// Retrieves all the tags. + /// @returns The result. + Array GetTags() const; + + UInt_64 GetCode() const; + + /// Retrieves the detailed error message string. + /// @returns The error message. + Str_8 GetMsg() const; + + Str_8 ToStr() const; + + /// Retrieves whether or not this is a valid object. + /// @returns The result. + /// @note To be a valid object it must have one or more tags and a message size greater than zero. + bool IsValid() const; + }; +} + +#ifndef EHS_LOG_INT + #ifdef EHS_DEBUG + #define EHS_LOG_INT(type, code, msg) Log::Raise({{type, GetAcronym_8(), EHS_FILE, EHS_FUNC, Str_8::FromNum((UInt_32)EHS_LINE)}, code, msg}) + #else + #define EHS_LOG_INT(type, code, msg) Log::Raise({{type, GetAcronym_8(), EHS_FUNC}, code, msg}) + #endif +#endif + +#ifndef EHS_LOG + #ifdef EHS_DEBUG + #define EHS_LOG(type, code, msg) ehs::Log::Raise({{type, ehs::GetAppName_8(), EHS_FILE, EHS_FUNC, ehs::Str_8::FromNum((ehs::UInt_32)EHS_LINE)}, code, msg}) + #else + #define EHS_LOG(type, code, msg) ehs::Log::Raise({{type, ehs::GetAppName_8(), EHS_FUNC}, code, msg}) + #endif +#endif \ No newline at end of file diff --git a/include/ehs/Mat2.h b/include/ehs/Mat2.h new file mode 100644 index 0000000..f98ce40 --- /dev/null +++ b/include/ehs/Mat2.h @@ -0,0 +1,217 @@ +#pragma once + +#include "EHS.h" +#include "Vec2.h" + +namespace ehs +{ + template + class Mat2 + { + private: + T data[4]; + + public: + Mat2() + { + for (UInt_8 i = 0; i < 4; ++i) + data[i] = 0; + } + + template + Mat2(const Mat2& mat) + { + for (UInt_8 i = 0; i < 4; ++i) + data[i] = mat.data[i]; + } + + template + Mat2& operator=(const Mat2& mat) + { + if (this == &mat) + return *this; + + for (UInt_8 i = 0; i < 4; ++i) + data[i] = mat.data[i]; + + return *this; + } + + operator const T*() const + { + return data; + } + + operator T*() + { + return data; + } + + Vec2 operator*(Vec2 vec) const + { + Vec2 result; + result.x = vec.x * data[0] + vec.y * data[2]; + result.y = vec.x * data[1] + vec.y * data[3]; + + return result; + } + + Mat2& operator*=(const T scalar) + { + for (UInt_8 i = 0; i < 4; ++i) + data[i] *= scalar; + + return *this; + } + + Mat2 operator*(const T scalar) const + { + Mat2 result; + for (UInt_8 i = 0; i < 4; ++i) + result.data[i] = data[i] * scalar; + + return result; + } + + Mat2& operator*=(const Mat2& mat) + { + Mat2 old = *this; + for (UInt_8 i = 0; i < 4; ++i) + { + UInt_8 row = i / 2; + UInt_8 column = i % 2; + data[i] = 0; + data[i] += old.data[0 * 2 + column] * mat.data[row * 2 + 0]; + data[i] += old.data[1 * 2 + column] * mat.data[row * 2 + 1]; + } + + return *this; + } + + Mat2 operator*(const Mat2& mat) const + { + Mat2 result; + for (UInt_8 i = 0; i < 4; ++i) + { + UInt_8 row = i / 2; + UInt_8 column = i % 2; + result.data[i] += data[0 * 2 + column] * mat.data[row * 2 + 0]; + result.data[i] += data[1 * 2 + column] * mat.data[row * 2 + 1]; + } + + return result; + } + + Mat2 GetTranspose() const + { + Mat2 result; + for (UInt_8 i = 0; i < 4; ++i) + result.data[i] = data[2 * (i % 2) + i / 2]; + + return result; + } + + void Transpose() + { + Mat2 old = *this; + for (UInt_8 i = 0; i < 4; ++i) + data[i] = old.data[2 * (i % 2) + i / 2]; + } + + Mat2 GetMinor() const + { + Mat2 result(0); + result.data[0] = data[3]; + result.data[1] = data[2]; + result.data[2] = data[1]; + result.data[3] = data[0]; + return result; + } + + void Minor() + { + Mat2 old = *this; + data[0] = old.data[3]; + data[1] = old.data[2]; + data[2] = old.data[1]; + data[3] = old.data[0]; + } + + T GetDeterminant() const + { + return data[0] * data[3] - data[1] * data[2]; + } + + Mat2 GetCofactor() const + { + Mat2 minor = GetMinor(); + Mat2 result; + + for (UInt_8 r = 0; r < 2; ++r) + { + for (UInt_8 c = 0; c < 2; ++c) + { + UInt_8 i = 2 * c + r; + result.data[i] = minor.data[i] * Math::Pow(-1, r + c); + } + } + + return result; + } + + void Cofactor() + { + Mat2 minor = GetMinor(); + + for (UInt_8 r = 0; r < 2; ++r) + { + for (UInt_8 c = 0; c < 2; ++c) + { + UInt_8 i = 2 * c + r; + data[i] = minor.data[i] * Math::Pow(-1, r + c); + } + } + } + + Mat2 GetAdjugate() const + { + return GetCofactor().GetTranspose(); + } + + void Adjugate() + { + Cofactor(); + Transpose(); + } + + Mat2 GetInverse() const + { + T det = GetDeterminant(); + if (Math::ComCmp(det, 0.0f)) + return {}; + + return GetAdjugate() * (1 / det); + } + + void Inverse() + { + T det = GetDeterminant(); + if (Math::ComCmp(det, 0.0f)) + return; + + Adjugate(); + operator*=(1 / det); + } + + static Mat2 Identity() + { + Mat2 result; + result[0] = 1; + result[3] = 1; + return result; + } + }; + + typedef Mat2 Mat2_f; + typedef Mat2 Mat2_d; +} \ No newline at end of file diff --git a/include/ehs/Mat3.h b/include/ehs/Mat3.h new file mode 100644 index 0000000..74d637b --- /dev/null +++ b/include/ehs/Mat3.h @@ -0,0 +1,322 @@ +#pragma once + +#include "EHS.h" +#include "Vec3.h" +#include "Mat2.h" + +namespace ehs +{ + template + class Mat3 + { + private: + T data[9]; + + public: + Mat3() + { + for (UInt_8 i = 0; i < 9; ++i) + data[i] = 0; + } + + template + Mat3(const Mat2& mat) + { + for (UInt_8 i = 0; i < 4; ++i) + data[i / 2 * 4 + i % 2] = mat.data[i]; + } + + template + Mat3(const Mat3& mat) + { + for (UInt_8 i = 0; i < 9; ++i) + data[i] = mat.data[i]; + } + + template + Mat3& operator=(const Mat3& mat) + { + if (this == &mat) + return *this; + + for (UInt_8 i = 0; i < 9; ++i) + data[i] = mat.data[i]; + + return *this; + } + + operator Mat2() const + { + Mat2 result; + + for (UInt_8 i = 0; i < 4; ++i) + result.data[i] = data[i / 2 * 4 + i % 2]; + + return result; + } + + operator const T*() const + { + return data; + } + + operator T*() + { + return data; + } + + Vec3 operator*(Vec3 vec) const + { + Vec3 result; + result.x = vec.x * data[0] + vec.y * data[3] + vec.z * data[6]; + result.y = vec.x * data[1] + vec.y * data[4] + vec.z * data[7]; + result.z = vec.x * data[2] + vec.y * data[5] + vec.z * data[8]; + + return result; + } + + Mat3& operator*=(const T scalar) + { + for (UInt_8 i = 0; i < 9; ++i) + data[i] *= scalar; + + return *this; + } + + Mat3 operator*(const T scalar) const + { + Mat3 result; + for (UInt_8 i = 0; i < 9; ++i) + result.data[i] = data[i] * scalar; + + return result; + } + + Mat3& operator*=(const Mat3& mat) + { + Mat3 old = *this; + for (UInt_8 i = 0; i < 9; ++i) + { + UInt_8 row = i / 3; + UInt_8 column = i % 3; + data[i] = 0; + data[i] += old.data[0 * 3 + column] * mat.data[row * 3 + 0]; + data[i] += old.data[1 * 3 + column] * mat.data[row * 3 + 1]; + data[i] += old.data[2 * 3 + column] * mat.data[row * 3 + 2]; + } + + return *this; + } + + Mat3 operator*(const Mat3& mat) const + { + Mat3 result; + for (UInt_8 i = 0; i < 9; ++i) + { + UInt_8 row = i / 3; + UInt_8 column = i % 3; + result.data[i] += data[0 * 3 + column] * mat.data[row * 3 + 0]; + result.data[i] += data[1 * 3 + column] * mat.data[row * 3 + 1]; + result.data[i] += data[2 * 3 + column] * mat.data[row * 3 + 2]; + } + + return result; + } + + Mat3 GetTranspose() const + { + Mat3 result; + for (UInt_8 i = 0; i < 9; ++i) + result.data[i] = data[3 * (i % 3) + i / 3]; + + return result; + } + + void Transpose() + { + Mat3 old = *this; + for (UInt_8 i = 0; i < 9; ++i) + data[i] = old.data[3 * (i % 3) + i / 3]; + } + + Mat3 GetMinor() const + { + Mat3 result; + + for (UInt_8 r = 0; r < 3; ++r) + for (UInt_8 c = 0; c < 3; ++c) + result[3 * r + c] = Cut(r, c).GetDeterminant(); + + return result; + } + + void Minor() + { + Mat3 old = *this; + + for (UInt_8 r = 0; r < 3; ++r) + for (UInt_8 c = 0; c < 3; ++c) + data[3 * r + c] = old.Cut(r, c).GetDeterminant(); + } + + Mat2 Cut(const UInt_8 row, const UInt_8 column) const + { + Mat2 result; + UInt_8 index = 0; + + for (UInt_8 r = 0; r < 3; ++r) + { + for (UInt_8 c = 0; c < 3; ++c) + { + if (r == row || c == column) + continue; + + result[index++] = data[3 * r + c]; + } + } + + return result; + } + + T GetDeterminant() const + { + Mat3 cofactor = GetCofactor(); + T result = 0; + + for (UInt_8 c = 0; c < 3; ++c) + result += data[c] * cofactor[c]; + + return result; + } + + Mat3 GetCofactor() const + { + Mat3 minor = GetMinor(); + Mat3 result; + + for (UInt_8 r = 0; r < 3; ++r) + { + for (UInt_8 c = 0; c < 3; ++c) + { + UInt_8 i = 3 * c + r; + result.data[i] = minor.data[i] * Math::Pow(-1, r + c); + } + } + + return result; + } + + void Cofactor() + { + Mat3 minor = GetMinor(); + + for (UInt_8 r = 0; r < 3; ++r) + { + for (UInt_8 c = 0; c < 3; ++c) + { + UInt_8 i = 3 * c + r; + data[i] = minor.data[i] * Math::Pow(-1, r + c); + } + } + } + + Mat3 GetAdjugate() const + { + return GetCofactor().GetTranspose(); + } + + void Adjugate() + { + Cofactor(); + Transpose(); + } + + Mat3 GetInverse() const + { + T det = GetDeterminant(); + if (Math::ComCmp(det, 0.0f)) + return {}; + + return GetAdjugate() * (1 / det); + } + + void Inverse() + { + T det = GetDeterminant(); + if (Math::ComCmp(det, 0.0f)) + return; + + Adjugate(); + operator*=(1 / det); + } + + static Mat3 Identity() + { + Mat3 result; + result.data[0] = 1; + result.data[4] = 1; + result.data[8] = 1; + return result; + } + + static Mat3 Scale(const Vec3& scale) + { + Mat3 result; + result.data[0] = scale.x; + result.data[4] = scale.y; + result.data[8] = scale.z; + + return result; + } + + static Mat3 PitchRotate(const T angle) + { + T radians = Math::Rads(angle); + + Mat3 result; + result.data[0] = 1; + result.data[4] = Math::Cos(radians); + result.data[5] = Math::Sin(radians); + result.data[7] = -Math::Sin(radians); + result.data[8] = Math::Cos(radians); + + return result; + } + + static Mat3 YawRotate(const T angle) + { + T radians = Math::Rads(angle); + + Mat3 result; + result.data[0] = Math::Cos(radians); + result.data[2] = -Math::Sin(radians); + result.data[4] = 1; + result.data[6] = Math::Sin(radians); + result.data[8] = Math::Cos(radians); + + return result; + } + + static Mat3 RollRotate(const T angle) + { + T radians = Math::Rads(angle); + + Mat3 result; + result.data[0] = Math::Cos(radians); + result.data[1] = Math::Sin(radians); + result.data[3] = -Math::Sin(radians); + result.data[4] = Math::Cos(radians); + result.data[8] = 1; + + return result; + } + + static Mat3 Rotate(const Vec3& vec) + { + return YawRotate(vec.y) * RollRotate(vec.z) * PitchRotate(vec.x); + } + }; + + typedef Mat3 Mat3_f; + typedef Mat3 Mat3_d; +} \ No newline at end of file diff --git a/include/ehs/Mat4.h b/include/ehs/Mat4.h new file mode 100644 index 0000000..e935215 --- /dev/null +++ b/include/ehs/Mat4.h @@ -0,0 +1,426 @@ +#pragma once + +#include "EHS.h" +#include "Math.h" +#include "Mat3.h" +#include "Vec4.h" +#include "Vec3.h" + +namespace ehs +{ + template + class Mat4 + { + private: + friend class GpuUniform; + + T data[16]; + + public: + Mat4() + { + for (UInt_8 i = 0; i < 16; ++i) + data[i] = 0; + } + + explicit Mat4(const T* data) + { + for (UInt_8 i = 0; i < 16; ++i) + this->data[i] = data[i]; + } + + template + Mat4(const Mat3& mat) + { + for (UInt_8 i = 0; i < 9; ++i) + { + UInt_8 row = i / 3; + UInt_8 column = i % 3; + UInt_8 dst = row * 4 + column; + + data[dst] = (T)mat[i]; + } + + data[3] = 0; + data[7] = 0; + data[11] = 0; + data[12] = 0; + data[13] = 0; + data[14] = 0; + data[15] = 1; + } + + template + Mat4(const Mat4& mat) + { + for (UInt_8 i = 0; i < 16; ++i) + data[i] = (T)mat.data[i]; + } + + template + Mat4& operator=(const Mat4& mat) + { + if (this == &mat) + return *this; + + for (UInt_8 i = 0; i < 16; ++i) + data[i] = (T)mat.data[i]; + + return *this; + } + + Vec4 operator*(Vec4 vec) const + { + Vec4 result; + result.x = vec.x * data[0] + vec.y * data[4] + vec.z * data[8] + vec.w * data[12]; + result.y = vec.x * data[1] + vec.y * data[5] + vec.z * data[9] + vec.w * data[13]; + result.z = vec.x * data[2] + vec.y * data[6] + vec.z * data[10] + vec.w * data[14]; + result.w = vec.x * data[3] + vec.y * data[7] + vec.z * data[11] + vec.w * data[15]; + + return result; + } + + Mat4& operator*=(const T scalar) + { + for (UInt_8 i = 0; i < 16; ++i) + data[i] *= scalar; + + return *this; + } + + Mat4 operator*(const T scalar) const + { + Mat4 result; + for (UInt_8 i = 0; i < 16; ++i) + result.data[i] = data[i] * scalar; + + return result; + } + + Mat4& operator*=(const Mat4& mat) + { + Mat4 transposed = GetTranspose(); + + for (UInt_8 i = 0; i < 16; i++) + { + UInt_8 row = i / 4 * 4; + UInt_8 column = i % 4 * 4; + data[i] += transposed.data[column] * mat.data[row]; + data[i] += transposed.data[column + 1] * mat.data[row + 1]; + data[i] += transposed.data[column + 2] * mat.data[row + 2]; + data[i] += transposed.data[column + 3] * mat.data[row + 3]; + } + + return *this; + } + + Mat4 operator*(const Mat4& mat) const + { + Mat4 transposed = GetTranspose(); + + Mat4 result; + for (UInt_8 i = 0; i < 16; i++) + { + UInt_8 row = i / 4 * 4; + UInt_8 column = i % 4 * 4; + result.data[i] += transposed.data[column] * mat.data[row]; + result.data[i] += transposed.data[column + 1] * mat.data[row + 1]; + result.data[i] += transposed.data[column + 2] * mat.data[row + 2]; + result.data[i] += transposed.data[column + 3] * mat.data[row + 3]; + } + + return result; + } + + operator const T*() const + { + return data; + } + + operator T*() + { + return data; + } + + Vec3 GetRight() const + { + return Vec3(data[0], data[4], data[8]); + } + + Vec3 GetUp() const + { + return Vec3(data[1], data[5], data[9]); + } + + Vec3 GetForward() const + { + return Vec3(data[2], data[6], data[10]); + } + + Mat4 GetTranspose() const + { + Mat4 result; + for (UInt_8 i = 0; i < 16; ++i) + result.data[i] = data[i % 4 * 4 + i / 4]; + + return result; + } + + void Transpose() + { + Mat4 old = *this; + for (UInt_8 i = 0; i < 16; ++i) + data[i] = old.data[4 * (i % 4) + i / 4]; + } + + Mat3 Cut(const UInt_8 row, const UInt_8 column) const + { + Mat3 result; + UInt_8 index = 0; + + for (UInt_8 r = 0; r < 4; ++r) + { + for (UInt_8 c = 0; c < 4; ++c) + { + if (r == row || c == column) + continue; + + result[index++] = data[4 * r + c]; + } + } + + return result; + } + + Mat4 GetMinor() const + { + Mat4 result; + + for (UInt_8 r = 0; r < 4; ++r) + for (UInt_8 c = 0; c < 4; ++c) + result.data[4 * r + c] = Cut(r, c).GetDeterminant(); + + return result; + } + + void Minor() + { + Mat4 old = *this; + + for (UInt_8 r = 0; r < 4; ++r) + for (UInt_8 c = 0; c < 4; ++c) + data[4 * r + c] = old.Cut(r, c).GetDeterminant(); + } + + Mat4 GetCofactor() const + { + Mat4 minor = GetMinor(); + Mat4 result; + + for (UInt_8 r = 0; r < 4; ++r) + { + for (UInt_8 c = 0; c < 4; ++c) + { + UInt_8 i = 4 * c + r; + result.data[i] = minor.data[i] * Math::Pow(-1, r + c); + } + } + + return result; + } + + void Cofactor() + { + Mat4 minor = GetMinor(); + + for (UInt_8 r = 0; r < 4; ++r) + { + for (UInt_8 c = 0; c < 4; ++c) + { + UInt_8 i = 4 * c + r; + data[i] = minor.data[i] * Math::Pow(-1, r + c); + } + } + } + + T GetDeterminant() const + { + Mat4 cofactor = GetCofactor(); + T result = 0; + + for (UInt_8 c = 0; c < 4; ++c) + result += data[c] * cofactor[c]; + + return result; + } + + Mat4 GetAdjugate() const + { + return GetCofactor().GetTranspose(); + } + + void Adjugate() + { + Cofactor(); + Transpose(); + } + + Mat4 GetInverse() const + { + T det = GetDeterminant(); + if (Math::ComCmp(det, 0.0f)) + return {}; + + return GetAdjugate() * (1 / det); + } + + void Inverse() + { + T det = GetDeterminant(); + if (Math::ComCmp(det, 0.0f)) + return; + + Adjugate(); + operator*=(1 / det); + } + + static Mat4 Identity() + { + Mat4 result; + result.data[0] = 1; + result.data[5] = 1; + result.data[10] = 1; + result.data[15] = 1; + return result; + } + + static Mat4 Scale(const Vec3& scale) + { + Mat4 result; + result.data[0] = scale.x; + result.data[5] = scale.y; + result.data[10] = scale.z; + result.data[15] = 1; + + return result; + } + + static Mat4 Translate(const Vec3& pos) + { + Mat4 result = Identity(); + result.data[12] = pos.x; + result.data[13] = pos.y; + result.data[14] = pos.z; + + return result; + } + + static Mat4 PitchRotate(const T angle) + { + T radians = Math::Rads(angle); + + Mat4 result; + result.data[0] = 1; + result.data[5] = Math::Cos(radians); + result.data[6] = Math::Sin(radians); + result.data[9] = -Math::Sin(radians); + result.data[10] = Math::Cos(radians); + result.data[15] = 1; + + return result; + } + + static Mat4 YawRotate(const T angle) + { + T radians = Math::Rads(angle); + + Mat4 result; + result.data[0] = Math::Cos(radians); + result.data[2] = -Math::Sin(radians); + result.data[5] = 1; + result.data[8] = Math::Sin(radians); + result.data[10] = Math::Cos(radians); + result.data[15] = 1; + + return result; + } + + static Mat4 RollRotate(const T angle) + { + T radians = Math::Rads(angle); + + Mat4 result; + result.data[0] = Math::Cos(radians); + result.data[1] = Math::Sin(radians); + result.data[4] = -Math::Sin(radians); + result.data[5] = Math::Cos(radians); + result.data[10] = 1; + result.data[15] = 1; + + return result; + } + + static Mat4 Rotate(const Vec3& vec) + { + return YawRotate(vec.y) * RollRotate(vec.z) * PitchRotate(vec.x); + } + + static Mat4 RH_Perspective(const T fov, const T aspect, const T zNear, const T zFar) + { + const float tanHalfFovy = tan(Math::Rads(fov) / 2.0f); + + Mat4 result; + result[0] = 1.0f / (aspect * tanHalfFovy); + result[5] = -1.0f / tanHalfFovy; + result[10] = zFar / (zFar - zNear); + result[14] = -(zFar * zNear) / (zFar - zNear); + + return result; + } + + static Mat4 LH_Perspective(const T fov, const T aspect, const T zNear, const T zFar) + { + const float tanHalfFovy = Math::Tan(Math::Rads(fov) / 2.0f); + + Mat4 result; + result[0] = 1.0f / (aspect * tanHalfFovy); + result[5] = -1.0f / tanHalfFovy; + result[10] = zFar / (zFar - zNear); + result[11] = 1.0f; + result[14] = -(zFar * zNear) / (zFar - zNear); + result[15] = 0.0f; + + return result; + } + + static Mat4 LH_Orthographic(const T left, const T right, const T top, const T bottom, const T zNear, const T zFar) + { + Mat4 result; + result[0] = 2.0f / (right - left); // 0,0 entry + result[5] = 2.0f / (bottom - top); // 1,1 entry + result[10] = 1.0f / (zFar - zNear); // 2,2 entry + result[12] = -(right + left) / (right - left); // 3,0 entry + result[13] = -(bottom + top) / (bottom - top); // 3,1 entry + result[14] = -zNear / (zFar - zNear); // 3,2 entry + result[15] = 1.0f; // 3,3 entry + + return result; + + /* + Mat4 result; + result.data[0] = 2 / (right - left); + result.data[5] = 2 / (top - bottom); + result.data[10] = 1 / (zFar - zNear); + result.data[12] = (left + right) / (left - right); + result.data[13] = (top + bottom) / (bottom - top); + result.data[14] = zNear / (zNear - zFar); + result.data[15] = 1; + + return result; + */ + } + }; + + typedef Mat4 Mat4_f; + typedef Mat4 Mat4_d; +} \ No newline at end of file diff --git a/include/ehs/Math.h b/include/ehs/Math.h new file mode 100644 index 0000000..2f7bbbb --- /dev/null +++ b/include/ehs/Math.h @@ -0,0 +1,316 @@ +#pragma once + +#include "ehs/system/CPU.h" + +#define EHS_LOW_WORD(x) *((int*)&x) + 1 + +namespace ehs +{ + class Math + { + private: + static float Sqrt_AVX(const float from); + + static double Sqrt_AVX(const double from); + + static float Sqrt_SSE(const float from); + + static double Sqrt_SSE2(const double from); + + static float Sqrt_VFP4(const float from); + + static double Sqrt_VFP4(const double from); + + public: + constexpr static float fltEpsilon = 1e-7f; + constexpr static double dblEpsilon = 1e-16; + + /// Absolute tolerance comparison for single precision floats. + static bool AbsCmp(const float a, const float b); + + /// Absolute tolerance comparison for double precision floats. + static bool AbsCmp(const double a, const double b); + + /// Relative tolerance comparison for single precision floats. + static bool RelCmp(const float a, const float b); + + /// Relative tolerance comparison for double precision floats. + static bool RelCmp(const double a, const double b); + + /// Combined absolute and relative tolerance comparison for single precision floats. + static bool ComCmp(const float a, const float b); + + /// Combined absolute and relative tolerance comparison for double precision floats. + static bool ComCmp(const double a, const double b); + + template + static T Max(const T a, const T b) + { + return a > b ? a : b; + } + + template + static T Min(const T a, const T b) + { + return a < b ? a : b; + } + + template + static T Clamp(const T value, const T min, const T max) + { + if (value < min) + return min; + else if (value > max) + return max; + + return value; + } + + template + static T Abs(const T from) + { + return from < 0 ? -from : from; + } + + /// Retrieves a very accurate version of Pi as a long double and converts it. + /// @tparam T The data type to return Pi as. + /// @returns The result. + template + static constexpr T Pi() + { + return (T)3.141592653589793238462643383279502884L; + } + + /// Converts degrees into radians. + /// @tparam T The data type to return; + /// @param [in] from The value to convert to radians. + /// @returns The value in radians. + template + static T Rads(const T from) + { + return from * 0.01745329251994329576923690768489; + } + + /// Converts radians into degrees. + /// @tparam T The data type to return; + /// @param [in] from The value to convert to degrees. + /// @returns The value in degrees. + template + static T Degr(const T from) + { + return from * 57.295779513082320876798154814105; + } + + /// A method for use of exponents. + /// @tparam T The data type to return; + /// @tparam I The data type to use as the exponent. + /// @param [in] from The value to use the exponent on. + /// @param [in] of The exponent. + /// @returns The result. + template + static T Pow(const T from, const I of) + { + if (of < 0) + { + if (from == 0) + return -0; + + return 1 / (from * Pow(from, (-of) - 1)); + } + + if (of == 0) + return 1; + else if (of == 1) + return from; + + return from * Pow(from, of - 1); + } + + static float Near(const float from); + + static double Near(const double from); + + static float Floor(const float from); + + static double Floor(const double from); + + static float Ceil(const float from); + + static double Ceil(const double from); + + static float Trunc(const float from); + + static double Trunc(const double from); + + static float Mod(const float from, const float divisor); + + static double Mod(const double from, const double divisor); + + /// A method for retrieving the square root of a value. + /// @tparam T The data type to use. + /// @param [in] from The value to retrieve to square root of. + /// @returns The result. + template + static T Sqrt(const T from) + { + T temp = 0; + T result = from / 2; + + while (result != temp) + { + temp = result; + result = (from / temp + temp) / 2; + } + + return result; + } + + static double Sqrt(const double from); + + static float Sqrt(const float from); + + template + static R Sin(const R angle, const R precision = 0.001) + { + R sum = angle; + R term = angle; + + for (UInt_64 i = 1; Abs(term) >= precision; ++i) + { + term *= -angle * angle / (R)((2 * i + 1) * (2 * i)); + sum += term; + } + + return sum; + + /* + R sum = 0; + + for (USize n = 0; n < precision; ++n) + sum += Pow(-1, n) / (R)Fact(2 * n + 1) * Pow(angle, 2 * n + 1); + + return sum; + */ + } + + template + static R ASin(const R yPos, const T precision = 10) + { + R sum = 0; + + for (T n = 0; n < precision; ++n) + sum += (R)Fact(2 * n) / (Pow(4, n) * Pow((R)Fact(n), 2) * (2 * n + 1)) * Pow(yPos, 2 * n + 1); + + return sum; + } + + /// A trigonometry Cosine function for finding the X-Axis position from the Z-Axis angle. + /// @tparam R BaseInput and result data type. + /// @tparam T Precision data type. + /// @param[in] angle The angle in radians from the Z-Axis. + /// @param [in] precision Sigma max. + /// @returns The X-Axis position. + template + static R Cos(const R angle, const R precision = 0.001) + { + R sum = 1.0; + R term = 1.0; + + for (UInt_64 i = 2; Abs(term) >= precision; i += 2) + { + term *= -angle * angle / (R)(i * (i - 1)); + sum += term; + } + + return sum; + + /* + R sum = 0; + + for (T n = 0; n < precision; ++n) + sum += Pow(-1, n) / (R)Fact(2 * n) * Pow(angle, 2 * n); + + return sum; + */ + } + + /// A trigonometry Arc Cosine function for finding the Z-Axis angle form the X-Axis position. + /// @tparam R BaseInput and result data type. + /// @tparam T Precision data type. + /// @param [in] xPos The position from the X-Axis. + /// @param [in] precision Sigma max. + /// @returns The Z-Axis angle. + template + static R ACos(const R xPos, const T precision = 10) + { + return Pi() / 2 - ASin(xPos, precision); + } + + template + static R Tan(const R angle, const R precision = 0.001) + { + /* + R sum = 0; + + for (T n = 1; n < precision + 1; ++n) + sum += B(2 * n) * Pow(-4, n) * (1 - Pow(4, n)) / (R)Fact(2 * n) * Pow(angle, 2 * n - 1); + + return sum; + */ + return Sin(angle) / Cos(angle); + } + + template + static R ATan(const R x, const T precision = 1) + { + R sum = 0; + + for (T n = 0; n < precision; ++n) + sum += Pow(-1, n) / (2 * n + 1) * Pow(x, 2 * n + 1); + + return sum; + } + + template + static R Cot(const R x, const R precision = 0.001) + { + return 1 / Tan(x, precision); + } + + template + static T Fact(const T n) + { + if (n <= 1) + return 1; + + return n * Fact(n - 1); + } + + template + static R Combination(const T n, const T k) + { + if (k <= n) + return (R)Fact(n) / (R)(Fact(n - k) * Fact(k)); + + return 0; + } + + template + static R B(const T n) + { + R innerSum = 0; + R outerSum = 0; + + for (T k = 0; k <= n; ++k) + { + for (T r = 0; r <= k; ++r) + innerSum += Pow(-1, r) * Combination(k, r) * Pow(r, n); + + outerSum += 1 / ((R)k + 1) * innerSum; + innerSum = 0; + } + + return outerSum; + } + }; +} \ No newline at end of file diff --git a/include/ehs/PRNG.h b/include/ehs/PRNG.h new file mode 100644 index 0000000..55282a0 --- /dev/null +++ b/include/ehs/PRNG.h @@ -0,0 +1,60 @@ +#pragma once + +#include "EHS.h" + +namespace ehs +{ + template + class PRNG + { + private: + T seed; + T last; + + public: + PRNG() + : seed(0), last(0) + { + } + + PRNG(const T seed) + : seed(seed), last(seed) + { + } + + PRNG(const PRNG& prng) + : seed(prng.seed), last(prng.last) + { + } + + PRNG& operator=(const PRNG& prng) + { + if (this == &prng) + return *this; + + seed = prng.seed; + last = prng.last; + + return *this; + } + + T Generate(const T min, const T max) + { + return Generate() % (max - min) + min; + } + + T Generate() + { + return ((last = last * (T)214013 + (T)2531011) >> 16) & (T)0x7fff; + } + }; + + typedef PRNG PRNG_s64; + typedef PRNG PRNG_u64; + typedef PRNG PRNG_s32; + typedef PRNG PRNG_u32; + typedef PRNG PRNG_s16; + typedef PRNG PRNG_u16; + typedef PRNG PRNG_s8; + typedef PRNG PRNG_u8; +} \ No newline at end of file diff --git a/include/ehs/PtrData.h b/include/ehs/PtrData.h new file mode 100644 index 0000000..d692fe4 --- /dev/null +++ b/include/ehs/PtrData.h @@ -0,0 +1,18 @@ +#pragma once + +#include "DataType.h" + +namespace ehs +{ + struct PtrData + { + UInt_64 references; + void* data; + }; + + bool HasPtrData(void* data); + + void AddPtrData(void* data); + + bool RemovePtrData(void* data); +} \ No newline at end of file diff --git a/include/ehs/Quat.h b/include/ehs/Quat.h new file mode 100644 index 0000000..085a504 --- /dev/null +++ b/include/ehs/Quat.h @@ -0,0 +1,396 @@ +#pragma once + +#include "EHS.h" +#include "Math.h" +#include "Mat4.h" +#include "Vec3.h" + +namespace ehs +{ + template + class Quat + { + public: + T w; + T x; + T y; + T z; + + Quat() + : w(0), x(0), y(0), z(0) + { + } + + Quat(const T w, const T x, const T y, const T z) + : w(w), x(x), y(y), z(z) + { + } + + Quat(const T yaw, const T pitch, const T roll) + : w(0), x(0), y(0), z(0) + { + T c1 = cos(yaw / 2); + T c2 = cos(pitch / 2); + T c3 = cos(roll / 2); + T s1 = sin(yaw / 2); + T s2 = sin(pitch / 2); + T s3 = sin(roll / 2); + + w = c1 * c2 * c3 - s1 * s2 * s3; + x = s1 * s2 * c3 + c1 * c2 * s3; + y = s1 * c2 * c3 + c1 * s2 * s3; + z = c1 * s2 * c3 - s1 * c2 * s3; + } + + explicit Quat(const Vec3& euler) + : w(0), x(0), y(0), z(0) + { + T c1 = cos(euler.x / 2); + T c2 = cos(euler.y / 2); + T c3 = cos(euler.z / 2); + T s1 = sin(euler.x / 2); + T s2 = sin(euler.y / 2); + T s3 = sin(euler.z / 2); + + w = c1 * c2 * c3 - s1 * s2 * s3; + x = s1 * s2 * c3 + c1 * c2 * s3; + y = s1 * c2 * c3 + c1 * s2 * s3; + z = c1 * s2 * c3 - s1 * c2 * s3; + } + + Quat(const Vec3& n, const T a) + : w(cosf(a / 2)), x(n.x * sinf(a / 2)), y(n.y * sinf(a / 2)), z(n.z * sinf(a / 2)) + { + } + + explicit Quat(const Mat4& rotMatrix) + : w(0), x(0), y(0), z(0) + { + ToQuaternion(rotMatrix); + } + + Quat(const Quat& quat) + : w(quat.w), x(quat.x), y(quat.y), z(quat.z) + { + } + + explicit Quat(const T scalar) + : w(scalar), x(scalar), y(scalar), z(scalar) + { + } + + Quat& operator=(const Quat& quat) + { + if (this == &quat) + return *this; + + w = quat.w; + x = quat.x; + y = quat.y; + z = quat.z; + + return *this; + } + + Quat& operator=(const Mat4& rotMatrix) + { + ToQuaternion(rotMatrix); + + return *this; + } + + Quat operator+(const Quat& other) const + { + return {w + other.w, x + other.x, y + other.y, z + other.z}; + } + + Quat operator-() const + { + return {-w, -x, -y, -z}; + } + + Quat operator-(const Quat& other) const + { + return {w - other.w, x - other.x, y - other.y, z - other.z}; + } + + Quat operator*(const T scalar) + { + return {w * scalar, x * scalar, x * scalar, x * scalar}; + } + + Quat operator*(const Quat& other) + { + return Quat + ( + w * other.w - x * other.x - y * other.y - z * other.z, + w * other.x + x * other.w + y * other.z - z * other.y, + w * other.y - x * other.z + y * other.w + z * other.x, + w * other.z + x * other.y - y * other.x + z * other.w + ); + } + + Vec3 operator*(const Vec3& vect) + { + Quat tmp(0, vect[0], vect[1], vect[2]); + Vec3 tmpVect(x, y, z); + + Vec3 vcV = tmpVect.CrossProduct(vect); + return vect + vcV * (2 * w) + tmpVect.CrossProduct(vcV) * 2; + } + + Quat operator^(const T t) + { + Vec3 n; + T a; + + ToAxisAngle(&n, &a); + + float at = a * t; + + return Quat(n, at); + } + + bool operator==(const Quat& quat) const + { + return w == quat.w && x == quat.x && y == quat.y && z == quat.z; + } + + bool operator!=(const Quat& quat) const + { + return w != quat.w || x != quat.x || y != quat.y || z == quat.z; + } + + T operator[](const UInt_64 index) const + { + switch (index) + { + case 0: + return w; + case 1: + return x; + case 2: + return y; + case 3: + return z; + default: + return w; + } + } + + T& operator[](const UInt_64 index) + { + switch (index) + { + case 0: + return w; + case 1: + return x; + case 2: + return y; + case 3: + return z; + default: + return w; + } + } + + void ToAxisAngle(Vec3* vectAxis, T* flAngle) + { + Vec3 tmp(x, y, z); + + if (tmp.GetDis2() < 0.0001f) + *vectAxis = Vec3(1, 0, 0); + else + *vectAxis = tmp.GetNorm(); + + *flAngle = acosf(w) * 2; + *flAngle = Math::Degr(*flAngle); + } + + void ToQuaternion(const Mat4& rotMatrix) + { + T trace = rotMatrix[0][0] + rotMatrix[1][1] + rotMatrix[2][2]; + + if (trace > 0) + { + T s = 0.5f / Math::Sqrt(trace + 1.0f); + w = 0.25f / s; + + x = (rotMatrix[2][1] - rotMatrix[1][2]) * s; + y = (rotMatrix[0][2] - rotMatrix[2][0]) * s; + z = (rotMatrix[1][0] - rotMatrix[0][1]) * s; + } else + { + if ((rotMatrix[0][0] > rotMatrix[1][1]) && (rotMatrix[0][0] > rotMatrix[2][2])) + { + T s = 2.0f * Math::Sqrt(1.0f + rotMatrix[0][0] - rotMatrix[1][1] - rotMatrix[2][2]); + w = (rotMatrix[2][1] - rotMatrix[1][2]) / s; + x = 0.25f * s; + y = (rotMatrix[0][1] + rotMatrix[1][0]) / s; + z = (rotMatrix[0][2] + rotMatrix[2][0]) / s; + } else if (rotMatrix[1][1] > rotMatrix[2][2]) + { + T s = 2.0f * sqrtf(1.0f + rotMatrix[1][1] - rotMatrix[0][0] - rotMatrix[2][2]); + w = (rotMatrix[0][2] - rotMatrix[2][0]) / s; + x = (rotMatrix[0][1] + rotMatrix[1][0]) / s; + y = 0.25f * s; + z = (rotMatrix[1][2] + rotMatrix[2][1]) / s; + } else + { + T s = 2.0f * sqrtf(1.0f + rotMatrix[2][2] - rotMatrix[0][0] - rotMatrix[1][1]); + w = (rotMatrix[1][0] - rotMatrix[0][1]) / s; + x = (rotMatrix[0][2] + rotMatrix[2][0]) / s; + y = (rotMatrix[1][2] + rotMatrix[2][1]) / s; + z = 0.25f * s; + } + } + } + + /* + Vec3 ToEulerAngle() const + { + Vec3 euler; + + float ysqr = y * y; + + float t0 = 2 * (w * x + y * z); + float t1 = 1 - 2 * (x * x + ysqr); + euler.z = std::atan2(t0, t1); + + float t2 = 2 * (w * y - z * x); + t2 = t2 > 1 ? 1 : t2; + t2 = t2 < -1 ? -1 : t2; + euler.y = std::asin(t2); + + float t3 = 2 * (w * z + x * y); + float t4 = 1 - 2 * (ysqr + z * z); + euler.x = std::atan2(t3, t4); + + return euler; + } + */ + + Mat4 ToMatrix() const + { + Mat4 result; + + T x2 = x + x; + T y2 = y + y; + T z2 = z + z; + T x2w = x2 * w; + T y2w = y2 * w; + T z2w = z2 * w; + T x2x = x2 * x; + T y2x = y2 * x; + T z2x = z2 * x; + T y2y = y2 * y; + T z2y = z2 * y; + T z2z = z2 * y; + + result[0] = T(1) - (y2y + z2z); + result[1] = y2x - z2w; + result[2] = z2x + y2w; + result[3] = T(0); + + result[4] = y2x + z2w; + result[5] = T(1) - (x2x + z2z); + result[6] = z2y - x2w; + result[7] = T(0); + + result[8] = z2x - y2w; + result[9] = z2y + x2w; + result[10] = T(1) - (x2x + y2y); + result[11] = T(0); + + result[12] = T(0); + result[13] = T(0); + result[14] = T(0); + result[15] = T(1); + + return result; + } + + float GetMagnitude() + { + return Math::Sqrt(Math::Pow(w, 2) + Math::Pow(x, 2) + Math::Pow(y, 2) + Math::Pow(z, 2)); + } + + Quat GetNormalized() + { + T mag = GetMagnitude(); + + return Quat(w / mag, x / mag, y / mag, z / mag); + } + + void Normalize() + { + T mag = GetMagnitude(); + + w = w / mag; + x = x / mag; + y = y / mag; + z = z / mag; + } + + T Dot(const Quat& other) const + { + return w * other.w + x * other.x + y * other.y + z * other.z; + } + + Quat GetConjugate() + { + return Quat(w, -x, -y, -z); + } + + void Conjugate() + { + x = -x; + y = -y; + z = -z; + } + + Quat GetInverse() + { + return Quat(w, -x, -y, -z); + } + + void Inverse() + { + x = -x; + y = -y; + z = -z; + } + + static Quat Slerp(Quat start, Quat finish, const T t) + { + T cosHalfTheta = start.Dot(finish); + if (Math::Abs(cosHalfTheta) >= 1.0f) + return start; + + float halfTheta = Math::ACos(cosHalfTheta); + float sinHalfTheta = Math::Sqrt(1.0f - cosHalfTheta * cosHalfTheta); + if (Math::Abs(sinHalfTheta) < 0.001f) + { + return { + start.w * 0.5f + finish.w * 0.5f, + start.x * 0.5f + finish.x * 0.5f, + start.y * 0.5f + finish.y * 0.5f, + start.z * 0.5f + finish.z * 0.5f + }; + } + + float ratioA = Math::Sin((1 - t) * halfTheta) / sinHalfTheta; + float ratioB = Math::Sin(t * halfTheta) / sinHalfTheta; + + return { + start.w * ratioA + finish.w * ratioB, + start.x * ratioA + finish.x * ratioB, + start.y * ratioA + finish.y * ratioB, + start.z * ratioA + finish.z * ratioB + }; + } + }; + + typedef Quat Quat_f; +} \ No newline at end of file diff --git a/include/ehs/Range.h b/include/ehs/Range.h new file mode 100644 index 0000000..bfea28a --- /dev/null +++ b/include/ehs/Range.h @@ -0,0 +1,27 @@ +#pragma once + +namespace ehs +{ + template + class Range + { + public: + N min; + N max; + + Range() + : min(0), max(0) + { + } + + Range(const N min, const N max) + : min(min), max(max) + { + } + + Range(const Range& range) + : min(range.min), max(range.max) + { + } + }; +} diff --git a/include/ehs/Rect.h b/include/ehs/Rect.h new file mode 100644 index 0000000..d652741 --- /dev/null +++ b/include/ehs/Rect.h @@ -0,0 +1,186 @@ +#pragma once + +#include "EHS.h" +#include "Vec2.h" +#include "Vec3.h" +#include "Vec4.h" + +namespace ehs +{ + template + class Rect + { + public: + T x; + T y; + T w; + T h; + + Rect(const T scalar = 0) + : x(scalar), y(scalar), w(scalar), h(scalar) + { + } + + Rect(const T x, const T y, const T w, const T h) + : x(x), y(y), w(w), h(h) + { + } + + template + Rect(const Vec2& vec, const T w = 0, const T h = 0) + : x((T)vec.x), y((T)vec.y), w(w), h(h) + { + } + + template + Rect(const Vec3& vec, const T h = 0) + : x((T)vec.x), y((T)vec.y), w((T)vec.z), h(h) + { + } + + template + Rect(const Vec4& vec) + : x((T)vec.x), y((T)vec.y), w((T)vec.z), h((T)vec.w) + { + } + + template + Rect(const Rect& rect) + : x((T)rect.x), y((T)rect.y), w((T)rect.w), h((T)rect.h) + { + } + + template + Rect& operator=(const Vec2& vec) + { + x = (T)vec.x; + y = (T)vec.y; + w = 0; + h = 0; + + return *this; + } + + template + Rect& operator=(const Vec3& vec) + { + x = (T)vec.x; + y = (T)vec.y; + w = (T)vec.z; + h = 0; + + return *this; + } + + template + Rect& operator=(const Vec4& vec) + { + x = (T)vec.x; + y = (T)vec.y; + w = (T)vec.z; + h = (T)vec.w; + + return *this; + } + + template + Rect& operator=(const Rect& rect) + { + if (this == &rect) + return *this; + + x = (T)rect.x; + y = (T)rect.y; + w = (T)rect.w; + h = (T)rect.h; + + return *this; + } + + bool operator==(const Vec4& vec) + { + return x == vec.x && y == vec.y && w == vec.z && h == vec.w; + } + + bool operator!=(const Vec4& vec) + { + return x != vec.x || y != vec.y || w != vec.z || h != vec.w; + } + + bool operator==(const Rect& rect) + { + return x == rect.x && y == rect.y && w == rect.w && h == rect.h; + } + + bool operator!=(const Rect& rect) + { + return x != rect.x || y != rect.y || w != rect.w || h != rect.h; + } + + T operator[](const UInt_64 index) const + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return w; + case 3: + return h; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Rectangle."); + return x; + } + } + + T& operator[](const UInt_64 index) + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return w; + case 3: + return h; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Rectangle."); + return x; + } + } + + operator Vec4() + { + return Vec4(x, y, w, h); + } + + Vec2 GetPos() const + { + return {x, y}; + } + + Vec2 GetScale() const + { + return {w, h}; + } + }; + + typedef Rect Rect_u64; + typedef Rect Rect_s64; + typedef Rect Rect_64; + typedef Rect Rect_u32; + typedef Rect Rect_s32; + typedef Rect Rect_32; + typedef Rect Rect_u16; + typedef Rect Rect_s16; + typedef Rect Rect_16; + typedef Rect Rect_u8; + typedef Rect Rect_s8; + typedef Rect Rect_8; + typedef Rect Rect_f; + typedef Rect Rect_d; +} \ No newline at end of file diff --git a/include/ehs/SArray.h b/include/ehs/SArray.h new file mode 100644 index 0000000..23519f6 --- /dev/null +++ b/include/ehs/SArray.h @@ -0,0 +1,110 @@ +#pragma once + +#include "EHS.h" +#include "Log.h" + +namespace ehs +{ + /// This container is useful for sorting arrays efficiently. + template + class SArray + { + private: + T* data; + N size; + + public: + ~SArray() + { + delete[] data; + } + + SArray() + : data(nullptr), size(0) + { + } + + SArray(const N size) + : data(new T[size]), size(size) + { + } + + SArray(const SArray& sArray) + : data(new T[sArray.size]), size(sArray.size) + { + for (N i = 0; i < size; ++i) + data[i] = sArray.data[i]; + } + + SArray(SArray&& sArray) noexcept + : data(sArray.data), size(sArray.size) + { + sArray.data = nullptr; + sArray.size = 0; + } + + SArray& operator=(const SArray& pArray) + { + if (this == &pArray) + return *this; + + delete[] data; + data = new T[pArray.size]; + for (N i = 0; i < pArray.size; ++i) + data[i] = pArray.data[i]; + + size = pArray.size; + + return *this; + } + + SArray& operator=(SArray&& pArray) noexcept + { + if (this == &pArray) + return *this; + + delete[] data; + data = pArray.data; + size = pArray.size; + + pArray.data = nullptr; + pArray.size = 0; + + return *this; + } + + operator const T* () const + { + return data; + } + + operator T* () + { + return data; + } + + void Insert(const N index, T value) + { + if (index >= size) + { + EHS_LOG_INT("Warning", 0, "Cannot insert value at " + Str_8::FromNum(index) + " because it is outside of array range of " + size + "."); + return; + } + + for (N i = size; i > index + 1; --i) + data[i - 1] = std::move(data[i - 2]); + + data[index] = std::move(value); + } + + void SetSize(const N newSize) + { + size = newSize; + } + + N Size() const + { + return size; + } + }; +} \ No newline at end of file diff --git a/include/ehs/Serializer.h b/include/ehs/Serializer.h new file mode 100644 index 0000000..7f58f92 --- /dev/null +++ b/include/ehs/Serializer.h @@ -0,0 +1,1153 @@ +#pragma once + +#include "Types.h" +#include "BaseObj.h" +#include "Util.h" +#include "Array.h" +#include "Vector.h" +#include "Str.h" +#include "Version.h" +#include "Vec2.h" +#include "Vec3.h" +#include "Color3.h" +#include "Vec4.h" +#include "Rect.h" +#include "Color4.h" +#include "Quat.h" +#include "Mat2.h" +#include "Mat3.h" +#include "Mat4.h" + +namespace ehs +{ + template + class Serializer : public BaseObj + { + private: + Endianness endianness; + Byte* data; + N size; + N offset; + + public: + ~Serializer() override + { + delete[] data; + } + + Serializer() + : endianness(Endianness::BE), data(nullptr), size(0), offset(0) + { + AddType("Serializer"); + } + + Serializer(const Endianness endianness) + : endianness(endianness), data(nullptr), size(0), offset(0) + { + AddType("Serializer"); + } + + Serializer(const Endianness endianness, const N size) + : endianness(endianness), data(new Byte[size]), size(size), offset(0) + { + AddType("Serializer"); + } + + Serializer(const Endianness endianness, const Byte* const data, const N size, const N offset = 0) + : endianness(endianness), data(new Byte[size]), size(size), offset(offset) + { + AddType("Serializer"); + + for (N i = 0; i < size; ++i) + this->data[i] = data[i]; + } + + Serializer(Serializer&& serializer) noexcept + : BaseObj((BaseObj&&)serializer), endianness(serializer.endianness), data(serializer.data), + size(serializer.size), offset(serializer.offset) + { + serializer.data = nullptr; + serializer.size = 0; + serializer.offset = 0; + } + + Serializer(const Serializer& serializer) + : BaseObj(serializer), endianness(serializer.endianness), data(new Byte[serializer.size]), + size(serializer.size), offset(serializer.offset) + { + for (N i = 0; i < serializer.size; ++i) + data[i] = serializer.data[i]; + } + + Serializer& operator=(Serializer&& serializer) noexcept + { + if (this == &serializer) + return *this; + + BaseObj::operator=((Serializer&&)serializer); + + endianness = serializer.endianness; + delete[] data; + data = serializer.data; + size = serializer.size; + offset = serializer.offset; + + serializer.data = nullptr; + serializer.size = 0; + serializer.offset = 0; + + return *this; + } + + Serializer& operator=(const Serializer& serializer) + { + if (this == &serializer) + return *this; + + BaseObj::operator=(serializer); + + endianness = serializer.endianness; + + delete[] data; + data = new Byte[serializer.size]; + for (N i = 0; i < serializer.size; ++i) + data[i] = serializer.data[i]; + + size = serializer.size; + offset = serializer.offset; + + return *this; + } + + bool operator==(const Serializer& in) const + { + if (size != in.size) + return false; + + return Util::Compare(data, in.data, size); + } + + bool operator!=(const Serializer& in) const + { + if (size != in.size) + return true; + + return !Util::Compare(data, in.data, size); + } + + Serializer& operator+=(const N size) + { + offset += size; + return *this; + } + + Serializer& operator-=(const N size) + { + offset -= size; + return *this; + } + + Serializer& operator*=(const N size) + { + offset *= size; + return *this; + } + + Serializer& operator/=(const N size) + { + offset /= size; + return *this; + } + + Serializer& operator%=(const N size) + { + offset %= size; + return *this; + } + + operator const Byte* () const + { + return data; + } + + operator Byte* () + { + return data; + } + + void SetEndianness(const Endianness newEndianness) + { + endianness = newEndianness; + } + + Endianness GetEndianness() const + { + return endianness; + } + + template + void WriteArray(const T* const value, const O size) + { + if (sizeof(N) + size * sizeof(T) > this->size - offset) + { + N remainder = sizeof(N) + size * sizeof(T) - (this->size - offset); + + Byte* r = new Byte[this->size + remainder]; + + for (N i = 0; i < this->size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + this->size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + { + *(N*)&data[offset] = (N)size; + + offset += sizeof(N); + + for (N i = 0; i < size; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + else + { + N tmp = (N)size; + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmp)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < size; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - i - 1]; + } + } + else + { + if (endianness == Endianness::LE) + { + N tmp = (N)size; + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmp)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < size; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - i - 1]; + } + else + { + *(N*)&data[offset] = (N)size; + + offset += sizeof(N); + + for (N i = 0; i < size; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + } + + offset += sizeof(T) * size; + } + + template + void WriteArray(const Array& value) + { + if (sizeof(N) + value.Size() * sizeof(T) > size - offset) + { + N remainder = sizeof(N) + value.Size() * sizeof(T) - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + { + *(N*)&data[offset] = (N)value.Size(); + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + else + { + N tmpSize = (N)value.Size(); + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmpSize)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - i - 1]; + } + } + else + { + if (endianness == Endianness::LE) + { + N tmpSize = (N)value.Size(); + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmpSize)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - i - 1]; + } + else + { + *(N*)&data[offset] = (N)value.Size(); + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + } + + offset += sizeof(T) * value.Size(); + } + + template + void WriteVector(const Vector& value) + { + if (sizeof(N) + value.Size() * sizeof(T) > size - offset) + { + N remainder = sizeof(N) + value.Size() * sizeof(T) - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + { + *(N*)&data[offset] = (N)value.Size(); + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + else + { + N tmpSize = (N)value.Size(); + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmpSize)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - i - 1]; + } + } + else + { + if (endianness == Endianness::LE) + { + N tmpSize = (N)value.Size(); + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmpSize)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - i - 1]; + } + else + { + *(N*)&data[offset] = (N)value.Size(); + + offset += sizeof(N); + + for (N i = 0; i < value.Size(); ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + } + + offset += sizeof(T) * value.Size(); + } + + template + void WriteStr(const Str& str) + { + if (sizeof(N) + sizeof(T) * str.Size() > size - offset) + { + N remainder = sizeof(N) + sizeof(T) * str.Size() - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + { + *(N*)&data[offset] = (N)str.Size(); + + offset += sizeof(N); + + for (N i = 0; i < str.Size(); ++i) + *(T*)&data[offset + i * sizeof(T)] = str[i]; + } + else + { + N tmpSize = (N)str.Size(); + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmpSize)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < str.Size(); ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&str[i])[sizeof(T) - i - 1]; + } + } + else + { + if (endianness == Endianness::LE) + { + N tmpSize = (N)str.Size(); + + for (N i = 0; i < sizeof(N); ++i) + data[offset + i] = ((Byte*)&tmpSize)[sizeof(N) - i - 1]; + + offset += sizeof(N); + + for (N i = 0; i < str.Size(); ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&str[i])[sizeof(T) - i - 1]; + } + else + { + *(N*)&data[offset] = (N)str.Size(); + + offset += sizeof(N); + + for (N i = 0; i < str.Size(); ++i) + *(T*)&data[offset + i * sizeof(T)] = str[i]; + } + } + + offset += str.Size() * sizeof(T); + } + + void WriteVersion(const Version& value) + { + if (sizeof(unsigned int) * 3 > size - offset) + { + N remainder = sizeof(unsigned int) * 3 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 3; ++i) + *(unsigned int*)&data[offset + i * sizeof(unsigned int)] = value[i]; + else + for (N i = 0; i < 3; ++i) + for (N b = 0; b < sizeof(unsigned int); ++b) + data[offset + i * sizeof(unsigned int) + b] = value[sizeof(unsigned int) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 3; ++i) + for (N b = 0; b < sizeof(unsigned int); ++b) + data[offset + i * sizeof(unsigned int) + b] = value[sizeof(unsigned int) - i - 1]; + else + for (N i = 0; i < 3; ++i) + *(unsigned int*)&data[offset + i * sizeof(unsigned int)] = value[i]; + } + + offset += sizeof(unsigned int) * 3; + } + + template + void WriteVec2(const Vec2& value) + { + if (sizeof(T) * 2 > size - offset) + { + N remainder = sizeof(T) * 2 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 2; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + else + for (N i = 0; i < 2; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 2; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + else + for (N i = 0; i < 2; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + + offset += sizeof(T) * 2; + } + + template + void WriteVec3(const Vec3& value) + { + if (sizeof(T) * 3 > size - offset) + { + N remainder = sizeof(T) * 3 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 3; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + else + for (N i = 0; i < 3; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 3; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + else + for (N i = 0; i < 3; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + + offset += sizeof(T) * 3; + } + + void WriteColor3(const Color3& value) + { + if (sizeof(float) * 3 > size - offset) + { + N remainder = sizeof(float) * 3 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 3; ++i) + *(float*)&data[offset + i * sizeof(float)] = value[i]; + else + for (N i = 0; i < 3; ++i) + for (N b = 0; b < sizeof(float); ++b) + data[offset + i * sizeof(float) + b] = value[sizeof(float) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 3; ++i) + for (N b = 0; b < sizeof(float); ++b) + data[offset + i * sizeof(float) + b] = value[sizeof(float) - i - 1]; + else + for (N i = 0; i < 3; ++i) + *(float*)&data[offset + i * sizeof(float)] = value[i]; + } + + offset += sizeof(float) * 3; + } + + template + void WriteVec4(const Vec4& value) + { + if (sizeof(T) * 4 > size - offset) + { + N remainder = sizeof(T) * 4 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + else + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + else + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + + offset += sizeof(T) * 4; + } + + template + void WriteRect(const Rect& value) + { + if (sizeof(T) * 4 > size - offset) + { + N remainder = sizeof(T) * 4 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + else + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + else + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + + offset += sizeof(T) * 4; + } + + void WriteColor4(const Color4& value) + { + if (sizeof(float) * 4 > size - offset) + { + N remainder = sizeof(float) * 4 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + *(float*)&data[offset + i * sizeof(float)] = value[i]; + else + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(float); ++b) + data[offset + i * sizeof(float) + b] = value[sizeof(float) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(float); ++b) + data[offset + i * sizeof(float) + b] = value[sizeof(float) - i - 1]; + else + for (N i = 0; i < 4; ++i) + *(float*)&data[offset + i * sizeof(float)] = value[i]; + } + + offset += sizeof(float) * 4; + } + + template + void WriteQuat(const Quat& value) + { + if (sizeof(T) * 4 > size - offset) + { + N remainder = sizeof(T) * 4 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + else + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = value[sizeof(T) - i - 1]; + else + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + + offset += sizeof(T) * 4; + } + + template + void WriteMat2(const Mat2& value) + { + if (sizeof(T) * 4 > size - offset) + { + N remainder = sizeof(T) * 4 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + else + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - b - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 4; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - b - 1]; + else + for (N i = 0; i < 4; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + + offset += sizeof(T) * 4; + } + + template + void WriteMat3(const Mat3& value) + { + if (sizeof(T) * 9 > size - offset) + { + N remainder = sizeof(T) * 9 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N i = 0; i < 9; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + else + for (N i = 0; i < 9; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - b - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < 9; ++i) + for (N b = 0; b < sizeof(T); ++b) + data[offset + i * sizeof(T) + b] = ((Byte*)&value[i])[sizeof(T) - b - 1]; + else + for (N i = 0; i < 9; ++i) + *(T*)&data[offset + i * sizeof(T)] = value[i]; + } + + offset += sizeof(T) * 9; + } + + template + void WriteMat4(const Mat4& value) + { + if (sizeof(T) * 16 > size - offset) + { + N remainder = sizeof(T) * 16 - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + for (N y = 0; y < 4; ++y) + for (N x = 0; x < 4; ++x) + *(T*)&data[offset + y * sizeof(T) * 4 + x * sizeof(T)] = value[y][x]; + else + for (N y = 0; y < 4; ++y) + for (N x = 0; x < 4; ++x) + for (N i = 0; i < sizeof(T); ++i) + data[offset + y * sizeof(T) * 4 + x * sizeof(T) + i] = ((Byte*)&value[y][x])[sizeof(T) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N y = 0; y < 4; ++y) + for (N x = 0; x < 4; ++x) + for (N i = 0; i < sizeof(T); ++i) + data[offset + y * sizeof(T) * 4 + x * sizeof(T) + i] = ((Byte*)&value[y][x])[sizeof(T) - i - 1]; + else + for (N y = 0; y < 4; ++y) + for (N x = 0; x < 4; ++x) + *(T*)&data[offset + y * sizeof(T) * 4 + x * sizeof(T)] = value[y][x]; + } + + offset += sizeof(T) * 16; + } + + void WriteSer(const Serializer& ser) + { + if (ser.endianness != endianness) + return; + + if (ser.Size() > size - offset) + { + N remainder = ser.Size() - (size - offset); + + Byte* result = new Byte[size + remainder]; + + Util::Copy(result, data, size); + + delete[] data; + data = result; + size += remainder; + } + + Util::Copy(&data[offset], &ser[0], ser.Size()); + + offset += ser.Size(); + } + + template + void Write(const T value) + { + if (sizeof(T) > size - offset) + { + N remainder = sizeof(T) - (size - offset); + + Byte* r = new Byte[size + remainder]; + + for (N i = 0; i < size; ++i) + r[i] = data[i]; + + delete[] data; + data = r; + size += remainder; + } + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + *(T*)&data[offset] = value; + else + for (N i = 0; i < sizeof(T); ++i) + data[offset + i] = ((Byte*)&value)[sizeof(T) - i - 1]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < sizeof(T); ++i) + data[offset + i] = ((Byte*)&value)[sizeof(T) - i - 1]; + else + *(T*)&data[offset] = value; + } + + offset += sizeof(T); + } + + template + void ReadArray(T* const value, O* const size) + { + if (!*size) + *size = (O)Read(); + + for (N i = 0; i < *size; ++i) + value[i] = Read(); + } + + template + Array ReadArray(const O size = 0) + { + Array result(size ? size : (O)Read()); + + for (O i = 0; i < result.Size(); ++i) + result[i] = Read(); + + return result; + } + + template + Vector ReadVector(const O size = 0) + { + Vector result(size ? size : (O)Read()); + + for (O i = 0; i < result.Size(); ++i) + result[i] = Read(); + + return result; + } + + template + Str ReadStr(const O size = 0) + { + Str result(size ? size : (O)Read()); + + for (O i = 0; i < result.Size(); ++i) + result[i] = Read(); + + return result; + } + + Version ReadVersion() + { + return {Read(), Read(), Read()}; + } + + template + Vec2 ReadVec2() + { + return {Read(), Read()}; + } + + template + Vec3 ReadVec3() + { + return {Read(), Read(), Read()}; + } + + Color3 ReadColor3() + { + return {Read(), Read(), Read()}; + } + + template + Vec4 ReadVec4() + { + return {Read(), Read(), Read(), Read()}; + } + + template + Rect ReadRect() + { + return {Read(), Read(), Read(), Read()}; + } + + Color4 ReadColor4() + { + return {Read(), Read(), Read(), Read()}; + } + + template + Quat ReadQuat() + { + return {Read(), Read(), Read(), Read()}; + } + + template + Mat2 ReadMat2() + { + Mat2 result; + for (N i = 0; i < 4; ++i) + result[i] = Read(); + + return result; + } + + template + Mat3 ReadMat3() + { + Mat3 result; + for (N i = 0; i < 9; ++i) + result[i] = Read(); + + return result; + } + + template + Mat4 ReadMat4() + { + Mat4 result; + for (N i = 0; i < 16; ++i) + result[i] = Read(); + + return result; + } + + template + T Read() + { + T value = (T)0; + + if (CPU::GetEndianness() == Endianness::LE) + { + if (endianness == Endianness::LE) + value = *(T*)&data[offset]; + else + for (N i = 0; i < sizeof(T); ++i) + ((Byte*)&value)[sizeof(T) - i - 1] = data[offset + i]; + } + else + { + if (endianness == Endianness::LE) + for (N i = 0; i < sizeof(T); ++i) + ((Byte*)&value)[sizeof(T) - i - 1] = data[offset + i]; + else + value = *(T*)&data[offset]; + } + + offset += sizeof(T); + + return value; + } + + void SetOffset(const N offset) + { + this->offset = offset; + } + + N GetOffset() const + { + return offset; + } + + void Resize(const N newSize) + { + Byte* result = new Byte[newSize]; + Util::Copy(result, data, size); + delete[] data; + data = result; + size = newSize; + } + + N Size() const + { + return size; + } + }; +} \ No newline at end of file diff --git a/include/ehs/ShdPtr.h b/include/ehs/ShdPtr.h new file mode 100644 index 0000000..a2e55bb --- /dev/null +++ b/include/ehs/ShdPtr.h @@ -0,0 +1,121 @@ +#pragma once + +#include "PtrData.h" + +namespace ehs +{ + template + class ShdPtr + { + private: + T* data; + + public: + ~ShdPtr() + { + if (RemovePtrData(data)) + delete data; + } + + ShdPtr() + : data(nullptr) + { + } + + ShdPtr(T* data) + : data(data) + { + AddPtrData(data); + } + + ShdPtr(ShdPtr&& shdPtr) noexcept + : data(shdPtr.data) + { + shdPtr.data = nullptr; + } + + ShdPtr(const ShdPtr& shdPtr) + : data(shdPtr.data) + { + AddPtrData(data); + } + + ShdPtr& operator=(ShdPtr&& shdPtr) noexcept + { + if (this == &shdPtr) + return *this; + + if (RemovePtrData(data)) + delete data; + + data = shdPtr.data; + + shdPtr.data = nullptr; + + return *this; + } + + ShdPtr& operator=(const ShdPtr& shdPtr) noexcept + { + if (this == &shdPtr) + return *this; + + if (RemovePtrData(data)) + delete data; + + data = shdPtr.data; + + AddPtrData(data); + + return *this; + } + + bool operator==(const ShdPtr& shdPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data == shdPtr.data; + } + + bool operator==(T* inPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data == inPtr; + } + + bool operator!=(const ShdPtr& shdPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data != shdPtr.data; + } + + bool operator!=(T* inPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data != inPtr; + } + + void Release() + { + if (RemovePtrData(data)) + delete data; + + data = nullptr; + } + + T* GetData() + { + if (!HasPtrData(data)) + data = nullptr; + + return data; + } + }; +} \ No newline at end of file diff --git a/include/ehs/Str.h b/include/ehs/Str.h new file mode 100644 index 0000000..dc05684 --- /dev/null +++ b/include/ehs/Str.h @@ -0,0 +1,1875 @@ +#pragma once + +#include "Types.h" +#include "BaseObj.h" +#include "Util.h" +#include "Vector.h" + +#include + +namespace ehs +{ + enum class SearchPattern + { + LEFT_RIGHT, + RIGHT_LEFT + }; + + enum class IndexResult + { + BEGINNING, + ENDING + }; + + /// A helper class for C-style strings. + /// @tparam T The character's data type to use. + /// @tparam N The number data type to use. + template + class Str : public BaseObj + { + private: + N size; + T* data; + + public: + /// Frees any data created on the heap. + ~Str() + { + delete[] data; + } + + /// Default members initialization. + Str() + : size(0), data(nullptr) + { + AddType("Str"); + } + + /// Initializes members with given C-style string. + /// @param [in] str The C-style string. + /// @param [in] size The size of the given C-style string. + Str(const T* const str, const N size) + : size((size) ? size : Len(str)), data(nullptr) + { + data = new T[this->size + 1]; + + Util::Copy(data, str, Size(true)); + + data[this->size] = 0; + + AddType("Str"); + } + + /// Initializes members with given C-style string. + /// @param [in] str The C-style string. + Str(const T* const str) + : size(Len(str)), data(nullptr) + { + data = new T[size + 1]; + + Util::Copy(data, str, Size(true)); + + data[size] = 0; + + AddType("Str"); + } + + /// Initializes string with the given size. + /// @param [in] size The size. + Str(const N size) + : size(size), data(new T[size + 1]) + { + data[size] = 0; + + AddType("Str"); + } + + Str(Str&& str) noexcept + : BaseObj((BaseObj&&)str), size(str.size), data(str.data) + { + str.size = 0; + str.data = nullptr; + } + + /// Copies all members from the given string object. + /// @param [in] str The string object to copy from. + Str(const Str& str) + : BaseObj(str), size(str.size), data(new T[size + 1]) + { + Util::Copy(data, str.data, Size(true)); + + data[size] = 0; + } + + Str& operator=(Str&& str) noexcept + { + if (this == &str) + return *this; + + BaseObj::operator=((BaseObj&&)str); + + size = str.size; + delete[] data; + data = str.data; + + str.size = 0; + str.data = nullptr; + + return *this; + } + + /// Copies all members from the given string object. + /// @param [in] str The string object to copy from. + /// @returns The string that has been assigned to. + Str& operator=(const Str& str) + { + if (&str == this) + return *this; + + BaseObj::operator=(str); + + size = str.size; + + delete[] data; + data = new T[size + 1]; + Util::Copy(data, str.data, Size(true)); + data[size] = 0; + + return *this; + } + + /// Copies the given C-style string and retrieves the size. + /// @param [in] str The C-style string to copy from. + /// @returns The string object that has been assigned to. + Str& operator=(const T* const str) + { + size = Len(str); + + delete[] data; + + data = new T[size + 1]; + + Util::Copy(data, str, Size(true)); + + data[size] = 0; + + return *this; + } + + /// Concatenates with the given C-style string. + /// @param [in] str The given C-style string. + /// @returns The result. + Str& operator+=(const T* const str) + { + N inputSize = Len(str); + + T* result = new T[size + inputSize + 1]; + + Util::Copy(result, data, Size(true)); + + delete[] data; + + Util::Copy(&result[size], str, inputSize * sizeof(T)); + + result[size + inputSize] = 0; + size += inputSize; + data = result; + + return *this; + } + + /// Concatenates with the given string object. + /// @param [in] str The given string object. + /// @returns The result. + Str& operator+=(const Str& str) + { + T* result = new T[size + str.size + 1]; + + Util::Copy(result, data, Size(true)); + + delete[] data; + + Util::Copy(&result[size], str, str.Size(true)); + + result[size + str.size] = 0; + size += str.Size(); + data = result; + + return *this; + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const SInt_64 num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const UInt_64 num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const SInt_32 num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const UInt_32 num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const SInt_16 num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const UInt_16 num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const SInt_8 num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const UInt_8 num) + { + return operator+=(FromNum(num)); + } + + #ifdef EHS_OS_WINDOWS + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const DWORD num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const HRESULT num) + { + return operator+=(FromNum(num)); + } + #endif + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const float num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const double num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] num The given number to concatenate. + /// @returns The result. + Str& operator+=(const long double num) + { + return operator+=(FromNum(num)); + } + + /// Concatenates with the given C-style string. + /// @param [in] str The given C-style string. + /// @returns The result. + Str operator+(const T* const str) const + { + N inSize = Len(str); + + Str result(size + inSize); + + Util::Copy(result.data, data, Size(true)); + + Util::Copy(&result.data[size], str, inSize * sizeof(T)); + + result.data[size + inSize] = 0; + + return result; + } + + /// Concatenates with the given string object. + /// @param [in] str The given string object. + /// @returns The result. + Str operator+(const Str& str) const + { + Str result(size + str.size); + + Util::Copy(result.data, data, Size(true)); + + Util::Copy(&result.data[size], str.data, str.Size(true)); + + result.data[size + str.size] = 0; + + return result; + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const SInt_64 num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const UInt_64 num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const SInt_32 num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const UInt_32 num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const SInt_16 num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const UInt_16 num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const SInt_8 num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const UInt_8 num) const + { + return operator+(FromNum(num)); + } + + #ifdef EHS_OS_WINDOWS + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const DWORD num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const HRESULT num) const + { + return operator+(FromNum(num)); + } + #endif + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const float num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const double num) const + { + return operator+(FromNum(num)); + } + + /// Concatenates with the given number. + /// @param [in] str The given number to Concatenate. + /// @returns The result. + Str operator+(const long double num) const + { + return operator+(FromNum(num)); + } + + bool operator==(T* str) const + { + if (size != Len(str)) + return false; + + return Util::Compare(data, str, Size(true)); + } + + /// Compares with a C-style string. First comparing sizes. + /// @param [in] str The C-style string to compare with. + /// @returns Whether or not they are equal. + bool operator==(const T* const str) const + { + if (size != Len(str)) + return false; + + return Util::Compare(data, str, Size(true)); + } + + /// Compares with a string object. First comparing sizes. + /// @param [in] str The string object to compare with. + /// @returns Whether or not they are equal. + bool operator==(const Str& str) const + { + if (size != str.size) + return false; + + return Util::Compare(data, str, Size(true)); + } + + /// Compares with a C-style string. First comparing sizes. + /// @param [in] str The C-style string to compare with. + /// @returns Whether or not they are equal. + bool operator!=(const T* const str) const + { + if (size != Len(str)) + return true; + + return !Util::Compare(data, str, Size(true)); + } + + /// Compares with a string object. First comparing sizes. + /// @param [in] str The string to compare with. + /// @returns Whether or not they are equal. + bool operator!=(const Str& str) const + { + if (size != str.size) + return true; + + return !Util::Compare(data, str, Size(true)); + } + + /// Retrieves the raw C-style string from casting a string object. + operator T* () const + { + return data; + } + + /// Resizes the string. + /// @param [in] newSize The size to change to. + void Resize(const N newSize) + { + if (newSize == size) + return; + + T* result = new T[newSize + 1]; + + if (newSize > size) + Util::Copy(result, data, Size(true)); + else + Util::Copy(result, data, newSize * sizeof(T)); + + size = newSize; + + delete[] data; + + data = result; + data[size] = 0; + } + + /// Retrieves the size of the string. + /// @param [in] inBytes To retrieve the size in bytes. + /// @returns The resulting size. + N Size(bool inBytes = false) const + { + if (inBytes) + return sizeof(T) * size; + else + return size; + } + + /// Finds a null terminator in the string and makes it the exact size if greater than. + void ExactSize() + { + size = Len(data); + + T* result = new T[size + 1]; + Util::Copy(result, data, Size(true)); + + delete[] data; + + result[size] = 0; + data = result; + } + + /// Copies a string object to the referenced string object. + /// @param dstOffset The offset index to copy to. + /// @param src The source string object to copy from. + void Copy(const N dstOffset, const Str& src) + { + if (dstOffset + src.size > size) + { + //Log_8 log(__FUNCSIG__, LogType::ERR, 0, "The parameters \"dstOffset\" (" + Str_8::FromNum(dstOffset) + ") + \"srcSize\" (" + Str_8::FromNum(src.Size()) + ") is greater than the referenced string object's size (" + Str_8::FromNum(size) + ")."); + return; + } + + Util::Copy(data[dstOffset], src, src.Size(true)); + } + + /// Copies a C-style string to the referenced string object. + /// @param dstOffset The offset index to copy to. + /// @param src The source C-style string to copy from. + void Copy(const N dstOffset, const T* const src) + { + N srcSize = Len(src); + + if (dstOffset + srcSize > size) + { + //Log_8 log(__FUNCSIG__, LogType::ERR, 0, "The parameters \"dstOffset\" (" + Str_8::FromNum(dstOffset) + ") + \"srcSize\" (" + Str_8::FromNum(srcSize) + ") is greater than the referenced string object's size (" + Str_8::FromNum(size) + ")."); + return; + } + + Util::Copy(data[dstOffset], src, srcSize * sizeof(T)); + } + + /// Copies a C-style string to the referenced string object. + /// @param dstOffset The offset index to copy to. + /// @param src The source C-style string to copy from. + /// @param srcSize The size of the source C-style string. + void Copy(const N dstOffset, const T* const src, const N srcSize) + { + if (dstOffset + srcSize > size) + { + //Log_8 log(__FUNCSIG__, LogType::ERR, 0, "The parameters \"dstOffset\" (" + Str_8::FromNum(dstOffset) + ") + \"srcSize\" (" + Str_8::FromNum(srcSize) + ") is greater than the referenced string object's size (" + Str_8::FromNum(size) + ")."); + return; + } + + Util::Copy(data[dstOffset], src, srcSize * sizeof(T)); + } + + void Insert(const N index, const Str& value) + { + if (!value.size) + return; + + N newSize = size + value.size; + + T* result = new T[newSize + 1]; + + Util::Copy(result, data, index * sizeof(T)); + + Util::Copy(&result[index], value.data, value.Size(true)); + + Util::Copy(&result[index + value.size], &data[index], size - index); + + result[newSize] = 0; + + delete[] data; + data = result; + size = newSize; + } + + void Insert(const N index, const T value) + { + N newSize = 0; + if (index > size - 1) + newSize = size + ((index + 1) - size); + else + newSize = size + 1; + + T* result = new T[newSize + 1]; + + for (N i = 0; i < index; ++i) + result[i] = data[i]; + + result[index] = value; + + for (N i = index; i < size; ++i) + result[i + 1] = data[i]; + + result[newSize] = 0; + + delete[] data; + data = result; + size = newSize; + } + + Str Remove(const N start, const N end) + { + if (!size || start >= size || end > size || end <= start) + return {}; + + Str popped(&data[start], end - start); + + N newSize = size - popped.size; + T* result = new T[newSize + 1]; + + Util::Copy(result, data, start * sizeof(T)); + + Util::Copy(&result[start], &data[end], (size - end) * sizeof(T)); + + result[newSize] = 0; + + delete[] data; + data = result; + size = newSize; + + return popped; + } + + T Remove(const N index) + { + T popped = {}; + + if (!size || index >= size) + return popped; + + popped = data[index]; + + N newSize = size - 1; + T* result = new T[newSize]; + + for (N i = 0; i < index; ++i) + result[i] = data[i]; + + for (N i = index + 1; i < size; ++i) + result[i - 1] = data[i]; + + delete[] data; + data = result; + size = newSize; + + return popped; + } + + /// Adds a value at the end of the string. + /// @param [in] value The character to push to the end of the string. + /// @note Automatically moves the null terminator after the value is pushed. + void Push(const Str &value) + { + T* result = new T[size + value.size + 1]; + Util::Copy(result, data, Size(true)); + Util::Copy(&result[size], value, value.Size(true)); + + result[size + value.Size()] = 0; + + delete[] data; + + data = result; + size += value.size; + } + + /// Adds a value at the end of the string. + /// @param [in] value The C-style string to push to the end of the string. + /// @param [in] size The size of the given C-style string. + /// @note Automatically moves the null terminator after the value is pushed. + void Push(const T* const value, const N size) + { + T* result = new T[this->size + size + 1]; + Util::Copy(result, data, Size(true)); + Util::Copy(&result[this->size], value, size * sizeof(T)); + + result[this->size + size] = 0; + + delete[] data; + + data = result; + this->size += size; + } + + /// Adds a value at the end of the string. + /// @param [in] value The C-style string to push to the end of the string. + /// @note Automatically moves the null terminator after the value is pushed. + void Push(const T* const value) + { + N inSize = Len(value); + + T* result = new T[size + inSize + 1]; + Util::Copy(result, data, Size(true)); + Util::Copy(result[size], value, inSize * sizeof(T)); + + result[size + inSize] = 0; + + delete[] data; + + data = result; + size += inSize; + } + + /// Adds a value at the end of the string. + /// @param [in] value The character to push to the end of the string. + /// @note Automatically moves the null terminator after the value is pushed. + void Push(const T value) + { + T* result = new T[size + 2]; + + Util::Copy(result, data, Size(true)); + + result[size] = value; + result[size + 1] = 0; + + delete[] data; + + data = result; + ++size; + } + + /// Removes a value at the end of the array. + /// @returns The value that was popped. + T Pop() + { + T* result = new T[size--]; + + T value = data[size]; + + Util::Copy(result, data, Size(true)); + + result[size] = 0; + + delete[] data; + + data = result; + + return value; + } + + /// Retrieves the string in bytes for serialization. + /// @returns The string in bytes. + const Byte* ToBytes() const + { + return (Byte*)data; + } + + /// Retrieves the string in bytes for serialization. + /// @returns The string in bytes. + Byte* ToBytes() + { + return (Byte*)data; + } + + /// Changes all upper-case ASCII characters to lower-case. + void ToLower() + { + for (N i = 0; i < size; ++i) + if (data[i] > 64 && data[i] < 91) + data[i] += 32; + } + + /// Changes all upper-case ASCII characters to lower-case. + /// @returns The result. + Str GetLower() const + { + Str result(size); + + for (N i = 0; i < size; ++i) + if (data[i] > 64 && data[i] < 91) + result[i] = data[i] + 32; + else + result[i] = data[i]; + + return result; + } + + /// Changes all lower-case ASCII characters to upper-case. + void ToUpper() + { + for (N i = 0; i < size; ++i) + if (data[i] > 96 && data[i] < 123) + data[i] -= 32; + } + + /// Changes all lower-case ASCII characters to upper-case. + /// @returns The result. + Str GetUpper() const + { + Str result(size); + + for (N i = 0; i < size; ++i) + if (data[i] > 96 && data[i] < 123) + result[i] = data[i] - 32; + else + result[i] = data[i]; + + return result; + } + + /// Reverses the entire referenced string object. + void Reverse() + { + if (size <= 1 || !data) + return; + + T* r = new T[size + 1]; + + for (N i = 0; i < size; ++i) + r[i] = data[size - 1 - i]; + + r[size] = 0; + + delete[] data; + + data = r; + } + + /// Reverses the entire string object. + /// @returns The result. + Str GetReverse() + { + if (size <= 1 || !data) + return *this; + + Str r(size); + + for (N i = 0; i < size; ++i) + r[i] = data[size - 1 - i]; + + return r; + } + + /// Clips the string at the given index and with the given size. + /// @param [in] index The index to clip at. + /// @param [in] size The size for the clip starting from the index. + /// @returns The result. + Str Sub(const N index, const N size = 0) const + { + if (index >= this->size) + { + //Log_8 log(__FUNCSIG__, LogType::ERR, 0, "The given index parameter, \"" + Str::FromNum(index) + "\" is greater than the string's size, \"" + Str::FromNum(this->size) + "\"."); + + return Str(); + } + + if (size) + { + if (size > this->size) + { + //Log_8 log(__FUNCSIG__, LogType::ERR, 1, "The given size parameter, \"" + Str::FromNum(size) + "\" is greater than the string's size, \"" + Str::FromNum(this->size) + "\", at the index, \"" + Str::FromNum(index) + "\"."); + + return Str(); + } + + Str result(size); + + Util::Copy(result, &data[index], result.Size(true)); + + return result; + } + else + { + Str result(this->size - index); + + Util::Copy(result, &data[index], result.Size(true)); + + return result; + } + } + + /// Splits a string into an array with the given separator. + /// @param [in] ide The given string as the separator. + /// @param [in] max The max amount of times to split the string. + /// @returns The result. + Vector, N> Split(const Str& ide, const N max = 0) const + { + Vector, N> result(0, 5); + + N b = 0; + + for (N i = 0, c = 0; i < size; ++i) + { + if (data[i] == ide[c]) + { + if (++c == ide.Size()) + { + N r = i - (c - 1) - b; + if (!r) + result.Push(Str()); + else + result.Push(Str(&data[b], r)); + + b = i + 1; + c = 0; + + if (max && result.Size() == max) + break; + } + } + else + { + c = 0; + } + } + + if (b < size) + result.Push(Str(&data[b], size - b)); + + return result; + } + + /// Removes all instances of the given string object. + /// @param [in] ide The string to look for. + /// @returns The result. + Str RemoveAll(const Str& ide) const + { + Str result(size); + + for (N i = 0, b = 0, c = 0; i < size; ++i) + { + if (data[i] == ide[c]) + { + if (++c == ide.Size()) + c = 0; + } + else + { + if (c) + for (N d = c; d < 0; --d) + result[b++] = data[i - d]; + else + result[b++] = data[i]; + + c = 0; + } + } + + return result; + } + + Str ReplaceAll(const Str& ide, const Str& replacer) const + { + Str result; + + for (N i = 0, b = 0; i < size; ++i) + { + if (data[i] == ide[b]) + { + if (++b == ide.Size()) + { + result.Push(replacer); + b = 0; + } + } + else + { + result.Push(data[i]); + } + } + + return result; + } + + /// Finds the first instance of the given string object. + /// @param [in] ide The string to look for. + /// @param [out] index The index of the string found. Can be a nullptr. + /// @param [in] pattern The search pattern for optimization. + /// @param [in] result What index to return where the first instance is found. + /// @returns The index where the instance was found with the result varying from the result parameter. + bool Find(const Str& ide, N* const index = nullptr, const SearchPattern pattern = SearchPattern::LEFT_RIGHT, const IndexResult result = IndexResult::BEGINNING) const + { + if (pattern == SearchPattern::LEFT_RIGHT) + { + for (N i = 0, c = 0; i < size; ++i) + { + if (data[i] == ide[c]) + { + if (++c == ide.Size()) + { + if (result == IndexResult::BEGINNING) + { + if (index) + *index = i - (c - 1); + + return true; + } + else + { + if (index) + *index = i; + + return true; + } + } + } + } + } + else if (pattern == SearchPattern::RIGHT_LEFT) + { + for (N i = size, c = ide.Size(); i > 0; --i) + { + if (data[i - 1] == ide[c - 1]) + { + if (--c == 0) + { + if (result == IndexResult::BEGINNING) + { + if (index) + *index = i - (ide.Size() - 1); + + return true; + } + else + { + if (index) + *index = i; + + return true; + } + } + } + } + } + + return false; + } + + bool Contains(const Str& ide, const SearchPattern pattern = SearchPattern::LEFT_RIGHT) const + { + if (pattern == SearchPattern::LEFT_RIGHT) + { + for (N i = 0, c = 0; i < size; ++i) + { + if (data[i] == ide[c]) + { + if (++c == ide.Size()) + { + return true; + } + } + } + } + else if (pattern == SearchPattern::RIGHT_LEFT) + { + for (N i = size, c = ide.Size(); i > 0; --i) + { + if (data[i - 1] == ide[c - 1]) + { + if (--c == 0) + { + return true; + } + } + } + } + + return false; + } + + /// Checks if the string represents a number. Must not have any alphabetical characters. + /// @returns The result. + bool IsNum() const + { + if (!size) + return false; + + if ((data[0] < '0' || data[0] > '9') && data[0] != '-' && data[0] != '.') + return false; + + for (N i = 1; i < size; ++i) + if ((data[i] < '0' || data[i] > '9') && data[i] != '.') + return false; + + return true; + } + + template + static Str NumToHex(const I num) + { + static const T hex[] = "0123456789ABCDEF"; + + Str result(sizeof(I) * 2); + + for (UInt_8 i = 0; i < sizeof(I); ++i) + { + result[i * 2] = hex[((Byte*)&num)[i] / 16]; + result[i * 2 + 1] = hex[((Byte*)&num)[i] % 16]; + } + + return result; + } + + template + static I HexToNum(const Str& in) + { + N offset = 0; + bool neg = false; + + if (in[offset] == 45) + { + neg = true; + ++offset; + } + else if (in[offset] == 43) + ++offset; + + if (in[offset] == 48 && (in[offset + 1] == 88 || in[offset + 1] == 120)) + offset += 2; + + N acc = 0; + + for (N i = offset; i < in.Size(); ++i) + { + I value = 0; + + if (in[i] > 47 && in[i] < 58) + value = in[i] - 48; + else if (in[i] > 64 && in[i] < 71) + value = in[i] - 55; + else if (in[i] > 96 && in[i] < 103) + value = in[i] - 87; + + if (value >= 16) + return 0; + + acc *= 16; + acc += value; + } + + return neg ? -acc : acc; + } + + template + I HexToNum() const + { + N offset = 0; + bool neg = false; + + if (data[offset] == 45) + { + neg = true; + ++offset; + } + else if (data[offset] == 43) + ++offset; + + if (data[offset] == 48 && (data[offset + 1] == 88 || data[offset + 1] == 120)) + offset += 2; + + N acc = 0; + + for (N i = offset; i < size; ++i) + { + I value = 0; + + if (data[i] > 47 && data[i] < 58) + value = data[i] - 48; + else if (data[i] > 64 && data[i] < 71) + value = data[i] - 55; + else if (data[i] > 96 && data[i] < 103) + value = data[i] - 87; + + if (value >= 16) + return 0; + + acc *= 16; + acc += value; + } + + return neg ? -acc : acc; + } + + /// Converts the string into a number. + /// @tparam I The resulting number's data type. + /// @returns The result. + /// @note Use "IsNum" before this if the referenced string object will not always be a number. + template + I ToDecimal() const + { + I r = 0; + + if (!size) + return r; + + for (N i = data[0] == '-' ? 1 : 0; i < size; ++i) + r = r * 10 + data[i] - '0'; + + if (data[0] == '-') + r *= -1; + + return r; + } + + float ToFloat() const + { + N decPoint = size; + Find(".", &decPoint); + + float result = 0.0f; + float fraction = 0.0f; + float scale = 1.0f; + + for (N i = 0; i < decPoint; ++i) + result = result * 10.0f + data[i] - '0'; + + for (N i = decPoint + 1; i < size; ++i) + { + fraction = fraction * 10.0f + data[i] - '0'; + scale *= 10.0f; + } + + result += fraction / scale; + + return result; + } + + double ToDouble() const + { + N decPoint = size; + Find(".", &decPoint); + + double result = 0.0f; + double fraction = 0.0f; + double scale = 1.0f; + + for (N i = 0; i < decPoint; ++i) + result = result * 10.0f + data[i] - '0'; + + for (N i = decPoint + 1; i < size; ++i) + { + fraction = fraction * 10.0f + data[i] - '0'; + scale *= 10.0f; + } + + result += fraction / scale; + + return result; + } + + long double ToLDouble() const + { + N decPoint = size; + Find(".", &decPoint); + + long double result = 0.0f; + long double fraction = 0.0f; + long double scale = 1.0f; + + for (N i = 0; i < decPoint; ++i) + result = result * 10.0f + data[i] - '0'; + + for (N i = decPoint + 1; i < size; ++i) + { + fraction = fraction * 10.0f + data[i] - '0'; + scale *= 10.0f; + } + + result += fraction / scale; + + return result; + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const SInt_64 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(21); + + SInt_64 nonNeg; + if (num < 0) + nonNeg = -num; + else + nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + if (num < 0) + r[i++] = 45; + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const UInt_64 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(21); + + UInt_64 nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const SInt_32 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(11); + + SInt_32 nonNeg; + if (num < 0) + nonNeg = -num; + else + nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + if (num < 0) + r[i++] = 45; + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const UInt_32 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(11); + + UInt_32 nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const SInt_16 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(6); + + SInt_16 nonNeg; + if (num < 0) + nonNeg = -num; + else + nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + if (num < 0) + r[i++] = 45; + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const UInt_16 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(6); + + UInt_16 nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const SInt_8 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(4); + + SInt_8 nonNeg; + if (num < 0) + nonNeg = -num; + else + nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + if (num < 0) + r[i++] = 45; + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const UInt_8 num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(4); + + UInt_8 nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + r.Resize(i); + + return r.GetReverse(); + } + + #ifdef EHS_OS_WINDOWS + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const DWORD num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(11); + + DWORD nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + r.Resize(i); + + return r.GetReverse(); + } + + /// Converts the given number into a string. + /// @returns The result. + static Str FromNum(const HRESULT num) + { + if (num == 0) + { + Str z(1); + z[0] = 48; + return z; + } + + Str r(11); + + HRESULT nonNeg; + if (num < 0) + nonNeg = -num; + else + nonNeg = num; + + N i = 0; + + while (nonNeg != 0) + { + r[i++] = 48 + nonNeg % 10; + nonNeg /= 10; + } + + if (num < 0) + r[i++] = 45; + + r.Resize(i); + + return r.GetReverse(); + } + #endif + + /// Converts the given float into a string. + /// @returns The result. + static Str FromNum(const float num, const UInt_8 maxDecimals = 5) + { + SInt_64 whole = (SInt_64)num; + + Str result; + if (whole < 0) + result += "-"; + + result += Str::FromNum(whole); + + UInt_64 power = 10; + for (UInt_64 i = 0; i < (UInt_64)maxDecimals - 1; ++i) + power *= 10; + + SInt_64 fraction = (SInt_64)((num - (float)whole) * (float)power); + if (!fraction) + return result; + + result += "."; + + Str fResult(maxDecimals); + N i = 0; + + while (fraction) + { + fResult[i++] = 48 + fraction % 10; + fraction /= 10; + } + + while (i < maxDecimals) + fResult[i++] = 48; + + fResult.Reverse(); + result += fResult; + + return result; + } + + /// Converts the given double into a string. + /// @returns The result. + static Str FromNum(const double num, const UInt_8 maxDecimals = 5) + { + SInt_64 whole = (SInt_64)num; + + Str result; + if (whole < 0) + result += "-"; + + result += Str::FromNum(whole); + + UInt_64 power = 10; + for (UInt_64 i = 0; i < (UInt_64)maxDecimals - 1; ++i) + power *= 10; + + SInt_64 fraction = (SInt_64)((num - (double)whole) * (double)power); + if (!fraction) + return result; + + result += "."; + + Str fResult(maxDecimals); + N i = 0; + + while (fraction) + { + fResult[i++] = 48 + fraction % 10; + fraction /= 10; + } + + while (i < maxDecimals) + fResult[i++] = 48; + + fResult.Reverse(); + result += fResult; + + return result; + } + + /// Converts the given long double into a string. + /// @returns The result. + static Str FromNum(const long double num, const UInt_8 maxDecimals = 5) + { + SInt_64 whole = (SInt_64)num; + + Str result; + if (whole < 0) + result += "-"; + + result += Str::FromNum(whole); + + UInt_64 power = 10; + for (UInt_64 i = 0; i < (UInt_64)maxDecimals - 1; ++i) + power *= 10; + + SInt_64 fraction = (SInt_64)((num - (long double)whole) * (long double)power); + if (!fraction) + return result; + + result += "."; + + Str fResult(maxDecimals); + N i = 0; + + while (fraction) + { + fResult[i++] = 48 + fraction % 10; + fraction /= 10; + } + + while (i < maxDecimals) + fResult[i++] = 48; + + fResult.Reverse(); + result += fResult; + + return result; + } + + /// A 32-bit FNV-1a hash algorithm. + /// @param [in] str The string to hash. + /// @returns The resulting hash. + static UInt_32 Hash_32(const Str& str) + { + if (!str.Size()) + return 0; + + const Byte* const bytes = str.ToBytes(); + + UInt_32 hash = 2166136261ul; + + for (N i = 0; i < str.Size(true); ++i) + hash = (hash ^ bytes[i]) * 16777619; + + return hash; + } + + UInt_32 Hash_32() const + { + if (!size) + return 0; + + const Byte* const bytes = ToBytes(); + + UInt_32 hash = 2166136261ul; + + for (N i = 0; i < Size(true); ++i) + hash = (hash ^ bytes[i]) * 16777619; + + return hash; + } + + /// A 64-bit FNV-1a hash algorithm. + /// @param [in] str The string to hash. + /// @returns The resulting hash. + static UInt_64 Hash_64(const Str& str) + { + if (!str.Size()) + return 0; + + const Byte* const bytes = str.ToBytes(); + + UInt_64 hash = 14695981039346656037ull; + + for (N i = 0; i < str.Size(true); ++i) + hash = (hash ^ bytes[i]) * 1099511628211; + + return hash; + } + + UInt_64 Hash_64() const + { + if (!size) + return 0; + + const Byte* const bytes = ToBytes(); + + UInt_64 hash = 14695981039346656037ull; + + for (N i = 0; i < Size(true); ++i) + hash = (hash ^ bytes[i]) * 1099511628211; + + return hash; + } + + static N Len(const T* const str) + { + N count = 0; + while (str[count]) + ++count; + return count; + } + + static bool Cmp(const T* const a, const T* const b) + { + N aSize = Len(a); + N bSize = Len(b); + + if (aSize != bSize) + return false; + + for (UInt_64 i = 0; i < aSize; ++i) + if (a[i] != b[i]) + return false; + + return true; + } + }; + + typedef Str Str_32; + typedef Str Str_16; + typedef Str Str_8; +} + +template +bool operator==(const T* const first, const ehs::Str& second) +{ + N inSize = ehs::Str::Len(first); + if (second.Size() != inSize) + return false; + + return ehs::Util::Compare(first, second, second.Size(true)); +} + +template +bool operator!=(const T* const first, const ehs::Str& second) +{ + N inSize = ehs::Str::Len(first); + if (second.Size() != inSize) + return true; + + return !ehs::Util::Compare(first, second, second.Size(true)); +} + +/// Concatenates a C-style string with a string. +/// @param [in] first The given C-style string. +/// @param [in] second The given string. +/// @returns The result. +template +ehs::Str operator+(const T* const first, const ehs::Str& second) +{ + N inSize = ehs::Str::Len(first); + + ehs::Str result(inSize + second.Size()); + + ehs::Util::Copy(result, first, inSize * sizeof(T)); + ehs::Util::Copy(&result[inSize], &second[0], second.Size(true)); + + result[inSize + second.Size()] = 0; + + return result; +} \ No newline at end of file diff --git a/include/ehs/Task.h b/include/ehs/Task.h new file mode 100644 index 0000000..47d5d03 --- /dev/null +++ b/include/ehs/Task.h @@ -0,0 +1,50 @@ +#pragma once + +#include "EHS.h" +#include "BaseObj.h" +#include "ehs/system/Thread.h" +#include "ehs/system/Semaphore.h" + +namespace ehs +{ + typedef void (*TaskCb)(Serializer*); + + class Task + { + private: + bool working; + Semaphore* available; + Semaphore* done; + Serializer** cbArgs; + TaskCb* callback; + Serializer* threadArgs; + Thread thread; + + public: + ~Task(); + + Task(); + + Task(Task&& task) noexcept; + + Task(const Task& task); + + Task& operator=(Task&& task) noexcept; + + Task& operator=(const Task& task); + + void Revalidate(); + + void Initialize(); + + void Release(); + + bool IsWorking() const; + + void GiveWork(Serializer args, TaskCb cb); + + void WaitUntilDone(); + + bool IsValid() const; + }; +} diff --git a/include/ehs/Type.h b/include/ehs/Type.h new file mode 100644 index 0000000..e5f862f --- /dev/null +++ b/include/ehs/Type.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Types.h" +#include "Util.h" + +namespace ehs +{ + class Type + { + private: + friend class BaseObj; + + UInt_64 size; + const Char_8* id; + UInt_64 hashId; + + public: + Type(); + + explicit Type(const Char_8* id); + + Type(Type&& type) noexcept; + + Type(const Type& type); + + Type& operator=(Type&& type) noexcept; + + Type& operator=(const Type& type); + + bool operator==(const Type& type) const; + + bool operator!=(const Type& type) const; + + bool operator==(UInt_64 inHashId) const; + + bool operator!=(UInt_64 inHashId) const; + + bool operator==(const Char_8* inStr) const; + + bool operator!=(const Char_8* inStr) const; + + UInt_64 GetSize() const; + + const Char_8* GetId() const; + + UInt_64 GetHashId() const; + + bool IsValid() const; + + private: + static UInt_64 CalcSize(const Char_8* id); + + static UInt_64 GenHash(const Char_8* id, UInt_64 size); + }; +} \ No newline at end of file diff --git a/include/ehs/Types.h b/include/ehs/Types.h new file mode 100644 index 0000000..0371795 --- /dev/null +++ b/include/ehs/Types.h @@ -0,0 +1,55 @@ +#pragma once + +#include "ehs/system/OS.h" + +#define EHS_MAX_PATH 0x104 +#define EHS_UINT_8_MAX 0xFF +#define EHS_SINT_8_MAX 0x7F +#define EHS_SINT_8_MIN 0x80 +#define EHS_UINT_16_MAX 0xFFFF +#define EHS_SINT_16_MAX 0x7FFF +#define EHS_SINT_16_MIN 0x8000 +#define EHS_UINT_24_MAX 0xFFFFFF +#define EHS_SINT_24_MAX 0x7FFFFF +#define EHS_SINT_24_MIN 0x800000 +#define EHS_UINT_32_MAX 0xFFFFFFFF +#define EHS_SINT_32_MAX 0x7FFFFFFF +#define EHS_SINT_32_MIN 0x80000000 +#define EHS_UINT_64_MAX 0xFFFFFFFFFFFFFFFF +#define EHS_SINT_64_MAX 0x7FFFFFFFFFFFFFFF +#define EHS_SINT_64_MIN 0x8000000000000000 +#define EHS_FLOAT_MAX 3.40282e+038f +#define EHS_FLOAT_MIN 1.17549e-038f +#define EHS_DOUBLE_MAX 1.79769e+308 +#define EHS_DOUBLE_MIN 2.22507e-308 +#define EHS_LDOUBLE_MAX 1.79769e+308 +#define EHS_LDOUBLE_MIN 2.22507e-308 + +#define EHS_INFINITE EHS_UINT_32_MAX + +namespace ehs +{ + typedef unsigned char Byte; + typedef char Char_8; + typedef wchar_t Char_16; + typedef char32_t Char_32; + typedef unsigned char UInt_8; + typedef signed char SInt_8; + typedef char Int_8; + typedef unsigned short UInt_16; + typedef signed short SInt_16; + typedef short Int_16; + typedef unsigned int UInt_32; + typedef signed int SInt_32; + typedef int Int_32; + + #if defined(EHS_OS_WINDOWS) + typedef unsigned long long UInt_64; + typedef signed long long SInt_64; + typedef long long Int_64; + #elif defined(EHS_OS_LINUX) + typedef unsigned long UInt_64; + typedef signed long SInt_64; + typedef long Int_64; + #endif +} \ No newline at end of file diff --git a/include/ehs/URI.h b/include/ehs/URI.h new file mode 100644 index 0000000..1dd683e --- /dev/null +++ b/include/ehs/URI.h @@ -0,0 +1,15 @@ +#pragma once + +#include "EHS.h" +#include "Str.h" + +namespace ehs +{ + class URI + { + public: + static Str_8 Encode(const Str_8& in); + + static Str_8 Decode(const Str_8& in); + }; +} \ No newline at end of file diff --git a/include/ehs/UTF.h b/include/ehs/UTF.h new file mode 100644 index 0000000..6646d6f --- /dev/null +++ b/include/ehs/UTF.h @@ -0,0 +1,455 @@ +#pragma once + +#include "EHS.h" +#include "Str.h" + +namespace ehs +{ + enum class CharEncoding + { + UTF_32, + UTF_16, + UTF_8 + }; + + /// A helper class for converting between UTF8, 16 and 32. + class UTF + { + public: + /// Converts the given UTF16 C-style string into UTF32. + /// @tparam N The number data type to use. + /// @param [in] from The given C-style UTF16 string. + /// @param [in] size The size of the given C-style UTF16 string. + /// @returns The result. + template + static Str To_32(const Char_16* const from, const N size = 0) + { + Str result((size) ? size : Str::Len(from)); + + N index = 0; + + for (N i = 0; i < result.Size(); ++i) + { + if (i != result.Size() - 1) + { + if ((from[i] & 0xDC00) == 0xDC00 && (from[i + 1] & 0xD800) == 0xD800) + { + result[index++] = (((from[i] - 0xD800) * 0x400) | (from[i] - 0xDC00)) + 0x10000; + + continue; + } + } + + result[index++] = (Char_32)from[i]; + } + + result.Resize(index); + + return result; + } + + /// Converts the given UTF16 string object into UTF32. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF16 string. + /// @returns The result. + template + static Str To_32(const Str& from) + { + Str result(from.Size()); + + N index = 0; + + for (N i = 0; i < from.Size(); ++i) + { + if (i != from.Size() - 1) + { + if ((from[i] & 0xDC00) == 0xDC00 && (from[i + 1] & 0xD800) == 0xD800) + { + result[index++] = (((from[i] - 0xD800) * 0x400) | (from[i] - 0xDC00)) + 0x10000; + + continue; + } + } + + result[index++] = (Char_32)from[i]; + } + + result.Resize(index); + + return result; + } + + /// Converts the given UTF8 C-style string into UTF32. + /// @tparam N The number data type to use. + /// @param [in] from The given C-style UTF8 string. + /// @param [in] size The size of the given C-style UTF8 string. + /// @returns The result. + template + static Str To_32(const Char_8* from, const N size = 0) + { + N rSize = size ? size : Str::Len(from); + + Str r(rSize); + + N c = 0; + + for (N i = 0; i < rSize; ++i) + { + if (from[i] <= 0b11110111 && i + 3 < rSize && from[i + 1] <= 0b10111111 && from[i + 2] <= 0b10111111 && from[i + 3] <= 0b10111111) + r[c++] = (Char_32)(from[i++] & 0b00000111) << 18 | (Char_32)(from[i++] & 0b00111111) << 12 | (Char_32)(from[i++] & 0b00111111) << 6 | (Char_32)(from[i] & 0b00111111); + else if (from[i] <= 0b11101111 && i + 2 < rSize && from[i + 1] <= 0b10111111 && from[i + 2] <= 0b10111111) + r[c++] = (Char_32)(from[i++] & 0b00001111) << 12 | (Char_32)(from[i++] & 0b00111111) << 6 | ((Char_32)from[i] & 0b00111111); + else if (from[i] <= 0b11011111 && i + 1 < rSize && from[i + 1] <= 0b10111111) + r[c++] = (Char_32)(from[i++] & 0b00011111) << 6 | (Char_32)(from[i] & 0b00111111); + else + r[c++] = (Char_32)from[i]; + } + + r.Resize(c); + + return r; + } + + /// Converts the given UTF8 string object into UTF32. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF8 string. + /// @returns The result. + template + static Str To_32(const Str& from) + { + Str r(from.Size()); + + N c = 0; + + for (N i = 0; i < from.Size(); ++i) + { + if (from[i] <= 0b11110111 && i + 3 < from.Size() && from[i + 1] <= 0b10111111 && from[i + 2] <= 0b10111111 && from[i + 3] <= 0b10111111) + r[c++] = (Char_32)(from[i++] & 0b00000111) << 18 | (Char_32)(from[i++] & 0b00111111) << 12 | (Char_32)(from[i++] & 0b00111111) << 6 | (Char_32)(from[i] & 0b00111111); + else if (from[i] <= 0b11101111 && i + 2 < from.Size() && from[i + 1] <= 0b10111111 && from[i + 2] <= 0b10111111) + r[c++] = (Char_32)(from[i++] & 0b00001111) << 12 | (Char_32)(from[i++] & 0b00111111) << 6 | ((Char_32)from[i] & 0b00111111); + else if (from[i] <= 0b11011111 && i + 1 < from.Size() && from[i + 1] <= 0b10111111) + r[c++] = (Char_32)(from[i++] & 0b00011111) << 6 | (Char_32)(from[i] & 0b00111111); + else + r[c++] = (Char_32)from[i]; + } + + r.Resize(c); + + return r; + } + + /// Converts the given UTF32 C-style string object into UTF16. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF32 string. + /// @param [in] size The size of the give C-style UTF32 string. + /// @returns The result. + template + static Str To_16(const Char_32* const from, const N size = 0) + { + N rSize = size ? size : Str::Len(from); + + Str result(rSize * sizeof(Char_16)); + + N index = 0; + + for (N i = 0; i < rSize; ++i) + { + if (from[i] <= 0xFFFF) + { + result[index++] = (Char_16)from[i]; + } + else + { + Char_32 t = from[i] - 0x10000; + + result[index++] |= (t >> 10) + 0xD800; + result[index++] |= t + 0xDC00; + } + } + + result.Resize(index); + + return result; + } + + /// Converts the given UTF32 string object into UTF16. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF32 string. + /// @returns The result. + template + static Str To_16(const Str& from) + { + Str result(from.Size() * sizeof(Char_16)); + + N index = 0; + + for (N i = 0; i < from.Size(); ++i) + { + if (from[i] <= 0xFFFF) + { + result[index++] = (Char_16)from[i]; + } + else + { + Char_32 t = from[i] - 0x10000; + + result[index++] |= (t >> 10) + 0xD800; + result[index++] |= t + 0xDC00; + } + } + + result.Resize(index); + + return result; + } + + /// Converts the given UTF8 C-style string into UTF16. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF8 C-style string. + /// @param [in] size The size of the given C-style UTF8 string. + /// @returns The result. + template + static Str To_16(const Char_8* const from, const N size = 0) + { + N rSize = size ? size : Str::Len(from); + + const Byte* const data = (const Byte* const)from; + + Str r(rSize); + + N c = 0; + + for (N i = 0; i < rSize; ++i) + { + if (data[i] >= 0b11110000 && i + 3 < rSize && data[i + 1] <= 0b10111111 && data[i + 2] <= 0b10111111 && data[i + 3] <= 0b10111111) + r[c++] = (0b00000011111111110000001111111111 & + ((Char_16)(data[i++] & 0b00000111) << 23) | + ((Char_16)(data[i++] & 0b00111111) << 18) | + ((Char_16)(data[i++] & 0b00111111) << 12) | + ((Char_16)(data[i++] & 0b00111111) << 6) | + (Char_16)(data[i] & 0b00111111)) | + 0b11011000000000001101110000000000; + else if (data[i] >= 0b11100000 && i + 2 < rSize && data[i + 1] <= 0b10111111 && data[i + 2] <= 0b10111111) + r[c++] = ((Char_16)(data[i++] & 0b00001111) << 12) | ((Char_16)(data[i++] & 0b00111111) << 6) | (Char_16)(data[i] & 0b00111111); + else if (data[i] >= 0b11000000 && i + 1 < rSize && data[i + 1] <= 0b10111111) + r[c++] = (Char_16)(data[i++] & 0b00011111) << 6 | (Char_16 )(data[i] & 0b00111111); + else + r[c++] = (Char_16 )data[i]; + } + + r.Resize(c); + + return r; + } + + /// Converts the given UTF8 string object into UTF16. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF8 string. + /// @returns The result. + template + static Str To_16(const Str& from) + { + const Byte* const data = from.ToBytes(); + + Str r(from.Size()); + + N c = 0; + + for (N i = 0; i < from.Size(); ++i) + { + if (data[i] >= 0b11110000 && i + 3 < from.Size() && data[i + 1] <= 0b10111111 && data[i + 2] <= 0b10111111 && data[i + 3] <= 0b10111111) + r[c++] = (0b00000011111111110000001111111111 & + ((Char_16)(data[i++] & 0b00000111) << 23) | + ((Char_16)(data[i++] & 0b00111111) << 18) | + ((Char_16)(data[i++] & 0b00111111) << 12) | + ((Char_16)(data[i++] & 0b00111111) << 6) | + (Char_16)(data[i] & 0b00111111)) | + 0b11011000000000001101110000000000; + else if (data[i] >= 0b11100000 && i + 2 < from.Size() && data[i + 1] <= 0b10111111 && data[i + 2] <= 0b10111111) + r[c++] = ((Char_16)(data[i++] & 0b00001111) << 12) | ((Char_16)(data[i++] & 0b00111111) << 6) | (Char_16)(data[i] & 0b00111111); + else if (data[i] >= 0b11000000 && i + 1 < from.Size() && data[i + 1] <= 0b10111111) + r[c++] = (Char_16)(data[i++] & 0b00011111) << 6 | (Char_16 )(data[i] & 0b00111111); + else + r[c++] = (Char_16 )data[i]; + } + + r.Resize(c); + + return r; + } + + /// Converts the given UTF16 C-style string into UTF8. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF16 string. + /// @param [in] size The size of the given C-style UTF8 string. + /// @returns The result. + template + static Str To_8(const Char_16* const from, const N size = 0) + { + N rSize = size ? size : Str::Len(from); + + Str r(rSize * sizeof(Char_16)); + + N c = 0; + + for (N i = 0; i < rSize; ++i) + { + if (from[i] & 0b1101100000000000 && i + 1 < rSize && from[i] & 0b1101110000000000) + { + r[c++] = ((Byte*)&from[i])[1] & 00000111 | 0b11110000; + r[c++] = ((Byte*)&from[i])[0] >> 2 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] << 4 | (((Byte*)&from[i + 1])[1] & 0b00000011) << 2 | ((Byte*)&from[i + 1])[0] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[++i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] <= 0b11111111) + { + r[c++] = (Byte)from[i]; + } + else if (from[i] > 0b11111111 && from[i] <= 0b0000011111111111) + { + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 | 0b11000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] > 0b0000011111111111) + { + r[c++] = ((Byte*)&from[i])[1] >> 4 | 0b11100000; + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + } + + r.Resize(c); + + return r; + } + + /// Converts the given UTF16 string object into UTF8. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF16 string. + /// @returns The result. + template + static Str To_8(const Str& from) + { + Str r(from.Size(true) * sizeof(Char_16)); + + N c = 0; + + for (N i = 0; i < from.Size(); ++i) + { + if (from[i] & 0b1101100000000000 && i + 1 < from.Size() && from[i] & 0b1101110000000000) + { + r[c++] = ((Byte*)&from[i])[1] & 00000111 | 0b11110000; + r[c++] = ((Byte*)&from[i])[0] >> 2 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] << 4 | (((Byte*)&from[i + 1])[1] & 0b00000011) << 2 | ((Byte*)&from[i + 1])[0] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[++i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] <= 0b11111111) + { + r[c++] = (Byte)from[i]; + } + else if (from[i] > 0b11111111 && from[i] <= 0b0000011111111111) + { + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 | 0b11000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] > 0b0000011111111111) + { + r[c++] = ((Byte*)&from[i])[1] >> 4 | 0b11100000; + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + } + + r.Resize(c); + + return r; + } + + /// Converts the given UTF32 C-style string into UTF8. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF32 string. + /// @param [in] size The size of the give C-style UTF32 string. + /// @returns The result. + template + static Str To_8(const Char_32* const from, const N size = 0) + { + N rSize = size ? size : Str::Len(from); + + Str r(rSize * sizeof(Char_32)); + + N c = 0; + + for (N i = 0; i < rSize; ++i) + { + if (from[i] <= 0b11111111) + { + r[c++] = (Char_8)from[i]; + } + else if (from[i] > 0b11111111 && from[i] <= 0b0000011111111111) + { + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 | 0b11000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] > 0b0000011111111111 && from[i] <= 0b1111111111111111) + { + r[c++] = ((Byte*)&from[i])[2] << 2 | ((Byte*)&from[i])[1] >> 6 | 0b11100000; + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] > 0b1111111111111111) + { + r[c++] = ((Byte*)&from[i])[3] << 2 | ((Byte*)&from[i])[3] >> 2 & 0b00000111 | 0b11110000; + r[c++] = ((Byte*)&from[i])[2] << 2 | ((Byte*)&from[i])[2] >> 6 & 0b00111111 | 0b11100000; + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[1] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + } + + r.Resize(c); + + return r; + } + + /// Converts the given UTF32 string object into UTF8. + /// @tparam N The number data type to use. + /// @param [in] from The given UTF32 string. + /// @returns The result. + template + static Str To_8(const Str& from) + { + Str r(from.Size() * sizeof(Char_32)); + + N c = 0; + + for (N i = 0; i < from.Size(); ++i) + { + if (from[i] <= 0b11111111) + { + r[c++] = (Char_8)from[i]; + } + else if (from[i] > 0b11111111 && from[i] <= 0b0000011111111111) + { + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 | 0b11000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] > 0b0000011111111111 && from[i] <= 0b1111111111111111) + { + r[c++] = ((Byte*)&from[i])[2] << 2 | ((Byte*)&from[i])[1] >> 6 | 0b11100000; + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[0] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + else if (from[i] > 0b1111111111111111) + { + r[c++] = ((Byte*)&from[i])[3] << 2 | ((Byte*)&from[i])[3] >> 2 & 0b00000111 | 0b11110000; + r[c++] = ((Byte*)&from[i])[2] << 2 | ((Byte*)&from[i])[2] >> 6 & 0b00111111 | 0b11100000; + r[c++] = ((Byte*)&from[i])[1] << 2 | ((Byte*)&from[i])[1] >> 6 & 0b00111111 | 0b10000000; + r[c++] = ((Byte*)&from[i])[0] & 0b00111111 | 0b10000000; + } + } + + r.Resize(c); + + return r; + } + }; +} \ No newline at end of file diff --git a/include/ehs/UniPtr.h b/include/ehs/UniPtr.h new file mode 100644 index 0000000..bbc56a3 --- /dev/null +++ b/include/ehs/UniPtr.h @@ -0,0 +1,85 @@ +#pragma once + +namespace ehs +{ + template + class UniPtr + { + private: + T* data; + + public: + ~UniPtr() + { + delete data; + } + + UniPtr() + : data(nullptr) + { + } + + UniPtr(T* data) + : data(data) + { + } + + UniPtr(UniPtr&& uniPtr) noexcept + : data(uniPtr.data) + { + uniPtr.data = nullptr; + } + + UniPtr(const UniPtr& uniPtr) + : data(nullptr) + { + } + + UniPtr& operator=(UniPtr&& uniPtr) noexcept + { + if (this == &uniPtr) + return *this; + + delete data; + + data = uniPtr.data; + + uniPtr.data = nullptr; + + return *this; + } + + UniPtr& operator=(const UniPtr& uniPtr) noexcept + { + if (this == &uniPtr) + return *this; + + delete data; + + data = nullptr; + + return *this; + } + + bool operator==(T* inPtr) const + { + return data == inPtr; + } + + bool operator!=(T* inPtr) const + { + return data != inPtr; + } + + void Release() + { + delete data; + data = nullptr; + } + + T* GetData() + { + return data; + } + }; +} \ No newline at end of file diff --git a/include/ehs/Util.h b/include/ehs/Util.h new file mode 100644 index 0000000..c9b23f9 --- /dev/null +++ b/include/ehs/Util.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Types.h" + +namespace ehs +{ + class Util + { + public: + static bool Compare(const void* a, const void* b, UInt_64 size); + + static void Copy(void* out, const void* in, UInt_64 size); + + static void Fill(void* out, UInt_64 outSize, const void* in, UInt_64 inSize); + + static void Zero(void* in, UInt_64 size); + }; +} diff --git a/include/ehs/Vec2.h b/include/ehs/Vec2.h new file mode 100644 index 0000000..464047e --- /dev/null +++ b/include/ehs/Vec2.h @@ -0,0 +1,386 @@ +#pragma once + +#include "Types.h" +#include "UTF.h" +#include "Str.h" +#include "Math.h" +#include "Log.h" + +namespace ehs +{ + template + class Vec2 + { + public: + T x; + T y; + + Vec2(const T x, const T y) + : x(x), y(y) + { + } + + template + Vec2(const Vec2& vec) + : x((T)vec.x), y((T)vec.y) + { + } + + Vec2(const T scalar = 0) + : x(scalar), y(scalar) + { + } + + template + Vec2& operator=(const Vec2& vec) + { + x = (T)vec.x; + y = (T)vec.y; + + return *this; + } + + bool operator==(const Vec2& vec) const + { + return x == vec.x && y == vec.y; + } + + bool operator!=(const Vec2& vec) const + { + return x != vec.x || y != vec.y; + } + + Vec2& operator+=(const Vec2& vec) + { + x += vec.x; + y += vec.y; + + return *this; + } + + Vec2 operator+(const Vec2& vec) const + { + Vec2 tmp; + + tmp.x = x + vec.x; + tmp.y = y + vec.y; + + return tmp; + } + + Vec2& operator+=(const T scalar) + { + x += scalar; + y += scalar; + + return *this; + } + + Vec2 operator+(const T scalar) const + { + Vec2 tmp; + + tmp.x = x + scalar; + tmp.y = y + scalar; + + return tmp; + } + + Vec2& operator-=(const Vec2& vec) + { + x -= vec.x; + y -= vec.y; + + return *this; + } + + Vec2 operator-(const Vec2& vec) const + { + Vec2 tmp; + + tmp.x = x - vec.x; + tmp.y = y - vec.y; + + return tmp; + } + + Vec2& operator-=(const T scalar) + { + x -= scalar; + y -= scalar; + + return *this; + } + + Vec2 operator-(const T scalar) const + { + Vec2 tmp; + + tmp.x = x - scalar; + tmp.y = y - scalar; + + return tmp; + } + + Vec2& operator/=(const Vec2& vec) + { + x /= vec.x; + y /= vec.y; + + return *this; + } + + Vec2 operator/(const Vec2& vec) const + { + Vec2 tmp; + + tmp.x = x / vec.x; + tmp.y = y / vec.y; + + return tmp; + } + + Vec2& operator/=(const T scalar) + { + x /= scalar; + y /= scalar; + + return *this; + } + + Vec2 operator/(const T scalar) const + { + + Vec2 tmp; + + tmp.x = x / scalar; + tmp.y = y / scalar; + + return tmp; + } + + Vec2& operator*=(const Vec2& vec) + { + x *= vec.x; + y *= vec.y; + + return *this; + } + + Vec2 operator*(const Vec2& vec) const + { + Vec2 tmp; + + tmp.x = x * vec.x; + tmp.y = y * vec.y; + + return tmp; + } + + Vec2& operator*=(const T scalar) + { + x *= scalar; + y *= scalar; + + return *this; + } + + Vec2 operator*(const T scalar) const + { + Vec2 tmp; + + tmp.x = x * scalar; + tmp.y = y * scalar; + + return tmp; + } + + bool operator<=(const Vec2& other) const + { + return x <= other.x && y <= other.y; + } + + bool operator<(const Vec2& other) const + { + return x < other.x && y < other.y; + } + + bool operator>=(const Vec2& other) const + { + return x >= other.x && y >= other.y; + } + + bool operator>(const Vec2& other) const + { + return x > other.x && y > other.y; + } + + Vec2 operator-() + { + return {-x, -y}; + } + + T operator[](const UInt_64 index) const + { + switch (index) + { + case 0: + return x; + case 1: + return y; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Vector 3."); + return x; + } + } + + T& operator[](const UInt_64 index) + { + switch (index) + { + case 0: + return x; + case 1: + return y; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Vector 3."); + return x; + } + } + + Vec2 GetAbs() const + { + Vec2 tmp; + + tmp.x = Math::Abs(x); + tmp.y = Math::Abs(y); + + return tmp; + } + + void Abs() + { + x = Math::Abs(x); + y = Math::Abs(y); + } + + /// If positive, the vectors are pointing in the same direction. If negative, the vectors are pointing in opposing directions. + /// If zero, the vectors are perpendicular. + T GetDot(const Vec2& vec) const + { + return x * vec.x + y * vec.y; + } + + T GetAngle(const Vec2& vec) const + { + return Math::ACos(GetDot(vec) / Math::Sqrt(GetMagnitude2() * vec.GetMagnitude2())); + } + + Vec2 GetProjection(const Vec2& length) const + { + return operator*(length.GetDot(*this) / GetMagnitude2()); + } + + Vec2 GetPerpendicular(const Vec2& length) const + { + return length - GetProjection(length); + } + + Vec2 GetReflection(const Vec2& normal) const + { + return operator-(normal * (GetDot(normal) * 2)); + } + + T GetMagnitude() const + { + return Math::Sqrt(x * x + y * y); + } + + T GetMagnitude2() const + { + return x * x + y * y; + } + + T GetDistance(const Vec2& vec) const + { + return Math::Sqrt(Math::Pow(vec.x - x, 2) + Math::Pow(vec.y - y, 2)); + } + + T GetDistance2(const Vec2& vec) const + { + return Math::Pow(vec.x - x, 2) + Math::Pow(vec.y - y, 2); + } + + Vec2 GetNorm() const + { + Vec2 norm; + + T dis = GetMagnitude(); + + norm.x = x / dis; + norm.y = y / dis; + + return norm; + } + + void Norm() + { + T dis = GetMagnitude(); + + x /= dis; + y /= dis; + } + + Vec2 GetRads() const + { + Vec2 tmp; + + tmp.x = Math::Rads(x); + tmp.y = Math::Rads(y); + + return tmp; + } + + void ToRads() + { + x = Math::Rads(x); + y = Math::Rads(y); + } + + Vec2 GetDegr() const + { + Vec2 tmp; + + tmp.x = Math::Degr(x); + tmp.y = Math::Degr(y); + + return tmp; + } + + void ToDegr() + { + x = Math::Degr(x); + y = Math::Degr(y); + } + + static Vec2 Lerp(const Vec2& start, const Vec2& finish, const T t) + { + return start + (finish - start) * t; + } + }; + + typedef Vec2 Vec2_u64; + typedef Vec2 Vec2_s64; + typedef Vec2 Vec2_64; + typedef Vec2 Vec2_u32; + typedef Vec2 Vec2_s32; + typedef Vec2 Vec2_32; + typedef Vec2 Vec2_u16; + typedef Vec2 Vec2_s16; + typedef Vec2 Vec2_16; + typedef Vec2 Vec2_u8; + typedef Vec2 Vec2_s8; + typedef Vec2 Vec2_8; + typedef Vec2 Vec2_f; + typedef Vec2 Vec2_d; +} \ No newline at end of file diff --git a/include/ehs/Vec3.h b/include/ehs/Vec3.h new file mode 100644 index 0000000..1f3e520 --- /dev/null +++ b/include/ehs/Vec3.h @@ -0,0 +1,445 @@ +#pragma once + +#include "Types.h" +#include "UTF.h" +#include "Str.h" +#include "Math.h" +#include "Vec2.h" +#include "Log.h" + +namespace ehs +{ + template + class Vec3 + { + public: + T x; + T y; + T z; + + Vec3(const T scalar = 0) + : x(scalar), y(scalar), z(scalar) + { + } + + Vec3(const T x, const T y, const T z) + : x(x), y(y), z(z) + { + } + + template + Vec3(const Vec2& vec, const T z = 0) + : x((T)vec.x), y((T)vec.y), z(z) + { + } + + template + Vec3(const Vec3& vec) + : x((T)vec.x), y((T)vec.y), z((T)vec.z) + { + } + + template + Vec3& operator=(const Vec2& vec) + { + x = (T)vec.x; + y = (T)vec.y; + z = 0; + + return *this; + } + + template + Vec3& operator=(const Vec3& vec) + { + x = (T)vec.x; + y = (T)vec.y; + z = (T)vec.z; + + return *this; + } + + bool operator==(const Vec3& vec) const + { + return Math::ComCmp(x, vec.x) && Math::ComCmp(y, vec.y) && Math::ComCmp(z, vec.z); + } + + + bool operator!=(const Vec3& vec) const + { + return !Math::ComCmp(z, vec.z) || !Math::ComCmp(y, vec.y) || !Math::ComCmp(z, vec.z); + } + + Vec3 operator+(const Vec3& vec) const + { + Vec3 tmp; + + tmp.x = x + vec.x; + tmp.y = y + vec.y; + tmp.z = z + vec.z; + + return tmp; + } + + Vec3& operator+=(const Vec3& vec) + { + x += vec.x; + y += vec.y; + z += vec.z; + + return *this; + } + + Vec3 operator+(const T scalar) const + { + Vec3 tmp; + + tmp.x = x + scalar; + tmp.y = y + scalar; + tmp.z = z + scalar; + + return tmp; + } + + Vec3& operator+=(const T scalar) + { + x += scalar; + y += scalar; + z += scalar; + + return *this; + } + + Vec3 operator-(const Vec3& vec) const + { + Vec3 tmp; + + tmp.x = x - vec.x; + tmp.y = y - vec.y; + tmp.z = z - vec.z; + + return tmp; + } + + Vec3& operator-=(const Vec3& vec) + { + x -= vec.x; + y -= vec.y; + z -= vec.z; + + return *this; + } + + Vec3 operator-(const T scalar) const + { + Vec3 tmp; + + tmp.x = x - scalar; + tmp.y = y - scalar; + tmp.z = z - scalar; + + return tmp; + } + + Vec3& operator-=(const T scalar) + { + x -= scalar; + y -= scalar; + z -= scalar; + + return *this; + } + + Vec3 operator*(const Vec3& vec) const + { + Vec3 tmp; + + tmp.x = x * vec.x; + tmp.y = y * vec.y; + tmp.z = z * vec.z; + + return tmp; + } + + Vec3& operator*=(const Vec3& vec) + { + x *= vec.x; + y *= vec.y; + z *= vec.z; + + return *this; + } + + Vec3 operator*(const T scalar) const + { + Vec3 tmp; + + tmp.x = x * scalar; + tmp.y = y * scalar; + tmp.z = z * scalar; + + return tmp; + } + + Vec3& operator*=(const T scalar) + { + x *= scalar; + y *= scalar; + z *= scalar; + + return *this; + } + + Vec3 operator/(const Vec3& vec) const + { + Vec3 tmp; + + tmp.x = x / vec.x; + tmp.y = y / vec.y; + tmp.z = z / vec.z; + + return tmp; + } + + Vec3& operator/=(const Vec3& vec) + { + x /= vec.x; + y /= vec.y; + z /= vec.z; + + return *this; + } + + Vec3 operator/(const T scalar) const + { + Vec3 tmp; + + tmp.x = x / scalar; + tmp.y = y / scalar; + tmp.z = z / scalar; + + return tmp; + } + + Vec3& operator/=(const T scalar) + { + x /= scalar; + y /= scalar; + z /= scalar; + + return *this; + } + + bool operator<=(const Vec3& other) const + { + return x <= other.x && y <= other.y && z <= other.z; + } + + bool operator<(const Vec3& other) const + { + return x < other.x && y < other.y && z < other.z; + } + + bool operator>=(const Vec3& other) const + { + return x >= other.x && y >= other.y && z >= other.z; + } + + bool operator>(const Vec3& other) const + { + return x > other.x && y > other.y && z > other.z; + } + + Vec3 operator-() + { + return {-x, -y, -z}; + } + + T operator[](const UInt_64 index) const + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Vector 3."); + return x; + } + } + + T& operator[](const UInt_64 index) + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Vector 3."); + return x; + } + } + + operator Vec2() + { + return Vec2(x, y); + } + + Vec3 GetAbs() const + { + Vec3 absolute; + + absolute.x = Math::Abs(x); + absolute.y = Math::Abs(y); + absolute.z = Math::Abs(z); + + return absolute; + } + + void Abs() + { + x = Math::Abs(x); + y = Math::Abs(y); + z = Math::Abs(z); + } + + Vec3 GetNorm() const + { + Vec3 norm; + + T dis = GetMagnitude(); + + norm.x = x / dis; + norm.y = y / dis; + norm.z = z / dis; + + return norm; + } + + void Norm() + { + T dis = GetMagnitude(); + + x /= dis; + y /= dis; + z /= dis; + } + + Vec3 GetCross(const Vec3& vec) const + { + return Vec3( + y * vec.z - z * vec.y, + z * vec.x - x * vec.z, + x * vec.y - y * vec.x + ); + } + + T GetDot(const Vec3& vec) const + { + return x * vec.x + y * vec.y + z * vec.z; + } + + T GetAngle(const Vec2& vec) const + { + return Math::ACos(GetDot(vec) / Math::Sqrt(GetMagnitude2() * vec.GetMagnitude2())); + } + + Vec2 GetProjection(const Vec2& length) const + { + return operator*(length.GetDot(*this) / GetMagnitude2()); + } + + Vec2 GetPerpendicular(const Vec2& length) const + { + return length - GetProjection(length); + } + + Vec2 GetReflection(const Vec2& normal) const + { + return operator-(normal * (GetDot(normal) * 2)); + } + + T GetMagnitude() const + { + return Math::Sqrt(x * x + y * y + z * z); + } + + T GetMagnitude2() const + { + return x * x + y * y + z * z; + } + + T GetDistance(const Vec3& vec) const + { + return (T)Math::Sqrt(Math::Pow(vec.x - x, 2) + Math::Pow(vec.y - y, 2) + Math::Pow(vec.z - z, 2)); + } + + T GetDistance2(const Vec3& vec) const + { + return static_cast(Math::Pow(vec.x - x, 2) + Math::Pow(vec.y - y, 2) + Math::Pow(vec.z - z, 2)); + } + + Vec3 GetRads() const + { + Vec3 tmp; + + tmp.x = Math::Rads(x); + tmp.y = Math::Rads(y); + tmp.z = Math::Rads(z); + + return tmp; + } + + void ToRads() + { + x = Math::Rads(x); + y = Math::Rads(y); + z = Math::Rads(z); + } + + Vec3 GetDegr() const + { + Vec3 tmp; + + tmp.x = Math::Degr(x); + tmp.y = Math::Degr(y); + tmp.z = Math::Degr(z); + + return tmp; + } + + void ToDegr() + { + x = Math::Degr(x); + y = Math::Degr(y); + z = Math::Degr(z); + } + + static Vec3 Lerp(const Vec3& start, const Vec3& finish, const T t) + { + return start + (finish - start) * t; + } + }; + + typedef Vec3 Vec3_u64; + typedef Vec3 Vec3_s64; + typedef Vec3 Vec3_64; + typedef Vec3 Vec3_u32; + typedef Vec3 Vec3_s32; + typedef Vec3 Vec3_32; + typedef Vec3 Vec3_u16; + typedef Vec3 Vec3_s16; + typedef Vec3 Vec3_16; + typedef Vec3 Vec3_u8; + typedef Vec3 Vec3_s8; + typedef Vec3 Vec3_8; + typedef Vec3 Vec3_f; + typedef Vec3 Vec3_d; +} \ No newline at end of file diff --git a/include/ehs/Vec4.h b/include/ehs/Vec4.h new file mode 100644 index 0000000..8a02e7b --- /dev/null +++ b/include/ehs/Vec4.h @@ -0,0 +1,345 @@ +#pragma once + +#include "Types.h" +#include "UTF.h" +#include "Str.h" +#include "Math.h" +#include "Vec2.h" +#include "Vec3.h" +#include "Log.h" + +namespace ehs +{ + template + class Vec4 + { + public: + T x; + T y; + T z; + T w; + + Vec4(const T scalar = 0) + : x(scalar), y(scalar), z(scalar), w(scalar) + { + } + + Vec4(const T x, const T y, const T z, const T w) + : x(x), y(y), z(z), w(w) + { + } + + template + Vec4(const Vec2& vec, const T z = 0, const T w = 0) + : x((T)vec.x), y((T)vec.y), z(z), w(w) + { + } + + template + Vec4(const Vec3& vec, const T w = 1) + : x((T)vec.x), y((T)vec.y), z((T)vec.z), w(w) + { + } + + template + Vec4(const Vec4& vec) + : x((T)vec.x), y((T)vec.y), z((T)vec.z), w((T)vec.w) + { + } + + template + Vec4& operator=(const Vec2& vec) + { + x = (T)vec.x; + y = (T)vec.y; + z = (T)0; + w = (T)0; + + return*this; + } + + template + Vec4& operator=(const Vec3& vec) + { + x = (T)vec.x; + y = (T)vec.y; + z = (T)vec.z; + w = (T)0; + + return*this; + } + + template + Vec4& operator=(const Vec4& vec) + { + x = (T)vec.x; + y = (T)vec.y; + z = (T)vec.z; + w = (T)vec.w; + + return*this; + } + + bool operator==(const Vec4& vec) + { + return Math::ComCmp(x, vec.x) && Math::ComCmp(y, vec.y) && Math::ComCmp(z, vec.z) && Math::ComCmp(w, vec.w); + } + + bool operator!=(const Vec4& vec) + { + return !Math::ComCmp(z, vec.z) || !Math::ComCmp(y, vec.y) || !Math::ComCmp(z, vec.z) || !Math::ComCmp(w, vec.w); + } + + Vec4& operator+=(const Vec4& vec) + { + x += vec.x; + y += vec.y; + z += vec.z; + w += vec.w; + + return *this; + } + + Vec4 operator+(const Vec4& vec) + { + Vec4 tmp; + + tmp.x = x + vec.x; + tmp.y = y + vec.y; + tmp.z = z + vec.z; + tmp.w = w + vec.w; + + return tmp; + } + + Vec4& operator+=(const T scalar) + { + x += scalar; + y += scalar; + z += scalar; + w += scalar; + + return *this; + } + + Vec4 operator+(const T scalar) + { + Vec4 tmp; + + tmp.x = x + scalar; + tmp.y = y + scalar; + tmp.z = z + scalar; + tmp.w = w + scalar; + + return tmp; + } + + Vec4& operator-=(const Vec4& vec) + { + x -= vec.x; + y -= vec.y; + z -= vec.z; + w -= vec.w; + + return *this; + } + + Vec4 operator-(const Vec4& vec) + { + Vec4 tmp; + + tmp.x = x - vec.x; + tmp.y = y - vec.y; + tmp.z = z - vec.z; + tmp.w = w - vec.w; + + return tmp; + } + + Vec4& operator-=(const T scalar) + { + x -= scalar; + y -= scalar; + z -= scalar; + w -= scalar; + + return *this; + } + + Vec4 operator-(const T scalar) + { + Vec4 tmp; + + tmp.x = x - scalar; + tmp.y = y - scalar; + tmp.z = z - scalar; + tmp.w = w - scalar; + + return tmp; + } + + Vec4& operator*=(const Vec4& vec) + { + x *= vec.x; + y *= vec.y; + z *= vec.z; + w *= vec.w; + + return *this; + } + + Vec4 operator*(const Vec4& vec) + { + Vec4 tmp; + + tmp.x = x * vec.x; + tmp.y = y * vec.y; + tmp.z = z * vec.z; + tmp.w = w * vec.w; + + return tmp; + } + + Vec4& operator*=(const T scalar) + { + x *= scalar; + y *= scalar; + z *= scalar; + w *= scalar; + + return *this; + } + + Vec4 operator*(const T scalar) + { + Vec4 tmp; + + tmp.x = x * scalar; + tmp.y = y * scalar; + tmp.z = z * scalar; + tmp.w = w * scalar; + + return tmp; + } + + Vec4& operator/=(const Vec4& vec) + { + x /= vec.x; + y /= vec.y; + z /= vec.z; + w /= vec.w; + + return *this; + } + + Vec4 operator/(const Vec4& vec) + { + Vec4 tmp; + + tmp.x = x / vec.x; + tmp.y = y / vec.y; + tmp.z = z / vec.z; + tmp.w = w / vec.w; + + return tmp; + } + + Vec4& operator/=(const T scalar) + { + x /= scalar; + y /= scalar; + z /= scalar; + w /= scalar; + + return *this; + } + + Vec4 operator/(const T scalar) + { + Vec4 tmp; + + tmp.x = x / scalar; + tmp.y = y / scalar; + tmp.z = z / scalar; + tmp.w = w / scalar; + + return tmp; + } + + T operator[](const UInt_64 index) const + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Vector 4."); + return x; + } + } + + T& operator[](const UInt_64 index) + { + switch (index) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + EHS_LOG_INT("Error", 0, "Index of, \"" + Str_8::FromNum(index) + "\" is out of range for a Vector 4."); + return x; + } + } + + operator Vec3() + { + return Vec3(x, y, z); + } + + operator Vec2() + { + return Vec2(x, y); + } + + T GetDotProduct(const Vec4& vec) const + { + return x * vec.x + y * vec.y + z * vec.z + w * vec.w; + } + + T GetMagnitude() const + { + return Math::Sqrt(x * x + y * y + z * z + w * w); + } + + T GetMagnitude2() const + { + return x * x + y * y + z * z + w * w; + } + }; + + typedef Vec4 Vec4_u64; + typedef Vec4 Vec4_s64; + typedef Vec4 Vec4_64; + typedef Vec4 Vec4_u32; + typedef Vec4 Vec4_s32; + typedef Vec4 Vec4_32; + typedef Vec4 Vec4_u16; + typedef Vec4 Vec4_s16; + typedef Vec4 Vec4_16; + typedef Vec4 Vec4_u8; + typedef Vec4 Vec4_s8; + typedef Vec4 Vec4_8; + typedef Vec4 Vec4_f; + typedef Vec4 Vec4_d; +} \ No newline at end of file diff --git a/include/ehs/Vector.h b/include/ehs/Vector.h new file mode 100644 index 0000000..946bc70 --- /dev/null +++ b/include/ehs/Vector.h @@ -0,0 +1,637 @@ +#pragma once + +#include "BaseObj.h" +#include "Types.h" +#include "Util.h" + +#include +#include + +namespace ehs +{ + /// An array with extra memory pre-allocated for fast pushes. + /// @tparam T Array data type to use. + /// @tparam N Number data type to use. + /// @note If extra memory is set to five then each time that memory is filled it will add five extra. + template + class Vector : public BaseObj + { + protected: + N rawSize; + N size; + N stride; + T* data; + + public: + /// Frees any data created on the heap. + ~Vector() override + { + delete[] data; + } + + /// Default members initialization. + Vector() + : rawSize(0), size(0), stride(5), data(nullptr) + { + AddType("Vector"); + } + + /// Initializes members for pre-allocated memory to write to later. + /// @param [in] size The size of memory to pre-allocate. + /// @param [in] stride The stride size of memory to pre-allocate. + Vector(const N size, const N stride) + : rawSize(size + stride), size(size), stride(stride), data(new T[rawSize]) + { + AddType("Vector"); + } + + /// Initializes this vector with an initializer list object. + /// @param [in] list The given initializer list. + /// @param [in] stride The extra amount of memory to allocate. + Vector(std::initializer_list list, const N stride = 5) + : rawSize(0), size(list.size()), stride(stride), data(nullptr) + { + AddType("Vector"); + + if (stride) + { + rawSize = list.size() / stride * stride; + if (list.size() % stride) + rawSize += stride; + } + else + { + rawSize = list.size(); + } + + data = new T[rawSize]; + + N i = 0; + for (auto v = list.begin(); v != list.end(); ++v) + data[i++] = std::move(*v); + } + + /// Initializes members with given C-style array. + /// @param [in] data The C-style array. + /// @param [in] size The size of the given C-style array. + /// @param [in] stride The size of the extra memory allocated. + Vector(const T* data, const N size, const N stride) + : rawSize(0), size(size), stride(stride), data(nullptr) + { + AddType("Vector"); + + if (stride) + { + rawSize = size / stride * stride; + if (size % stride) + rawSize += stride; + } + else + { + rawSize = size; + } + + data = new T[rawSize]; + + for (N i = 0; i < size; ++i) + this->data[i] = data[i]; + } + + /// Copies all members from the given vector object. + /// @param [in] vec The vector object to copy from. + Vector(const Vector& vec) + : BaseObj(vec), rawSize(vec.rawSize), size(vec.size), stride(vec.stride), data(new T[rawSize]) + { + for (N i = 0; i < size; ++i) + data[i] = vec.data[i]; + } + + Vector(Vector&& vec) noexcept + : BaseObj((BaseObj&&)vec), rawSize(vec.rawSize), size(vec.size), stride(vec.stride), data(vec.data) + { + vec.rawSize = 0; + vec.size = 0; + vec.stride = 0; + vec.data = nullptr; + } + + /// Copies all members from the given vector object. + /// @param [in] vec The vector object to copy from. + /// @returns The vector that has been assigned to. + Vector& operator=(const Vector& vec) + { + if (this == &vec) + return *this; + + BaseObj::operator=(vec); + + rawSize = vec.rawSize; + size = vec.size; + stride = vec.stride; + + delete[] data; + data = new T[rawSize]; + + for (N i = 0; i < size; ++i) + data[i] = vec.data[i]; + + return *this; + } + + Vector& operator=(Vector&& vec) noexcept + { + if (this == &vec) + return *this; + + BaseObj::operator=((BaseObj&&)vec); + + rawSize = vec.rawSize; + size = vec.size; + stride = vec.stride; + delete[] data; + data = vec.data; + + vec.rawSize = 0; + vec.size = 0; + vec.stride = 0; + vec.data = nullptr; + + return *this; + } + + bool operator==(const Vector& in) const + { + if (size != in.size) + return false; + + return Util::Compare(data, in.data, size); + } + + bool operator!=(const Vector& in) const + { + if (size != in.size) + return true; + + return !Util::Compare(data, in.data, size); + } + + /// Adds a given initializer list at the end of the vector. + /// @param [in] value The given initializer list to push to the end of the vector. + Vector& operator+=(std::initializer_list value) + { + if (size + value.size() >= rawSize) + { + if (stride) + { + rawSize = (size + value.size()) / stride * stride; + if ((size + value.size()) % stride) + rawSize += stride; + } + else + { + rawSize = size + value.size(); + } + + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(std::move(data[i])); + + delete[] data; + data = result; + } + + for (auto v = value.begin(); v != value.end(); ++v) + data[size++] = std::move(*v); + + return *this; + } + + /// Adds a given value at the end of the vector. + /// @param [in] value The given value to push to the end of the vector. + Vector& operator+=(const T value) + { + if (size + 1 >= rawSize) + { + if (stride) + rawSize = size + stride; + else + rawSize = size + 1; + + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + + data[size++] = std::move(value); + + return *this; + } + + /// Retrieves the raw C-style array from casting an array object. + operator T* () const + { + return data; + } + + /// Retrieves the size of the vector object including the extra memory allocated. + /// @returns The raw size. + N RawSize() const + { + return rawSize; + } + + /// Retrieves the size of the array not including the extra memory allocated. + /// @returns The size. + N Size() const + { + return size; + } + + /// Retrieves the size of the extra memory allocated. + /// @returns The extra size. + N Stride() const + { + return stride; + } + + N End() const + { + return size ? size - 1 : size; + } + + /// Copies a vector object with offsets. + /// @param [in] dstOffset The offset index to copy the given vector object to. + /// @param [in] src The given vector object. + /// @param [in] srcOffset The offset index from the given vector object to copy from. + void Copy(const N dstOffset, Vector src, const N srcOffset = 0) + { + for (N i = 0; i < src.Size() - srcOffset; ++i) + data[i + dstOffset] = std::move(src[i + srcOffset]); + } + + /// Copies a C-style array with offsets. + /// @param [in] dstOffset The offset index to copy the given C-style array to. + /// @param [in] src The given C-style array. + /// @param [in] size The size from the given C-style array to copy. + void Copy(const N dstOffset, const T* src, const N inSize) + { + if (dstOffset + inSize > size) + return; + + for (N i = 0; i < inSize; ++i) + data[i + dstOffset] = src[i]; + } + + /// Swaps two values in the vector. + /// @param [in] a The first index to swap with. + /// @param [in] b The second index to swap with. + void Swap(N a, N b) + { + T tmp = std::move(data[a]); + + data[a] = std::move(data[b]); + data[b] = std::move(tmp); + } + + /// Inserts a value at a specified index that is available. + /// @param [in] index The index to insert the value at. + /// @param [in] value The given value to insert. + void Insert(const N index, const T value) + { + N newSize = 0; + if (index > size - 1) + newSize = size + ((index + 1) - size); + else + newSize = size + 1; + + if (newSize >= rawSize) + { + if (stride) + rawSize += newSize + stride; + else + rawSize = newSize; + + T* result = new T[rawSize]; + + for (N i = 0; i < index; ++i) + result[i] = std::move(data[i]); + + result[index] = std::move(value); + + for (N i = index; i < size; ++i) + result[i + 1] = std::move(data[i]); + + delete[] data; + data = result; + } + else + { + for (N i = index; i < size; ++i) + data[i + 1] = std::move(data[i]); + + data[index] = std::move(value); + } + + size = newSize; + } + + /// Removes a value at a specified index. + /// @param [in] index The index to remove the value at. + /// @returns The removed data. + T Remove(const N index) + { + T popped = {}; + + if (!size || index >= size) + return popped; + + popped = std::move(data[index]); + + if (!stride) + { + rawSize = size - 1; + T* result = new T[rawSize]; + + for (N i = 0; i < index; ++i) + result[i] = std::move(data[i]); + + for (N i = index + 1; i < size; ++i) + result[i - 1] = std::move(data[i]); + + delete[] data; + data = result; + } + else if (rawSize - stride && size - 1 <= rawSize - stride) + { + rawSize -= stride; + T* result = new T[rawSize]; + + for (N i = 0; i < index; ++i) + result[i] = std::move(data[i]); + + for (N i = index + 1; i < size; ++i) + result[i - 1] = std::move(data[i]); + + delete[] data; + data = result; + } + else + { + for (N i = index + 1; i < size; ++i) + data[i - 1] = std::move(data[i]); + } + + --size; + + return popped; + } + + /// Adds a given C-style array at the end of the vector. + /// @param [in] value The given C-style array to push to the end of the vector. + /// @param [in] size The size of the given C-style array. + void Push(const T* const value, const N size) + { + if (this->size + size >= rawSize) + { + if (stride) + { + rawSize = (this->size + size()) / stride * stride; + if ((this->size + size) % stride) + rawSize += stride; + } + else + { + rawSize = this->size + size; + } + + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + + for (N i = 0; i < size; ++i) + data[this->size + i] = value[i]; + + this->size += size; + } + + /// Adds a given vector object at the end of the vector. + /// @param [in] value The given vector object to push to the end of the vector. + void Push(Vector value) + { + if (size + value.size >= rawSize) + { + if (stride) + { + rawSize = (size + value.size) / stride * stride; + if ((size + value.size) % stride) + rawSize += stride; + } + else + { + rawSize = size + value.size; + } + + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + + for (N i = 0; i < value.size; ++i) + data[size + i] = std::move(value.data[i]); + + size += value.size; + } + + /// Adds a given initializer at the end of the vector. + /// @param [in] value The given initializer list to push to the end of the vector. + void Push(std::initializer_list value) + { + if (size + value.size() >= rawSize) + { + if (stride) + { + rawSize = (size + value.size()) / stride * stride; + if ((size + value.size()) % stride) + rawSize += stride; + } + else + { + rawSize = size + value.size(); + } + + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + + for (auto v = value.begin(); v != value.end(); ++v) + data[size++] = std::move(*v); + } + + /// Adds a given value at the end of the vector. + /// @param [in] value The given value to push to the end of the vector. + void Push(T value) + { + if (size + 1 >= rawSize) + { + if (stride) + rawSize += stride; + else + rawSize = size + 1; + + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + + data[size++] = std::move(value); + } + + /// Much like the stack it pops a value at the end of the vector. + /// @returns The removed value. + T Pop() + { + T popped = {}; + + if (!size) + return popped; + + popped = std::move(data[--size]); + + if (!stride) + { + rawSize = size; + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + else if (rawSize - stride && size < rawSize - stride) + { + rawSize -= stride; + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + + return popped; + } + + /// Will swap the value at the given index with the value at the end of the vector and pops it. + /// @param [in] index The index of the value to swap with. + /// @returns The removed value. + T Pop(const N index) + { + if (!size) + return {}; + + N lastIndex = size - 1; + + if (index < lastIndex) + Swap(index, lastIndex); + + return Pop(); + } + + /// Resizes the vector while keeping its alignment. + /// @param [in] newSize The size to change to. + void Resize(const N newSize) + { + if (newSize == size) + return; + + if (stride) + { + rawSize = newSize / stride * stride; + if (newSize % stride) + rawSize += stride; + } + else + { + rawSize = newSize; + } + + T* result = new T[rawSize]; + + for (N i = 0; i < size && i < newSize; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + size = newSize; + } + + /// Removes any extra allocated memory. + void ExactSize() + { + if (!stride) + return; + + stride = 0; + + if (size) + { + rawSize = size; + T* result = new T[rawSize]; + + for (N i = 0; i < size; ++i) + result[i] = std::move(data[i]); + + delete[] data; + data = result; + } + else + { + rawSize = 0; + delete[] data; + data = nullptr; + } + } + + /// Clears all values in the vector object. + void Clear() + { + if (!size) + return; + + rawSize = stride; + size = 0; + + delete[] data; + + if (rawSize) + data = new T[rawSize]; + else + data = nullptr; + } + }; +} \ No newline at end of file diff --git a/include/ehs/Version.h b/include/ehs/Version.h new file mode 100644 index 0000000..3c8f2b6 --- /dev/null +++ b/include/ehs/Version.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Types.h" + +namespace ehs +{ + /// A helper class for storing version major, minor and patch. + class Version + { + public: + UInt_32 major; + UInt_32 minor; + UInt_32 patch; + + /// Default members initialization. + Version(); + + /// Initializes members with given major, minor and patch. + /// @param [in] major The major version. + /// @param [in] minor The minor version. + /// @param [in] patch The patch version. + Version(const UInt_32 major, const UInt_32 minor, const UInt_32 patch); + + /// Copies all members from the given version object. + /// @param [in] version The version object to copy from. + Version(const Version& version); + + /// Copies all members from the given version object. + /// @param [in] version The version object to copy from. + /// @returns The version object that has been assigned to. + Version& operator=(const Version& version); + + bool operator==(const Version& version) const; + + bool operator!=(const Version& version) const; + + unsigned int operator[](const UInt_32 i) const; + + unsigned int& operator[](const UInt_32 i); + }; +} \ No newline at end of file diff --git a/include/ehs/WkPtr.h b/include/ehs/WkPtr.h new file mode 100644 index 0000000..9b48e96 --- /dev/null +++ b/include/ehs/WkPtr.h @@ -0,0 +1,134 @@ +#pragma once + +#include "ShdPtr.h" + +namespace ehs +{ + template + class WkPtr + { + private: + T* data; + + public: + ~WkPtr() + { + } + + WkPtr() + : data(nullptr) + { + } + + WkPtr(T* data) + : data(data) + { + } + + WkPtr(ShdPtr& shdPtr) + : data(shdPtr.GetData()) + { + } + + WkPtr(WkPtr&& wkPtr) noexcept + : data(wkPtr.data) + { + wkPtr.data = nullptr; + } + + WkPtr(const WkPtr& wkPtr) + : data(wkPtr.data) + { + } + + WkPtr& operator=(const ShdPtr& shdPtr) + { + data = shdPtr.GetData(); + + return *this; + } + + WkPtr& operator=(WkPtr&& wkPtr) noexcept + { + if (this == &wkPtr) + return *this; + + data = wkPtr.data; + + wkPtr.data = nullptr; + + return *this; + } + + WkPtr& operator=(const WkPtr& wkPtr) noexcept + { + if (this == &wkPtr) + return *this; + + data = wkPtr.data; + + return *this; + } + + bool operator==(ShdPtr& shdPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data == shdPtr.GetData(); + } + + bool operator==(const WkPtr& wkPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data == wkPtr.data; + } + + bool operator==(T* inPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data == inPtr; + } + + bool operator!=(ShdPtr& shdPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data != shdPtr.GetData(); + } + + bool operator!=(const WkPtr& wkPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data != wkPtr.data; + } + + bool operator!=(T* inPtr) + { + if (!HasPtrData(data)) + data = nullptr; + + return data != inPtr; + } + + void Release() + { + data = nullptr; + } + + T* GetData() + { + if (!HasPtrData(data)) + data = nullptr; + + return data; + } + }; +} \ No newline at end of file diff --git a/include/ehs/database/DVar.h b/include/ehs/database/DVar.h new file mode 100644 index 0000000..40551cd --- /dev/null +++ b/include/ehs/database/DVar.h @@ -0,0 +1,20 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/BaseObj.h" +#include "ehs/Str.h" + +namespace ehs +{ + enum class DType : UInt_8 + { + + }; + + class DVar : public BaseObj + { + private: + Str_8 id; + UInt_64 hashId; + }; +} diff --git a/include/ehs/io/BaseFile.h b/include/ehs/io/BaseFile.h new file mode 100644 index 0000000..cadbbb8 --- /dev/null +++ b/include/ehs/io/BaseFile.h @@ -0,0 +1,260 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Vector.h" +#include "ehs/Array.h" +#include "ehs/Serializer.h" + +namespace ehs +{ + enum class Mode + { + READ, + WRITE, + READ_WRITE + }; + + enum class Disposition + { + CREATE_PERSISTENT, + CREATE, + OPEN_PERSISTENT, + OPEN, + TRUNCATE + }; + + /// A cross-platform wrapper class that handles native file input/output. + class BaseFile + { + protected: + Str_8 path; + Str_8 fullName; + Str_8 name; + Str_8 extension; + Mode mode; + Disposition disposition; + + public: + /// Frees all native handles. + virtual ~BaseFile() = default; + + /// Default members initialization. + BaseFile(); + + /// Initializes members with the given data. + /// @param [in] filePath The file path to read or write to. + /// @param [in] mode The mode when accessing the file. + /// @param [in] disposition How to handle the file. + BaseFile(const Str_8& filePath, const Mode mode, const Disposition disposition); + + BaseFile(BaseFile&& file) noexcept; + + /// Copy constructor. + /// @param [in] file The file object to copy from. + BaseFile(const BaseFile& file) = default; + + BaseFile& operator=(BaseFile&& file) noexcept; + + /// Copy operator. + /// @param [in] file The file object to copy from. + BaseFile& operator=(const BaseFile& file) = default; + + virtual operator const Byte*() const = 0; + + virtual operator Byte*() = 0; + + /// Uninitializes the native handle. + /// @param [in] raiseLog Whether or not to raise a log if already uninitialized. Mostly for deconstructor. + virtual void Release() = 0; + + virtual bool IsMapped() const = 0; + + virtual UInt_64 MapSize() const = 0; + + virtual void Map(const UInt_64 offset, const UInt_64 size) = 0; + + virtual void Unmap() = 0; + + virtual void FlushMap() = 0; + + /// Writes a C-style byte array to the file. + /// @param [in] data The C-style byte array to write to the file. + /// @param [in] size The size of the given C-style byte array. + virtual UInt_64 Write(const Byte* const data, const UInt_64 size) = 0; + + /// Writes a C-style string to the file. + /// @tparam T The character data type to use. + /// @param [in] str The C-style string to write to the file. + /// @param [in] size The size of the given C-style string. + void WriteStr_32(const Char_32* const str, const UInt_64 size); + + /// Writes a string to the file. + /// @tparam T The character data type to use. + /// @tparam N The data type to use for numbers. + /// @param [in] str The string to write to the file. + void WriteStr_32(const Str_32& str); + + /// Writes a C-style string to the file. + /// @tparam T The character data type to use. + /// @param [in] str The C-style string to write to the file. + /// @param [in] size The size of the given C-style string. + void WriteStr_16(const Char_16* const str, const UInt_64 size); + + /// Writes a string to the file. + /// @tparam T The character data type to use. + /// @tparam N The data type to use for numbers. + /// @param [in] str The string to write to the file. + void WriteStr_16(const Str_16& str); + + /// Writes a C-style string to the file. + /// @tparam T The character data type to use. + /// @param [in] str The C-style string to write to the file. + /// @param [in] size The size of the given C-style string. + void WriteStr_8(const Char_8* const str, const UInt_64 size); + + /// Writes a string to the file. + /// @tparam T The character data type to use. + /// @tparam N The data type to use for numbers. + /// @param [in] str The string to write to the file. + void WriteStr_8(const Str_8& str); + + /// Writes a vector to the file. + /// @tparam N The data type to use for numbers. + /// @param [in] vec The vector to write to the file. + void WriteVector(const Vector& vec); + + /// Writes an array to the file. + /// @tparam N The data type to use for numbers. + /// @param [in] arr The array to write to the file. + void WriteArray(const Array& arr); + + /// Writes a serializer to the file. + /// @tparam N The data type to use for numbers. + /// @param [in] ser The serializer to write to the file. + void WriteSerializer_64(const Serializer& ser); + + /// Writes a serializer to the file. + /// @tparam N The data type to use for numbers. + /// @param [in] ser The serializer to write to the file. + void WriteSerializer_32(const Serializer& ser); + + /// Reads data from the file as a C-style byte array. + /// @param [out] buffer The buffer to store the data read from the file. + /// @param [in] size The size of the given buffer and how much data to read. + virtual UInt_64 Read(Byte* const buffer, const UInt_64 size) = 0; + + /// Reads data from the file as a C-style string. + /// @tparam T The character data type to use. + /// @param [out] buffer The buffer to store the data read from the file. + /// @param [in] size The size of the given buffer and how much data to read. + void ReadStr_32(Char_32* const buffer, UInt_64& size); + + /// Reads data from the file as a string. + /// @tparam T The character data type to use. + /// @tparam N The data type to use for numbers. + /// @param [in] size The size of the buffer and how much data to read. + /// @returns The resulting string. + Str_32 ReadStr_32(const UInt_64 size); + + /// Reads data from the file as a C-style string. + /// @tparam T The character data type to use. + /// @param [out] buffer The buffer to store the data read from the file. + /// @param [in] size The size of the given buffer and how much data to read. + void ReadStr_16(Char_16* const buffer, UInt_64& size); + + /// Reads data from the file as a string. + /// @tparam T The character data type to use. + /// @tparam N The data type to use for numbers. + /// @param [in] size The size of the buffer and how much data to read. + /// @returns The resulting string. + Str_16 ReadStr_16(const UInt_64 size); + + /// Reads data from the file as a C-style string. + /// @tparam T The character data type to use. + /// @param [out] buffer The buffer to store the data read from the file. + /// @param [in] size The size of the given buffer and how much data to read. + void ReadStr_8(Char_8* const buffer, UInt_64& size); + + /// Reads data from the file as a string. + /// @tparam T The character data type to use. + /// @tparam N The data type to use for numbers. + /// @param [in] size The size of the buffer and how much data to read. + /// @returns The resulting string. + Str_8 ReadStr_8(const UInt_64 size); + + /// Reads data from the file as a vector. + /// @tparam N The data type to use for numbers. + /// @param [in] size The size of the buffer and how much data to read. + /// @returns The resulting vector. + Vector ReadVector(const UInt_64 size); + + /// Reads data from the file as an array. + /// @tparam N The data type to use for numbers. + /// @param [in] size The size of the buffer and how much data to read. + /// @returns The resulting array. + Array ReadArray(const UInt_64 size); + + /// Reads data from the file as a serializer. + /// @tparam N The data type to use for numbers. + /// @param[in] end The Endianness of the data in the file. + /// @param[in] size The size of the buffer and how much data to read. + /// @returns The resulting serializer. + Serializer ReadSerializer_64(const Endianness end, const UInt_64 size); + + /// Reads data from the file as a serializer. + /// @tparam N The data type to use for numbers. + /// @param[in] end The Endianness of the data in the file. + /// @param[in] size The size of the buffer and how much data to read. + /// @returns The resulting serializer. + Serializer ReadSerializer_32(const Endianness end, const UInt_32 size); + + virtual void Seek(UInt_64 index) = 0; + + virtual void SeekBeginning() = 0; + + virtual void SeekEnd() = 0; + + virtual void Truncate(const UInt_64 size) = 0; + + /// Retrieves the size of the file. + /// @returns The result. + virtual UInt_64 Size() const = 0; + + Str_8 GetPath() const; + + Str_8 GetFullName() const; + + Str_8 GetName() const; + + Str_8 GetExtension() const; + + /// Retrieves whether or not this object is valid. + /// @returns The result. + virtual bool IsValid() const = 0; + + static void Rename_32(const Str_32& filePath, const Str_32& newName); + + static void Rename_16(const Str_16& filePath, const Str_16& newName); + + static void Rename_8(const Str_8& filePath, const Str_8& newName); + + static Str_32 ParseFullName_32(const Str_32& filePath); + + static Str_16 ParseFullName_16(const Str_16& filePath); + + static Str_8 ParseFullName_8(const Str_8& filePath); + + static Str_32 ParseName_32(const Str_32& filePath); + + static Str_16 ParseName_16(const Str_16& filePath); + + static Str_8 ParseName_8(const Str_8& filePath); + + static Str_32 ParseExt_32(const Str_32& filePath); + + static Str_16 ParseExt_16(const Str_16& filePath); + + static Str_8 ParseExt_8(const Str_8& filePath); + }; +} diff --git a/include/ehs/io/BaseFileMonitor.h b/include/ehs/io/BaseFileMonitor.h new file mode 100644 index 0000000..d8c56c0 --- /dev/null +++ b/include/ehs/io/BaseFileMonitor.h @@ -0,0 +1,46 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +#define EHS_FE_NONE 0x00 +#define EHS_FE_MODIFIED 0x01 +#define EHS_FE_DELETED 0x02 +#define EHS_FE_MOVED 0x04 +#define EHS_FE_OPENED 0x08 + +namespace ehs +{ + class BaseFileMonitor + { + protected: + Str_8 filePath; + + public: + virtual ~BaseFileMonitor() = default; + + BaseFileMonitor() = default; + + BaseFileMonitor(Str_8 filePath); + + BaseFileMonitor(BaseFileMonitor&& fm) noexcept; + + BaseFileMonitor(const BaseFileMonitor& fm); + + BaseFileMonitor& operator=(BaseFileMonitor&& fm) noexcept; + + BaseFileMonitor& operator=(const BaseFileMonitor& fm); + + virtual void Initialize() = 0; + + virtual void Release() = 0; + + virtual UInt_8 Poll() = 0; + + Str_8 GetFilePath() const; + + bool IsValid() const; + + virtual bool IsInitialized() const = 0; + }; +} \ No newline at end of file diff --git a/include/ehs/io/BaseWindow.h b/include/ehs/io/BaseWindow.h new file mode 100644 index 0000000..21466a9 --- /dev/null +++ b/include/ehs/io/BaseWindow.h @@ -0,0 +1,114 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Vec2.h" +#include "ehs/Rect.h" +#include "ehs/io/hid/Input.h" + +namespace ehs +{ + enum class WindowState : UInt_8 + { + NONE, + FULLSCREEN + }; + + enum class CursorImg : UInt_8 + { + DEFAULT, + I_BEAM + }; + + class BaseWindow + { + protected: + bool created; + bool focused; + Vec2_s32 cursorPos; + bool cursorVisible; + bool cursorConstrained; + WindowState state; + InputHandler ih; + + public: + virtual ~BaseWindow() = default; + + BaseWindow(); + + BaseWindow(const BaseWindow& win); + + BaseWindow& operator=(const BaseWindow& win); + + virtual void Create_32(const Str_32& title, const Vec2_s32& pos, const Vec2_u32 scale) = 0; + + virtual void Create_16(const Str_16& title, const Vec2_s32& pos, const Vec2_u32 scale) = 0; + + virtual void Create_8(const Str_8& title, const Vec2_s32& pos, const Vec2_u32 scale) = 0; + + virtual void OnCreated() = 0; + + virtual void Close() = 0; + + virtual void Show() = 0; + + virtual void Hide() = 0; + + bool IsCreated() const; + + virtual bool Poll(); + + bool HasFocus() const; + + void EnableResizing(const bool enable); + + bool IsResizable() const; + + /// Gets the cursors position on the desktop in pixels. + /// @param [in] relative Whether the position should be relative to the windows client. + /// @returns The current value. + Vec2_s32 GetCursorPos() const; + + /// Shows the cursor on the window. + /// @param [in] toggle The new status. + virtual void ShowCursor(bool toggle) = 0; + + /// Checks whether the cursor is shown. + /// @returns The current status. + bool IsCursorVisible() const; + + virtual void ConstrainCursor(const bool constrain) = 0; + + bool IsCursorConstrained() const; + + WindowState GetState() const; + + const InputHandler* GetInputHandler() const; + + virtual void SetTitle_32(const Str_32& newTitle) = 0; + + virtual Str_32 GetTitle_32() const = 0; + + virtual void SetTitle_16(const Str_16& newTitle) = 0; + + virtual Str_16 GetTitle_16() const = 0; + + virtual void SetTitle_8(const Str_8& newTitle) = 0; + + virtual Str_8 GetTitle_8() const = 0; + + virtual void SetPos(const Vec2_s32& newPos) = 0; + + virtual Vec2_s32 GetPos() const = 0; + + virtual void SetScale(const Vec2_u32& newScale) = 0; + + virtual Vec2_u32 GetScale() const = 0; + + virtual Serializer GetClipboard() = 0; + + virtual void SetClipboard(Serializer data) = 0; + + virtual void SetCursorImg(const CursorImg img) = 0; + }; +} diff --git a/include/ehs/io/COM.h b/include/ehs/io/COM.h new file mode 100644 index 0000000..eb3b15b --- /dev/null +++ b/include/ehs/io/COM.h @@ -0,0 +1,55 @@ +#pragma once + +#include "ehs/EHS.h" + +namespace ehs +{ + enum class Parity : UInt_8 + { + NONE, + ODD, + EVEN, + MARK, + SPACE + }; + + enum class StopBits : UInt_8 + { + ONE, + ONE_POINT_FIVE, + TWO + }; + + class COM + { + private: + UInt_8 port; + UInt_32 baudRate; + UInt_8 byteSize; + Parity parity; + StopBits stopBits; + void* hdl; + bool initialized; + + public: + COM(); + + COM(const UInt_8 port, const UInt_32 baudRate, const UInt_8 byteSize, const Parity parity, const StopBits stopBits); + + COM(const COM& com) = default; + + void Initialize(); + + void UnInitialize(); + + UInt_32 Wait(); + + void Transmit(const Char_8 data); + + UInt_32 Send(const Char_8* data, const UInt_32 size); + + UInt_32 Receive(const Char_8* data, const UInt_32 size); + + void Flush(); + }; +} \ No newline at end of file diff --git a/include/ehs/io/Console.h b/include/ehs/io/Console.h new file mode 100644 index 0000000..ff4a783 --- /dev/null +++ b/include/ehs/io/Console.h @@ -0,0 +1,115 @@ +#pragma once + +#include "ehs/Str.h" +#include "ehs/UTF.h" +#include "ehs/Array.h" + +namespace ehs +{ + #if defined(EHS_OS_WINDOWS) + typedef void* ConsoleHdl; + #elif defined(EHS_OS_LINUX) + typedef int ConsoleHdl; + #endif + + class Console + { + private: + static ConsoleHdl hdlOut; + static ConsoleHdl hdlIn; + + #if defined(EHS_OS_WINDOWS) + static bool isConsole; + #endif + + public: + static void Attach(); + + /// Creates a console using standard input and output. + /// @param [in] inputRequired Whether or not input is required from the console. + static bool Create(); + + /// Frees the current console being used. + static void Free(); + + static bool CanRead(); + + static bool CanWrite(); + + /// Writes to console using UTF32. + /// @param [in] str The text to write to the console. + /// @param [in] newLine To make a new line after the given text. + /// @warning Has to convert from UTF32 to UTF16 for the Windows API. + static void Write_32(const Str_32& str, const bool newLine = true); + + /// Writes to console using UTF16. + /// @param [in] str The text to write to the console. + /// @param [in] newLine To make a new line after the given text. + static void Write_16(const Str_16& str, const bool newLine = true); + + /// Writes to console using UTF8. + /// @param [in] str The text to write to the console. + /// @param [in] newLine To make a new line after the given text. + /// @warning Has to convert from UTF8 to UTF16 for the Windows API. + static void Write_8(const Str_8& str, const bool newLine = true); + + /// Reads from the console using UTF32. + /// @returns The text the user wrote to the console. + /// @warning Has to convert from UTF16 to UTF32 for the Windows API. + static Str_32 Read_32(const UInt_64 bufferSize = 1024); + + /// Reads from the console using UTF16. + /// @returns The text the user wrote to the console. + static Str_16 Read_16(const UInt_64 bufferSize = 1024); + + /// Reads from the console using UTF8. + /// @returns The text the user wrote to the console. + /// @warning Has to convert from UTF8 to UTF16 for the Windows API. + static Str_8 Read_8(const UInt_64 bufferSize = 1024); + + /// Clears the console. + static void Clear(); + + /// Changes the console's title. + /// @param [in] title The text to change the title to. + /// @warning Has to convert from UTF32 to UTF16 for the Windows API. + static void SetTitle_32(const Str_32& title); + + /// Changes the console's title. + /// @param [in] title The text to change the title to. + static void SetTitle_16(const Str_16& title); + + /// Changes the console's title. + /// @param [in] title The text to change the title to. + /// @warning Has to convert from UTF8 to UTF16 for the Windows API. + static void SetTitle_8(const Str_8& title); + + /// Retrieves the console's title in UTF32. + /// @returns The console's title. + /// @warning Has to convert from UTF16 to UTF32 for the Windows API. + static Str_32 GetTitle_32(); + + /// Retrieves the console's title in UTF16. + /// @returns The console's title. + static Str_16 GetTitle_16(); + + /// Retrieves the console's title in UTF8. + /// @returns The console's title. + /// @warning Has to convert from UTF16 to UTF8 for the Windows API. + static Str_8 GetTitle_8(); + + /// Retrieves the string used when executing the end application through a command line interface in UTF32. + /// @returns The result. + static Vector GetArgs_32(const UInt_64 bufferSize = 1024); + + /// Retrieves the string used when executing the end application through a command line interface in UTF16. + /// @returns The result. + static Vector GetArgs_16(const UInt_64 bufferSize = 1024); + + /// Retrieves the string used when executing the end application through a command line interface in UTF8. + /// @returns The result. + static Vector GetArgs_8(const UInt_64 bufferSize = 1024); + + //static void* GetHandle(); + }; +} \ No newline at end of file diff --git a/include/ehs/io/File.h b/include/ehs/io/File.h new file mode 100644 index 0000000..d957f51 --- /dev/null +++ b/include/ehs/io/File.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ehs/EHS.h" + +#if defined(EHS_OS_WINDOWS) +#include "File_W32.h" +#elif defined(EHS_OS_LINUX) +#include "File_UNX.h" +#endif \ No newline at end of file diff --git a/include/ehs/io/FileMonitor.h b/include/ehs/io/FileMonitor.h new file mode 100644 index 0000000..54da73a --- /dev/null +++ b/include/ehs/io/FileMonitor.h @@ -0,0 +1,7 @@ +#pragma once + +#if defined(EHS_OS_WINDOWS) + #include "FileMonitor_W32.h" +#elif defined(EHS_OS_LINUX) + #include "FileMonitor_UNX.h" +#endif \ No newline at end of file diff --git a/include/ehs/io/FileMonitor_UNX.h b/include/ehs/io/FileMonitor_UNX.h new file mode 100644 index 0000000..bcdf8eb --- /dev/null +++ b/include/ehs/io/FileMonitor_UNX.h @@ -0,0 +1,37 @@ +#pragma once + +#include "ehs/EHS.h" +#include "BaseFileMonitor.h" + +namespace ehs +{ + class FileMonitor : public BaseFileMonitor + { + private: + int hdl; + int wd; + + public: + ~FileMonitor(); + + FileMonitor(); + + FileMonitor(Str_8 filePath); + + FileMonitor(FileMonitor&& fm) noexcept; + + FileMonitor(const FileMonitor& fm); + + FileMonitor& operator=(FileMonitor&& fm) noexcept; + + FileMonitor& operator=(const FileMonitor& fm); + + void Initialize() override; + + void Release() override; + + UInt_8 Poll() override; + + bool IsInitialized() const override; + }; +} \ No newline at end of file diff --git a/include/ehs/io/FileMonitor_W32.h b/include/ehs/io/FileMonitor_W32.h new file mode 100644 index 0000000..3d29424 --- /dev/null +++ b/include/ehs/io/FileMonitor_W32.h @@ -0,0 +1,37 @@ +#pragma once + +#include "ehs/EHS.h" +#include "BaseFileMonitor.h" + +namespace ehs +{ + class FileMonitor final : public BaseFileMonitor + { + private: + Handle hdl; + FILETIME time; + + public: + ~FileMonitor() override; + + FileMonitor(); + + FileMonitor(Str_8 filePath); + + FileMonitor(FileMonitor&& fm) noexcept; + + FileMonitor(const FileMonitor& fm); + + FileMonitor& operator=(FileMonitor&& fm) noexcept; + + FileMonitor& operator=(const FileMonitor& fm); + + void Initialize() override; + + void Release() override; + + UInt_8 Poll() override; + + bool IsInitialized() const override; + }; +} \ No newline at end of file diff --git a/include/ehs/io/File_UNX.h b/include/ehs/io/File_UNX.h new file mode 100644 index 0000000..6e10aa7 --- /dev/null +++ b/include/ehs/io/File_UNX.h @@ -0,0 +1,73 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/UTF.h" +#include "ehs/Vector.h" +#include "ehs/Array.h" +#include "ehs/Serializer.h" +#include "BaseFile.h" + +namespace ehs +{ + class File : public BaseFile + { + private: + int hdl; + void* map; + UInt_64 mapSize; + + public: + ~File() override; + + File(); + + File(const Str_8& filePath, const Mode mode, const Disposition disposition); + + File(File&& file) noexcept; + + File(const File& file); + + File& operator=(File&& file) noexcept; + + File& operator=(const File& file); + + operator const Byte*() const override; + + operator Byte*() override; + + void Release() override; + + bool IsMapped() const override; + + UInt_64 MapSize() const override; + + void Map(const UInt_64 offset, const UInt_64 size) override; + + void Unmap() override; + + void FlushMap() override; + + UInt_64 Write(const Byte* const data, const UInt_64 size) override; + + UInt_64 Read(Byte* const buffer, const UInt_64 size) override; + + void Seek(UInt_64 index) override; + + void SeekBeginning() override; + + void SeekEnd() override; + + void Truncate(const UInt_64 size) override; + + UInt_64 Size() const override; + + bool IsValid() const override; + + static void Rename_32(const Str_32& filePath, const Str_32& newName); + + static void Rename_16(const Str_16& filePath, const Str_16& newName); + + static void Rename_8(const Str_8& filePath, const Str_8& newName); + }; +} diff --git a/include/ehs/io/File_W32.h b/include/ehs/io/File_W32.h new file mode 100644 index 0000000..6f38812 --- /dev/null +++ b/include/ehs/io/File_W32.h @@ -0,0 +1,74 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/UTF.h" +#include "ehs/Vector.h" +#include "ehs/Array.h" +#include "ehs/Serializer.h" +#include "BaseFile.h" + +namespace ehs +{ + class File : public BaseFile + { + private: + HANDLE hdl; + HANDLE map; + Byte* view; + UInt_64 viewSize; + + public: + ~File() override; + + File(); + + File(const Str_8& filePath, const Mode mode, const Disposition disposition); + + File(File&& file) noexcept; + + File(const File& file); + + File& operator=(File&& file) noexcept; + + File& operator=(const File& file); + + operator const Byte*() const override; + + operator Byte*() override; + + void Release() override; + + bool IsMapped() const override; + + UInt_64 MapSize() const override; + + void Map(const UInt_64 offset, const UInt_64 size) override; + + void Unmap() override; + + void FlushMap() override; + + UInt_64 Write(const Byte* const data, const UInt_64 size) override; + + UInt_64 Read(Byte* const buffer, const UInt_64 size) override; + + void Seek(UInt_64 index) override; + + void SeekBeginning() override; + + void SeekEnd() override; + + void Truncate(const UInt_64 size) override; + + UInt_64 Size() const override; + + bool IsValid() const override; + + static void Rename_32(const Str_32& filePath, const Str_32& newName); + + static void Rename_16(const Str_16& filePath, const Str_16& newName); + + static void Rename_8(const Str_8& filePath, const Str_8& newName); + }; +} diff --git a/include/ehs/io/FontAtlas.h b/include/ehs/io/FontAtlas.h new file mode 100644 index 0000000..73b50c0 --- /dev/null +++ b/include/ehs/io/FontAtlas.h @@ -0,0 +1,69 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Array.h" + +#include "Glyph.h" +#include "ehs/Anchor.h" +#include "ehs/io/img/Img.h" +#include "ehs/io/model/Mesh.h" + +namespace ehs +{ + class FontAtlas : public BaseObj + { + private: + UInt_64 hashId; + Str_8 id; + UInt_64 glyphScale; + Array glyphs; + Vec2_u64 resolution; + UInt_64 size; + Byte* atlas; + + public: + ~FontAtlas() override; + + FontAtlas(); + + FontAtlas(const Str_8& filePath); + + FontAtlas(FontAtlas&& fa) noexcept; + + FontAtlas(const FontAtlas& fa); + + FontAtlas& operator=(FontAtlas&& fa) noexcept; + + FontAtlas& operator=(const FontAtlas& fa); + + operator Byte*() const; + + void Release(); + + UInt_64 GetHashId() const; + + Str_8 GetId() const; + + UInt_64 GetGlyphScale() const; + + const Array& GetGlyphs() const; + + Glyph GetGlyph(Char_32 code) const; + + Vec2_u64 GetResolution() const; + + UInt_64 GetSize() const; + + bool IsValid() const; + + Vec2_f CalculateSize(const Str_8& text) const; + + float CalculateWidth(const Str_8& text) const; + + float CalculateHeight(const Str_8& text) const; + + UInt_64 CalculateIndexAtPoint(const Str_8& text, const Vec2_f& point) const; + + Mesh Generate(Anchor anchor, const Str_8& text) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/Glyph.h b/include/ehs/io/Glyph.h new file mode 100644 index 0000000..50dec7d --- /dev/null +++ b/include/ehs/io/Glyph.h @@ -0,0 +1,59 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Vec2.h" +#include "ehs/Rect.h" +#include "ehs/Serializer.h" + +namespace ehs +{ + class Glyph + { + private: + Char_32 code; + Vec2_u64 pos; + Vec2_u64 scale; + Rect_f uv; + Vec2_64 bearing; + Vec2_64 advance; + + public: + Glyph(); + + Glyph(Serializer<>& ser); + + Glyph(const Char_32 code); + + Glyph(const Glyph& glyph); + + Glyph& operator=(const Glyph& glyph); + + bool operator==(const Glyph& glyph) const; + + bool operator!=(const Glyph& glyph) const; + + Char_32 GetCode() const; + + void SetPos(const Vec2_u64& newPos); + + Vec2_u64 GetPos() const; + + void SetScale(const Vec2_u64& newScale); + + Vec2_u64 GetScale() const; + + void SetUV(const Rect_f& newUV); + + Rect_f GetUV() const; + + void SetBearing(const Vec2_64& newBearing); + + Vec2_32 GetBearing() const; + + void SetAdvance(const Vec2_64& newAdvance); + + Vec2_32 GetAdvance() const; + + void Serialize(Serializer<>& ser) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/RIFF.h b/include/ehs/io/RIFF.h new file mode 100644 index 0000000..bcef57a --- /dev/null +++ b/include/ehs/io/RIFF.h @@ -0,0 +1,38 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Vector.h" +#include "ehs/Serializer.h" +#include "RIFF_Chunk.h" + +namespace ehs +{ + class RIFF + { + private: + Str_8 type; + Vector chunks; + + public: + RIFF() = default; + + RIFF(const Str_8& filePath); + + RIFF(Serializer<>& data); + + RIFF(const RIFF& riff) = default; + + operator const RIFF_Chunk*() const; + + Str_8 GetType() const; + + bool HasChunk(const UInt_64 hashId) const; + + bool HasChunk(const Str_8& id) const; + + RIFF_Chunk GetChunk(const UInt_64 hashId) const; + + RIFF_Chunk GetChunk(const Str_8& id) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/RIFF_Chunk.h b/include/ehs/io/RIFF_Chunk.h new file mode 100644 index 0000000..a5eea11 --- /dev/null +++ b/include/ehs/io/RIFF_Chunk.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Serializer.h" + +namespace ehs +{ + class RIFF_Chunk + { + private: + Str_8 id; + UInt_64 hashId; + Serializer<> data; + + public: + RIFF_Chunk(); + + RIFF_Chunk(const Str_8& id, const Serializer<>& data); + + RIFF_Chunk(const RIFF_Chunk& chunk); + + RIFF_Chunk& operator=(const RIFF_Chunk& chunk); + + Str_8 GetId() const; + + UInt_64 GetHashId() const; + + Serializer<> GetData() const; + + bool IsValid() const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/Resource.h b/include/ehs/io/Resource.h new file mode 100644 index 0000000..23e6136 --- /dev/null +++ b/include/ehs/io/Resource.h @@ -0,0 +1,45 @@ +#pragma once + +#include "ehs/Types.h" +#include "ehs/Str.h" +#include "ehs/Vec2.h" +#include "ehs/BaseObj.h" + +namespace ehs +{ + class Resource : public BaseObj + { + private: + ehs::UInt_64 hashId; + ehs::Str_8 id; + + public: + Resource(); + + Resource(ehs::Str_8 id); + + Resource(Resource&& rsrc) noexcept; + + Resource(const Resource& rsrc); + + Resource& operator=(Resource&& rsrc) noexcept; + + Resource& operator=(const Resource& rsrc); + + bool operator==(ehs::UInt_64 otherHashId) const; + + bool operator!=(ehs::UInt_64 otherHashId) const; + + virtual void Release(); + + void SetId(ehs::Str_8 newId); + + ehs::UInt_64 GetHashId() const; + + ehs::Str_8 GetId() const; + + virtual bool IsValid() const; + + Resource* Clone() const override; + }; +} \ No newline at end of file diff --git a/include/ehs/io/Window.h b/include/ehs/io/Window.h new file mode 100644 index 0000000..5f383ce --- /dev/null +++ b/include/ehs/io/Window.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ehs/system/OS.h" + +#if defined(EHS_OS_WINDOWS) + #include "Window_W32.h" +#elif defined(EHS_OS_LINUX) + #if defined(EHS_WS_XCB) + #include "Window_XCB.h" + #elif defined(EHS_WS_WAYLAND) + #include "Window_Way.h" + #endif +#endif \ No newline at end of file diff --git a/include/ehs/io/Window_W32.h b/include/ehs/io/Window_W32.h new file mode 100644 index 0000000..af81557 --- /dev/null +++ b/include/ehs/io/Window_W32.h @@ -0,0 +1,127 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Array.h" +#include "ehs/Str.h" +#include "ehs/Vec4.h" +#include "BaseWindow.h" +#include "HID/InputHandler.h" + +#define WM_HIDE (WM_APP + 1) +#define WM_SHOW (WM_APP + 2) +#define WM_HIDE_CURSOR (WM_APP + 3) +#define WM_SHOW_CURSOR (WM_APP + 4) + +namespace ehs +{ + class Window : public BaseWindow + { + private: + UInt_32 owner; + HINSTANCE instance; + HWND hdl; + + static Array windows; + + static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + public: + virtual ~Window() override; + + Window(); + + Window(const Window &win); + + Window& operator=(const Window &win); + + virtual bool Poll() override; + + ///Creates the native window. + void Create_32(const Str_32& title, const Vec2_s32& pos, const Vec2_u32 scale) override; + + ///Creates the native window. + void Create_16(const Str_16& title, const Vec2_s32& pos, const Vec2_u32 scale) override; + + ///Creates the native window. + void Create_8(const Str_8& title, const Vec2_s32& pos, const Vec2_u32 scale) override; + + ///Uses an already existing window to render an overlay. + void Use(HWND windowHdl); + + ///Closes the window. + virtual void Close() override; + + ///Shows the window. + void Show() override; + + ///Hides the window. + void Hide() override; + + void SetTitle_32(const Str_32& title) override; + + Str_32 GetTitle_32(); + + void SetTitle_16(const Str_16& title) override; + + Str_16 GetTitle_16(); + + void SetTitle_8(const Str_8& title) override; + + Str_8 GetTitle_8(); + + void SetIcon(const Str_8& filePath); + + /// Sets the windows client scale. + /// @param [in] w The width in pixels. + /// @param [in] h The height in pixels. + void SetClientSize(const Vec2& size); + + Vec2 GetClientSize(); + + /// Gets the windows native handle for the operating system or other native tasks. + /// @returns The window's native handle. + HWND GetHdl() const; + + /// Retrieves the first window handle that has been created using this library. + /// @returns The window's native handle. + static HWND GetAvailableHdl(); + + HINSTANCE GetInst() const; + + /// Toggles whether the window updates and renders. + /// @param [in] toggle The new status. + void ToggleEnabled(bool toggle); + + /// Checks whether the window updates and renders. + /// @returns The current status. + bool IsEnabled(); + + /// Sets the windows position on the desktop. + /// @param [in] x The x axis in pixels. + /// @param [in] y The y axis in pixels. + void SetPos(int x, int y); + + /// Gets the windows current position on the desktop. + /// @returns The current value. + Vec2 GetPos(); + + virtual void OnResized(const Vec2& newSize); + + /// Sets the windows scale which includes the border. + /// @param [in] w The width in pixels. + /// @param [in] h The height in pixels. + void SetSize(int w, int h); + + /// Gets the windows current scale. + /// @returns The current value. + Vec2 GetSize(); + + void ShowCursor(bool toggle) override; + + void ConstrainCursor(const bool toggle) override; + + protected: + void SendMsg(const UINT msg, const WPARAM wParam, const LPARAM lParam); + + }; +} \ No newline at end of file diff --git a/include/ehs/io/Window_Way.h b/include/ehs/io/Window_Way.h new file mode 100644 index 0000000..83a96b1 --- /dev/null +++ b/include/ehs/io/Window_Way.h @@ -0,0 +1,80 @@ +#pragma once + +#include "BaseWindow.h" + +#include +#include "xdg-shell-client-protocol.h" + +namespace ehs +{ + class Window : public BaseWindow + { + private: + wl_display* display; + wl_registry *registry; + wl_compositor* compositor; + wl_surface* surface; + xdg_wm_base *xdgShell; + xdg_surface *xdgSurface; + xdg_toplevel *xdgToplevel; + + static void SurfaceConfigure(void *data, xdg_surface *xdg_surface, UInt_32 serial); + + static void ShellPing(void *data, xdg_wm_base *shell, UInt_32 serial); + + static void RegistryHandler(void *data, wl_registry *registry, UInt_32 id, const char *interface, UInt_32 version); + + static void RegistryRemoved(void *data, wl_registry *registry, UInt_32 id); + + public: + ~Window() override; + + Window(); + + void Create_32(const Str_32& title, const Vec2_s32& pos, const Vec2_u32 scale) override; + + void Create_16(const Str_16& title, const Vec2_s32& pos, const Vec2_u32 scale) override; + + void Create_8(const Str_8& title, const Vec2_s32& pos, const Vec2_u32 scale) override; + + void OnCreated() override; + + void Close() override; + + void Show() override; + + void Hide() override; + + bool Poll() override; + + void ShowCursor(bool toggle) override; + + void ConstrainCursor(const bool constrain) override; + + void SetTitle_32(const Str_32& newTitle) override; + + Str_32 GetTitle_32() const override; + + void SetTitle_16(const Str_16& newTitle) override; + + Str_16 GetTitle_16() const override; + + void SetTitle_8(const Str_8& newTitle) override; + + Str_8 GetTitle_8() const override; + + void SetPos(const Vec2_s32& newPos) override; + + Vec2_s32 GetPos() const override; + + void SetScale(const Vec2_u32& newScale) override; + + Vec2_u32 GetScale() const override; + + Serializer GetClipboard() override; + + void SetClipboard(Serializer data) override; + + void SetCursorImg(const CursorImg img) override; + }; +} \ No newline at end of file diff --git a/include/ehs/io/Window_XCB.h b/include/ehs/io/Window_XCB.h new file mode 100644 index 0000000..6ce9784 --- /dev/null +++ b/include/ehs/io/Window_XCB.h @@ -0,0 +1,94 @@ +#pragma once + +#include "BaseWindow.h" +#include "File.h" + +#include +#include + +namespace ehs +{ + class Window : public BaseWindow + { + protected: + friend class Input; + + xcb_connection_t* server; + xcb_screen_t* screen; + xcb_window_t hdl; + xcb_atom_t masks[2]; + UInt_8 extOpCode; + Vector events; + Serializer clipboard; + + public: + ~Window() override; + + Window(); + + Window(Window&& win) noexcept; + + Window(const Window& win); + + Window& operator=(Window&& win) noexcept; + + Window& operator=(const Window& win); + + void Create_32(const Str_32& title, const Vec2_s32& pos, Vec2_u32 scale) override; + + void Create_16(const Str_16& title, const Vec2_s32& pos, Vec2_u32 scale) override; + + void Create_8(const Str_8& title, const Vec2_s32& pos, Vec2_u32 scale) override; + + void Close() override; + + void Show() override; + + void Hide() override; + + bool Poll() override; + + void ShowCursor(bool toggle) override; + + void ConstrainCursor(bool constrain) override; + + void SetTitle_32(const Str_32& newTitle) override; + + Str_32 GetTitle_32() const override; + + void SetTitle_16(const Str_16& newTitle) override; + + Str_16 GetTitle_16() const override; + + void SetTitle_8(const Str_8& newTitle) override; + + Str_8 GetTitle_8() const override; + + void SetPos(const Vec2_s32& newPos) override; + + Vec2_s32 GetPos() const override; + + void SetScale(const Vec2_u32& newScale) override; + + Vec2_u32 GetScale() const override; + + Serializer GetClipboard() override; + + void SetClipboard(Serializer data) override; + + void SetCursorImg(CursorImg img) override; + + xcb_connection_t* GetServer(); + + private: + xcb_generic_event_t* RetrieveEvent(); + + xcb_atom_t RetrieveAtom(bool create, const Str_8& name) const; + + xcb_get_property_reply_t* RetrieveProp(xcb_atom_t prop, xcb_atom_t type) const; + + void QueryPrimaryDevices(); + + Str_8 QueryDeviceName(UInt_16 id); + }; +} diff --git a/include/ehs/io/audio/Audio.h b/include/ehs/io/audio/Audio.h new file mode 100644 index 0000000..556d7b1 --- /dev/null +++ b/include/ehs/io/audio/Audio.h @@ -0,0 +1,204 @@ +#pragma once + +#include "ehs/Types.h" +#include "ehs/DataType.h" +#include "ehs/Str.h" +#include "ehs/Serializer.h" +#include "ehs/Vector.h" +#include "ehs/Array.h" +#include "ehs/io/Resource.h" +#include "AudioCodec.h" + +namespace ehs +{ + class Audio : public Resource + { + private: + static Array codecs; + UInt_64 sampleRate; + DataType dataType; + UInt_8 byteDepth; + UInt_8 channels; + UInt_64 frames; + float length; + Byte* data; + Byte* peak; + + public: + static bool HasCodec(UInt_64 hashExt); + + static bool HasCodec(const Str_8& ext); + + static bool AddCodec(AudioCodec codec); + + static const AudioCodec* GetCodec(UInt_64 hashExt); + + static const AudioCodec* GetCodec(const Str_8& ext); + + ~Audio() override; + + Audio(); + + Audio(Str_8 id); + + Audio(Str_8 id, UInt_64 sampleRate, DataType dataType, UInt_8 channels, UInt_64 frames, const Byte* data); + + Audio(Str_8 id, UInt_64 sampleRate, DataType dataType, UInt_8 channels, const Serializer& data); + + Audio(Str_8 id, UInt_64 sampleRate, DataType dataType, UInt_8 channels, const Vector& data); + + Audio(Str_8 id, UInt_64 sampleRate, DataType dataType, UInt_8 channels, const Array& data); + + Audio(Str_8 id, UInt_64 sampleRate, DataType dataType, UInt_8 channels, UInt_64 frames); + + Audio(Audio&& audio) noexcept; + + Audio(const Audio& audio); + + Audio& operator=(Audio&& audio) noexcept; + + Audio& operator=(const Audio& audio); + + operator const Byte*() const; + + operator Byte*(); + + void Release() override; + + UInt_64 GetSampleRate() const; + + DataType GetDataType() const; + + UInt_8 GetByteDepth() const; + + UInt_8 GetBitDepth() const; + + UInt_8 GetChannels() const; + + UInt_64 GetFrameCount() const; + + UInt_64 GetSampleCount() const; + + UInt_64 GetSize() const; + + float GetLength() const; + + Array FrameAsMono(UInt_64 frameIndex) const; + + Array FrameAsStereo(UInt_64 frameIndex) const; + + Array FrameAsFive_One(UInt_64 frameIndex) const; + + Array FrameAsSeven_One(UInt_64 frameIndex) const; + + SInt_8 SampleAsSInt_8(UInt_64 sampleIndex) const; + + SInt_16 SampleAsSInt_16(UInt_64 sampleIndex) const; + + float SampleAsFloat(UInt_64 sampleIndex) const; + + SInt_32 SampleAsSInt_32(UInt_64 sampleIndex) const; + + SInt_64 SampleAsSInt_64(UInt_64 sampleIndex) const; + + SInt_8 PeakAsSInt_8() const; + + SInt_16 PeakAsSInt_16() const; + + float PeakAsFloat() const; + + SInt_32 PeakAsSInt_32() const; + + SInt_64 PeakAsSInt_64() const; + + void SetPeak(UInt_64 size, const Byte* newPeak); + + const Byte* GetPeak() const; + + void ToDataType(DataType newDataType); + + Audio GetAsDataType(DataType newDataType) const; + + void ToChannels(UInt_8 newChannels); + + Audio GetAsChannels(UInt_8 newChannels) const; + + bool ToFile(const Str_8& filePath) const; + + static Audio FromFile(const Str_8& filePath); + + static Audio* FromFile_Heap(const Str_8& filePath); + + static Audio FromFile(const Str_8& filePath, DataType required); + + static Audio* FromFile_Heap(const Str_8& filePath, DataType required); + + static Audio FromData(Str_8 id, const Str_8& ext, Serializer& data); + + private: + void ToMono(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Mono_to_Stereo(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Five_One_to_Stereo(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Seven_One_to_Stereo(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Mono_to_Five_One(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Stereo_to_Five_One(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Seven_One_to_Five_One(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Mono_to_Seven_One(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Stereo_to_Seven_One(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + void Five_One_to_Seven_One(UInt_64 newFrameCount, Byte* newData, UInt_64 frameOffset) const; + + // To SInt_8 + void SInt_16_to_SInt_8(Byte* newData, Byte* newPeak) const; + + void Float_to_SInt_8(Byte* newData, Byte* newPeak) const; + + void SInt_32_to_SInt_8(Byte* newData, Byte* newPeak) const; + + void SInt_64_to_SInt_8(Byte* newData, Byte* newPeak) const; + + // To SInt_16 + void SInt_8_to_SInt_16(Byte* newData, Byte* newPeak) const; + + void Float_to_SInt_16(Byte* newData, Byte* newPeak) const; + + void SInt_32_to_SInt_16(Byte* newData, Byte* newPeak) const; + + void SInt_64_to_SInt_16(Byte* newData, Byte* newPeak) const; + + // To Float + void SInt_8_to_Float(Byte* newData, Byte* newPeak) const; + + void SInt_16_to_Float(Byte* newData, Byte* newPeak) const; + + void SInt_32_to_Float(Byte* newData, Byte* newPeak) const; + + void SInt_64_to_Float(Byte* newData, Byte* newPeak) const; + + // To SInt_32 + void SInt_8_to_SInt_32(Byte* newData, Byte* newPeak) const; + + void SInt_16_to_SInt_32(Byte* newData, Byte* newPeak) const; + + void Float_to_SInt_32(Byte* newData, Byte* newPeak) const; + + void SInt_64_to_SInt_32(Byte* newData, Byte* newPeak) const; + + // To SInt_64 + void SInt_8_to_SInt_64(Byte* newData, Byte* newPeak) const; + + void SInt_16_to_SInt_64(Byte* newData, Byte* newPeak) const; + + void Float_to_SInt_64(Byte* newData, Byte* newPeak) const; + + void SInt_32_to_SInt_64(Byte* newData, Byte* newPeak) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/audio/AudioCodec.h b/include/ehs/io/audio/AudioCodec.h new file mode 100644 index 0000000..d3df153 --- /dev/null +++ b/include/ehs/io/audio/AudioCodec.h @@ -0,0 +1,48 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/io/File.h" + +namespace ehs +{ + class Audio; + + class AudioCodec + { + private: + Str_8 id; + UInt_64 hashExt; + Str_8 ext; + Endianness endianness; + bool (*encodeCb)(const AudioCodec* const, Serializer&, const Audio*); + bool (*decodeCb)(const AudioCodec* const, Serializer&, Audio*); + + public: + AudioCodec(); + + AudioCodec(Str_8 id, Str_8 ext, const Endianness end, + bool (*encodeCb)(const AudioCodec* const, Serializer&, const Audio*), + bool (*decodeCb)(const AudioCodec* const, Serializer&, Audio*)); + + AudioCodec(AudioCodec&& codec) noexcept; + + AudioCodec(const AudioCodec& codec); + + AudioCodec& operator=(AudioCodec&& codec) noexcept; + + AudioCodec& operator=(const AudioCodec& codec); + + Str_8 GetId() const; + + UInt_64 GetHashExt() const; + + Str_8 GetExt() const; + + Endianness GetEndianness() const; + + bool Encode(Serializer& out, const Audio* in) const; + + bool Decode(Serializer& in, Audio* out) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/audio/AudioDevice.h b/include/ehs/io/audio/AudioDevice.h new file mode 100644 index 0000000..f570427 --- /dev/null +++ b/include/ehs/io/audio/AudioDevice.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ehs/EHS.h" + +#if defined(EHS_OS_WINDOWS) +#include "AudioDevice_W32.h" +#elif defined(EHS_OS_LINUX) +#include "AudioDevice_ALSA.h" +#endif \ No newline at end of file diff --git a/include/ehs/io/audio/AudioDevice_ALSA.h b/include/ehs/io/audio/AudioDevice_ALSA.h new file mode 100644 index 0000000..6a2341e --- /dev/null +++ b/include/ehs/io/audio/AudioDevice_ALSA.h @@ -0,0 +1,46 @@ +#pragma once + +#include "ehs/EHS.h" +#include "BaseAudioDevice.h" + +#include + +namespace ehs +{ + class AudioDevice : public BaseAudioDevice + { + private: + snd_pcm_t* hdl; + + public: + ~AudioDevice() override; + + AudioDevice(); + + AudioDevice(AudioDevice&& device) noexcept; + + AudioDevice(const AudioDevice& device); + + AudioDevice& operator=(AudioDevice&& device) noexcept; + + AudioDevice& operator=(const AudioDevice& device); + + void Release() override; + + void OpenStream() override; + + void CloseStream() override; + + UInt_64 GetAvailFrames() const override; + + Byte* Map(UInt_64* offset, UInt_64* frames) override; + + void UnMap(const UInt_64 offset, const UInt_64 frames) override; + + bool IsValid() const override; + + static AudioDevice GetDefault(const AudioDeviceType type); + + static Array Get(const AudioDeviceType type, const AudioDeviceState state); + }; +} diff --git a/include/ehs/io/audio/AudioDevice_W32.h b/include/ehs/io/audio/AudioDevice_W32.h new file mode 100644 index 0000000..f13c84a --- /dev/null +++ b/include/ehs/io/audio/AudioDevice_W32.h @@ -0,0 +1,66 @@ +#pragma once + +#include "ehs/EHS.h" +#include "BaseAudioDevice.h" + +#include +#include +#include +#include + +struct IMMDevice; + +namespace ehs +{ + class AudioDevice : public BaseAudioDevice + { + private: + IMMDevice* hdl; + IAudioClient* client; + IAudioRenderClient* playbackClient; + IAudioCaptureClient* captureClient; + + public: + ~AudioDevice() override; + + AudioDevice(); + + AudioDevice(AudioDevice&& device) noexcept; + + AudioDevice(const AudioDevice& device); + + AudioDevice& operator=(AudioDevice&& device) noexcept; + + AudioDevice& operator=(const AudioDevice& device); + + void Release() override; + + void OpenStream() override; + + void CloseStream() override; + + UInt_64 GetAvailFrames() const override; + + Byte* Map(UInt_64* offset, UInt_64* frames) override; + + void UnMap(const UInt_64 offset, const UInt_64 frames) override; + + Str_32 GetInterfaceName_32() const; + + Str_16 GetInterfaceName_16() const; + + Str_8 GetInterfaceName_8() const; + + Str_32 GetName_32() const; + + Str_16 GetName_16() const; + + Str_8 GetName_8() const; + + bool IsValid() const override; + + static AudioDevice GetDefault(const AudioDeviceType type); + + static Array Get(const AudioDeviceType type, const AudioDeviceState state); + }; +} diff --git a/include/ehs/io/audio/BaseAudioDevice.h b/include/ehs/io/audio/BaseAudioDevice.h new file mode 100644 index 0000000..e2cde25 --- /dev/null +++ b/include/ehs/io/audio/BaseAudioDevice.h @@ -0,0 +1,105 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Vector.h" +#include "ehs/Array.h" +#include "ehs/DataType.h" + +namespace ehs +{ + enum class AudioDeviceType + { + OUTPUT = 0x0, + INPUT = 0x1, + ALL = 0x2 + }; + + enum class AudioDeviceState + { + ACTIVE = 0x1, + DISABLED = 0x2, + NOT_PRESENT = 0x4, + UNPLUGGED = 0x8 + }; + + class BaseAudioDevice + { + protected: + AudioDeviceType type; + DataType dataType; + UInt_16 bitDepth; + UInt_32 sampleRate; + UInt_32 channels; + UInt_32 period; + UInt_32 latency; + UInt_64 maxFrames; + bool streaming; + + public: + virtual ~BaseAudioDevice() = default; + + BaseAudioDevice(); + + BaseAudioDevice(const BaseAudioDevice& device); + + BaseAudioDevice& operator=(const BaseAudioDevice& device); + + virtual void Release(); + + virtual void OpenStream(); + + virtual void CloseStream(); + + virtual UInt_64 GetAvailFrames() const; + + virtual Byte* Map(UInt_64* offset, UInt_64* frames); + + virtual void UnMap(const UInt_64 offset, const UInt_64 frames); + + AudioDeviceType GetType() const; + + void SetDataType(const DataType newDataType); + + DataType GetDataType() const; + + UInt_8 GetByteDepth() const; + + UInt_16 GetBitDepth() const; + + void SetSampleRate(const UInt_32 newSampleRate); + + UInt_32 GetSampleRate() const; + + void SetChannels(const UInt_32 newChannels); + + UInt_16 GetChannels() const; + + UInt_32 GetFrameSize() const; + + void SetPeriod(const UInt_32 newPeriod); + + UInt_32 GetPeriod() const; + + void SetLatency(const UInt_32 newLatency); + + UInt_32 GetLatency() const; + + UInt_64 GetMaxFrames() const; + + bool IsStreaming() const; + + virtual bool IsValid() const; + + /// Retrieves the default audio input/output device. + /// @param [in] type The audio device type to retrieve. + /// @param [out] device The default audio device. + static BaseAudioDevice GetDefault(const AudioDeviceType type); + + /// Retrieves a list of audio input/output devices. + /// @param [in] type The audio device type to retrieve. + /// @param [in] state The audio device state to retrieve. + /// @param [out] devices The list of audio devices. + static Array Get(const AudioDeviceType type, const AudioDeviceState state); + }; +} \ No newline at end of file diff --git a/include/ehs/io/hid/Button.h b/include/ehs/io/hid/Button.h new file mode 100644 index 0000000..f830837 --- /dev/null +++ b/include/ehs/io/hid/Button.h @@ -0,0 +1,31 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +namespace ehs +{ + class Button + { + private: + Str_8 name; + UInt_32 hash; + + public: + Button(); + + Button(const Str_8& name); + + Button(const Button& key); + + Button& operator=(const Button& key); + + bool operator==(const Button& key) const; + + bool operator!=(const Button& key) const; + + Str_8 GetName() const; + + UInt_32 GetHash() const; + }; +} diff --git a/include/ehs/io/hid/ButtonState.h b/include/ehs/io/hid/ButtonState.h new file mode 100644 index 0000000..3d07107 --- /dev/null +++ b/include/ehs/io/hid/ButtonState.h @@ -0,0 +1,55 @@ +#pragma once + +#include "ehs/EHS.h" +#include "Button.h" + +namespace ehs +{ + enum class State + { + JUST_RELEASED, + RELEASED, + PRESSED, + TOUCHED + }; + + class ButtonState + { + private: + Button button; + State state; + bool pressed; + float threshold; + + public: + ButtonState(); + + ButtonState(const Button& button, const State state); + + ButtonState(const ButtonState& bs); + + ButtonState& operator=(const ButtonState& bs); + + bool operator==(const Button& other) const; + + bool operator!=(const Button& other) const; + + bool operator==(const State otherState) const; + + bool operator!=(const State otherState) const; + + Button GetButton() const; + + void SetState(State newState); + + State GetState() const; + + void SetPressed(bool value); + + bool IsPressed() const; + + void SetThreshold(const float newThreshold); + + float GetThreshold() const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/hid/HID.h b/include/ehs/io/hid/HID.h new file mode 100644 index 0000000..68800dc --- /dev/null +++ b/include/ehs/io/hid/HID.h @@ -0,0 +1,91 @@ +#pragma once + +#include "ehs/Array.h" +#include "ButtonState.h" + +#define EHS_HID_UNKNOWN 0 + +namespace ehs +{ + class HID + { + protected: + UInt_8 type; + UInt_64 hashName; + Str_8 name; + UInt_64 id; + Array states; + + public: + HID(); + + HID(const UInt_8 type, Str_8 name, const UInt_64 id); + + HID(HID&& hid) noexcept; + + HID(const HID& hid); + + HID& operator=(HID&& hid) noexcept; + + HID& operator=(const HID& hid); + + bool operator==(const HID& other) const; + + bool operator!=(const HID& other) const; + + bool operator==(const UInt_64 otherId) const; + + bool operator!=(const UInt_64 otherId) const; + + virtual void Poll(); + + UInt_8 GetType() const; + + Str_8 GetName() const; + + UInt_64 GetId() const; + + void ReleaseAll(); + + Vector GetAllTouched() const; + + const ButtonState* IsTouched(const Button& button) const; + + const ButtonState* IsTouched() const; + + Vector GetAllDown() const; + + const ButtonState* IsDown(const Button& button) const; + + const ButtonState* IsDown() const; + + Vector GetAllJustReleased() const; + + const ButtonState* IsJustReleased(const Button& button) const; + + const ButtonState* IsJustReleased() const; + + Vector GetAllUp() const; + + const ButtonState* IsUp(const Button& button) const; + + const ButtonState* IsUp() const; + + void ButtonDown(const Button& button); + + void ButtonUp(const Button& button); + + const ButtonState* GetState(const Button& button) const; + + bool IsValid() const; + + virtual HID* Clone() const; + + private: + bool HasState(const Button& button) const; + + bool AddState(const ButtonState& state); + + ButtonState* GetState(const Button& button); + }; +} \ No newline at end of file diff --git a/include/ehs/io/hid/Input.h b/include/ehs/io/hid/Input.h new file mode 100644 index 0000000..7d4f6da --- /dev/null +++ b/include/ehs/io/hid/Input.h @@ -0,0 +1,46 @@ +#pragma once + +#include "ehs/Array.h" +#include "ehs/Serializer.h" +#include "InputHandler.h" + +namespace ehs +{ + class Input + { + private: + Array handlers; + bool initalized; + + public: + ~Input(); + + Input(); + + Input(Input&& input) noexcept; + + Input(const Input& input); + + Input& operator=(Input&& input) noexcept; + + Input& operator=(const Input& input); + + void Initialize(); + + void Release(); + + void Poll(); + + bool HasHandler(const UInt_64 hashId) const; + + bool HasHandler(const Str_8& id) const; + + bool AddHandler(InputHandler* handler); + + const InputHandler* GetHandler(const UInt_64 hashId) const; + + const InputHandler* GetHandler(const Str_8& id) const; + + bool IsInitialized() const; + }; +} diff --git a/include/ehs/io/hid/InputHandler.h b/include/ehs/io/hid/InputHandler.h new file mode 100644 index 0000000..9a6b749 --- /dev/null +++ b/include/ehs/io/hid/InputHandler.h @@ -0,0 +1,58 @@ +#pragma once + +#include "ehs/Array.h" +#include "HID.h" + +namespace ehs +{ + class InputHandler + { + private: + UInt_64 hashId; + Str_8 id; + + protected: + Array devices; + + public: + virtual ~InputHandler(); + + InputHandler(); + + InputHandler(Str_8 id); + + InputHandler(InputHandler&& ih) noexcept; + + InputHandler(const InputHandler& ih); + + InputHandler& operator=(InputHandler&& ih) noexcept; + + InputHandler& operator=(const InputHandler& ih); + + bool operator==(const UInt_64 otherHashId); + + bool operator!=(const UInt_64 otherHashId); + + virtual bool Initialize(); + + virtual bool Release(); + + virtual void Poll(); + + UInt_64 GetHashId() const; + + Str_8 GetId() const; + + void ResetAllStates(); + + bool HasDevice(const UInt_64 id) const; + + bool AddDevice(HID* device); + + HID* GetDevice(const UInt_64 id) const; + + HID* GetDeviceByType(const UInt_8 type) const; + + virtual bool IsInitialized() const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/hid/Keyboard.h b/include/ehs/io/hid/Keyboard.h new file mode 100644 index 0000000..fb15cf8 --- /dev/null +++ b/include/ehs/io/hid/Keyboard.h @@ -0,0 +1,121 @@ +#pragma once + +#include "ehs/Types.h" +#include "Button.h" +#include "HID.h" + +#define EHS_HID_KEYBOARD 0x02 + +namespace ehs +{ + class Keyboard : public HID + { + public: + Keyboard(); + + Keyboard(Str_8 name, const UInt_64 id); + + Keyboard(Keyboard&& hid) noexcept = default; + + Keyboard(const Keyboard& hid); + + Keyboard& operator=(Keyboard&& hid) noexcept = default; + + Keyboard& operator=(const Keyboard& hid); + + void Poll() override; + + Keyboard* Clone() const override; + + static const Button Unknown; + static const Button Escape; + static const Button Backspace; + static const Button Enter; + static const Button LShift; + static const Button RShift; + static const Button LAlt; + static const Button RAlt; + static const Button LCtrl; + static const Button RCtrl; + static const Button Space; + static const Button A; + static const Button B; + static const Button C; + static const Button D; + static const Button E; + static const Button F; + static const Button G; + static const Button H; + static const Button I; + static const Button J; + static const Button K; + static const Button L; + static const Button M; + static const Button N; + static const Button O; + static const Button P; + static const Button Q; + static const Button R; + static const Button S; + static const Button T; + static const Button U; + static const Button V; + static const Button W; + static const Button X; + static const Button Y; + static const Button Z; + static const Button One; + static const Button Two; + static const Button Three; + static const Button Four; + static const Button Five; + static const Button Six; + static const Button Seven; + static const Button Eight; + static const Button Nine; + static const Button Zero; + static const Button Minus; + static const Button Equals; + static const Button Tilde; + static const Button BackSlash; + static const Button LeftSquareBracket; + static const Button RightSquareBracket; + static const Button SemiColon; + static const Button Apostrophe; + static const Button Comma; + static const Button Period; + static const Button ForwardSlash; + static const Button F1; + static const Button F2; + static const Button F3; + static const Button F4; + static const Button F5; + static const Button F6; + static const Button F7; + static const Button F8; + static const Button F9; + static const Button F10; + static const Button F11; + static const Button F12; + static const Button F13; + static const Button F14; + static const Button F15; + static const Button F16; + static const Button F17; + static const Button F18; + static const Button F19; + static const Button F20; + static const Button F21; + static const Button F22; + static const Button F23; + static const Button F24; + static const Button Left; + static const Button Right; + static const Button Up; + static const Button Down; + + static Button TranslateScanCode(const UInt_32 code); + + static Char_8 TranslateToEnglish_8(const bool shifted, const Button& button); + }; +} diff --git a/include/ehs/io/hid/Mouse.h b/include/ehs/io/hid/Mouse.h new file mode 100644 index 0000000..52c1f29 --- /dev/null +++ b/include/ehs/io/hid/Mouse.h @@ -0,0 +1,55 @@ +#pragma once + +#include "ehs/Types.h" +#include "ehs/Vec2.h" +#include "Button.h" +#include "HID.h" + +#define EHS_HID_MOUSE 0x01 + +namespace ehs +{ + class Mouse : public HID + { + private: + friend class Input; + + Vec2_s32 delta; + + public: + Mouse(); + + Mouse(Str_8 name, const UInt_64 id); + + Mouse(Mouse&& hid) noexcept = default; + + Mouse(const Mouse& hid); + + Mouse& operator=(Mouse&& hid) noexcept = default; + + Mouse& operator=(const Mouse& hid); + + void Poll() override; + + void SetDelta(const Vec2_s32& newDelta); + + Vec2_s32 GetDelta() const; + + Mouse* Clone() const override; + + static const Button Unknown; + static const Button LMB; + static const Button MMB; + static const Button RMB; + static const Button Four; + static const Button Five; + static const Button ScrollUp; + static const Button ScrollDown; + static const Button ScrollLeft; + static const Button ScrollRight; + static const Button Back; + static const Button Forward; + + static Button TranslateXCB(const UInt_32 code); + }; +} diff --git a/include/ehs/io/img/Img.h b/include/ehs/io/img/Img.h new file mode 100644 index 0000000..05b2759 --- /dev/null +++ b/include/ehs/io/img/Img.h @@ -0,0 +1,188 @@ +#pragma once + +#include "ehs/Types.h" +#include "ehs/BaseObj.h" +#include "ehs/Str.h" +#include "ImgCodec.h" + +namespace ehs +{ + enum class Resampling : UInt_8 + { + NONE, + NEAREST_NEIGHBOR + }; + + class Img : public BaseObj + { + private: + static Array codecs; + + protected: + UInt_64 hashId; + Str_8 id; + UInt_8 byteDepth; + UInt_8 channels; + Vec2_u64 resolution; + UInt_64 size; + Byte* data; + + public: + static bool HasCodec(UInt_64 hashExt); + + static bool HasCodec(const Str_8& ext); + + static bool AddCodec(ImgCodec codec); + + static const ImgCodec* GetCodec(UInt_64 hashExt); + + static const ImgCodec* GetCodec(const Str_8& ext); + + ~Img() override; + + Img(); + + Img(Str_8 id); + + Img(Str_8 id, UInt_8 byteDepth, UInt_8 channels, const Vec2_u64& resolution, const Byte* data); + + Img(Str_8 id, UInt_8 byteDepth, UInt_8 channels, const Vec2_u64& resolution); + + Img(Img&& img) noexcept; + + Img(const Img& img); + + Img& operator=(Img&& img) noexcept; + + Img& operator=(const Img& img); + + operator const Byte* () const; + + operator Byte* (); + + void Release(); + + UInt_64 GetHashId() const; + + void SetId(Str_8 newId); + + Str_8 GetId() const; + + UInt_8 GetByteDepth() const; + + UInt_8 GetBitDepth() const; + + UInt_8 GetChannels() const; + + Vec2_u64 GetResolution() const; + + UInt_64 GetSize() const; + + void SetPixel(UInt_64 index, const Byte* pixel); + + void GetPixel(UInt_64 index, Byte* pixel) const; + + void SetPixel(UInt_64 x, UInt_64 y, const Byte* pixel); + + void GetPixel(UInt_64 x, UInt_64 y, Byte* pixel) const; + + void Resize(Resampling method, const Vec2_u64& newResolution); + + Img GetResized(Resampling method, const Vec2_u64& newResolution) const; + + void ToRGBA(); + + Img GetAsRGBA() const; + + void ToRGB(); + + Img GetAsRGB() const; + + void ToMonoA(); + + Img GetAsMonoA() const; + + void ToMono(); + + Img GetAsMono() const; + + void To32(); + + Img GetAs32() const; + + void To24(); + + Img GetAs24() const; + + void To16(); + + Img GetAs16() const; + + void To8(); + + Img GetAs8() const; + + bool IsValid() const; + + bool ToFile(const Str_8& filePath) const; + + static Img FromFile(const Str_8& filePath); + + static Img* FromFile_Heap(const Str_8& filePath); + + static Img FromData(Str_8 id, const Str_8& ext, Serializer& data); + + private: + Img GetNearestNeighbor(const Vec2_u64& newResolution) const; + + void NearestNeighbor(const Vec2_u64& newResolution); + + void RGB_To_RGBA(UInt_64 newSize, Byte* buffer) const; + + void MonoA_To_RGBA(UInt_64 newSize, Byte* buffer) const; + + void Mono_To_RGBA(UInt_64 newSize, Byte* buffer) const; + + void RGBA_To_RGB(UInt_64 newSize, Byte* buffer) const; + + void MonoA_To_RGB(UInt_64 newSize, Byte* buffer) const; + + void Mono_To_RGB(UInt_64 newSize, Byte* buffer) const; + + void RGBA_To_MonoA(UInt_64 newSize, Byte* buffer) const; + + void RGB_To_MonoA(UInt_64 newSize, Byte* buffer) const; + + void Mono_To_MonoA(UInt_64 newSize, Byte* buffer) const; + + void RGBA_To_Mono(UInt_64 newSize, Byte* buffer) const; + + void RGB_To_Mono(UInt_64 newSize, Byte* buffer) const; + + void MonoA_To_Mono(UInt_64 newSize, Byte* buffer) const; + + void BD24_to_BD32(UInt_64 newSize, Byte* buffer) const; + + void BD16_to_BD32(UInt_64 newSize, Byte* buffer) const; + + void BD8_to_BD32(UInt_64 newSize, Byte* buffer) const; + + void BD32_to_BD24(UInt_64 newSize, Byte* buffer) const; + + void BD16_to_BD24(UInt_64 newSize, Byte* buffer) const; + + void BD8_to_BD24(UInt_64 newSize, Byte* buffer) const; + + void BD32_to_BD16(UInt_64 newSize, Byte* buffer) const; + + void BD24_to_BD16(UInt_64 newSize, Byte* buffer) const; + + void BD8_to_BD16(UInt_64 newSize, Byte* buffer) const; + + void BD32_to_BD8(UInt_64 newSize, Byte* buffer) const; + + void BD24_to_BD8(UInt_64 newSize, Byte* buffer) const; + + void BD16_to_BD8(UInt_64 newSize, Byte* buffer) const; + }; +} diff --git a/include/ehs/io/img/ImgCodec.h b/include/ehs/io/img/ImgCodec.h new file mode 100644 index 0000000..deb44b2 --- /dev/null +++ b/include/ehs/io/img/ImgCodec.h @@ -0,0 +1,48 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/io/File.h" + +namespace ehs +{ + class Img; + + class ImgCodec + { + private: + Str_8 id; + UInt_64 hashExt; + Str_8 ext; + Endianness endianness; + bool (*encodeCb)(const ImgCodec* const, Serializer&, const Img*); + bool (*decodeCb)(const ImgCodec* const, Serializer&, Img*); + + public: + ImgCodec(); + + ImgCodec(Str_8 id, Str_8 ext, const Endianness end, + bool (*encodeCb)(const ImgCodec* const, Serializer&, const Img*), + bool (*decodeCb)(const ImgCodec* const, Serializer&, Img*)); + + ImgCodec(ImgCodec&& codec) noexcept; + + ImgCodec(const ImgCodec& codec); + + ImgCodec& operator=(ImgCodec&& codec) noexcept; + + ImgCodec& operator=(const ImgCodec& codec); + + Str_8 GetId() const; + + UInt_64 GetHashExt() const; + + Str_8 GetExt() const; + + Endianness GetEndianness() const; + + bool Encode(Serializer& out, const Img* in) const; + + bool Decode(Serializer& in, Img* out) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/img/PNG.h b/include/ehs/io/img/PNG.h new file mode 100644 index 0000000..6bb9912 --- /dev/null +++ b/include/ehs/io/img/PNG.h @@ -0,0 +1,51 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Serializer.h" +#include "PNG_Chunk.h" +#include "Img.h" + +namespace ehs +{ + class PNG + { + private: + Str_8 id; + UInt_64 hashId; + Array chunks; + + public: + PNG(); + + PNG(const Str_8& filePath); + + PNG(const Str_8& id, Serializer& data); + + PNG(const PNG& png); + + PNG& operator=(const PNG& png); + + bool HasChunk(const UInt_64 hashId) const; + + bool HasChunk(const Str_8& id) const; + + PNG_Chunk* GetChunk(const UInt_64 hashId); + + PNG_Chunk* GetChunk(const Str_8& id); + + static bool IsPNG(Serializer& data); + + static void FilterNone(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline); + + static void FilterSub(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline); + + static void FilterUp(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline); + + static void FilterAverage(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline); + + static void FilterPaeth(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline); + + private: + static Byte PaethPredictor(const Byte a, const Byte b, const Byte c); + }; +} \ No newline at end of file diff --git a/include/ehs/io/img/PNG_Chunk.h b/include/ehs/io/img/PNG_Chunk.h new file mode 100644 index 0000000..974f2ab --- /dev/null +++ b/include/ehs/io/img/PNG_Chunk.h @@ -0,0 +1,34 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Serializer.h" + +namespace ehs +{ + class PNG_Chunk + { + private: + Str_8 id; + UInt_64 hashId; + Serializer data; + Byte crc[4]; + + public: + PNG_Chunk(); + + PNG_Chunk(const Str_8& id, const Serializer& data, const Byte crc[4]); + + PNG_Chunk(const PNG_Chunk& chunk); + + PNG_Chunk& operator=(const PNG_Chunk& chunk); + + Str_8 GetId() const; + + UInt_64 GetHashId() const; + + Serializer* GetData(); + + const unsigned char* GetCRC() const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/model/AnimBone.h b/include/ehs/io/model/AnimBone.h new file mode 100644 index 0000000..868a2da --- /dev/null +++ b/include/ehs/io/model/AnimBone.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Array.h" +#include "KeyFrame.h" + +namespace ehs +{ + class AnimBone + { + private: + UInt_8 boneId; + Array keyFrames; + + public: + AnimBone(); + + AnimBone(const UInt_8 boneId); + + AnimBone(const UInt_8 boneId, const UInt_64 size); + + AnimBone(const UInt_8 boneId, Array keyFrames); + + AnimBone(AnimBone&& anim) noexcept; + + AnimBone(const AnimBone& anim); + + AnimBone& operator=(AnimBone&& anim) noexcept; + + AnimBone& operator=(const AnimBone& anim); + + UInt_8 GetBoneId() const; + + Array GetKeyFrames() const; + + Array& GetKeyFrames(); + + float GetPrevAndNext(KeyFrame& prev, KeyFrame& next, const float elapsed) const; + }; +} diff --git a/include/ehs/io/model/Animation.h b/include/ehs/io/model/Animation.h new file mode 100644 index 0000000..8cef9c9 --- /dev/null +++ b/include/ehs/io/model/Animation.h @@ -0,0 +1,49 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Array.h" +#include "AnimBone.h" + +namespace ehs +{ + class Animation + { + private: + UInt_64 hashId; + Str_8 id; + float duration; + Array animated; + + public: + Animation(); + + Animation(Str_8 id, const float duration); + + Animation(Str_8 id, const float duration, UInt_64 size); + + Animation(Str_8 id, const float duration, Array animated); + + Animation(Animation&& anim) noexcept; + + Animation(const Animation& anim); + + Animation& operator=(Animation&& anim) noexcept; + + Animation& operator=(const Animation& anim); + + UInt_64 GetHashId() const; + + void SetId(Str_8 newId); + + Str_8 GetId() const; + + float GetDuration() const; + + Array GetAnimated() const; + + Array& GetAnimated(); + + Array Interpolate(const UInt_64 boneCount, const float elapsed) const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/model/Bone.h b/include/ehs/io/model/Bone.h new file mode 100644 index 0000000..32844f8 --- /dev/null +++ b/include/ehs/io/model/Bone.h @@ -0,0 +1,73 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Quat.h" +#include "ehs/Mat4.h" + +namespace ehs +{ + class Bone + { + private: + UInt_64 hashName; + Str_8 name; + UInt_8 id; + Mat4_f animTrans; + Mat4_f localBindTrans; + Mat4_f invBindTrans; + Array children; + + public: + Bone(); + + Bone(Str_8 name, UInt_8 id, const Mat4_f& localBindTrans, const Mat4_f& invBindTrans); + + Bone(Bone&& bone) noexcept; + + Bone(const Bone& bone); + + Bone& operator=(Bone&& bone) noexcept; + + Bone& operator=(const Bone& bone); + + UInt_64 GetHashName() const; + + void SetName(Str_8 newId); + + Str_8 GetName() const; + + UInt_8 GetId() const; + + void SetAnimTrans(const Mat4_f& newTrans); + + Mat4_f GetAnimTrans() const; + + void GetAnimTransRec(Array& output) const; + + Mat4_f GetLocalBindTrans() const; + + Mat4_f GetInvBindTrans() const; + + UInt_8 GetBoneCount() const; + + bool HasBone(UInt_64 hashName, UInt_8 id) const; + + bool HasBone(UInt_64 hashName) const; + + bool HasBone(UInt_8 id) const; + + bool AddBone(Bone child); + + const Bone* GetBone(UInt_64 hashName) const; + + Bone* GetBone(UInt_64 hashName); + + const Bone* GetBone(UInt_8 id) const; + + Bone* GetBone(UInt_8 id); + + const Array& GetChildren() const; + + Array& GetChildren(); + }; +} diff --git a/include/ehs/io/model/KeyFrame.h b/include/ehs/io/model/KeyFrame.h new file mode 100644 index 0000000..945189e --- /dev/null +++ b/include/ehs/io/model/KeyFrame.h @@ -0,0 +1,55 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Array.h" +#include "ehs/Vec3.h" +#include "ehs/Quat.h" +#include "ehs/Mat4.h" +#include "PropertyChange.h" + +namespace ehs +{ + class KeyFrame + { + private: + float num; + float timeStamp; + Vec3_f pos; + Quat_f rot; + Vec3_f scale; + Mat4_f trans; + + public: + KeyFrame(); + + KeyFrame(const float num, const float timeStamp, const Vec3_f& pos, const Quat_f& rot, const Vec3_f& scale); + + KeyFrame(const float num, const float timeStamp); + + KeyFrame(const KeyFrame& kf); + + KeyFrame& operator=(const KeyFrame& kf); + + float GetNum() const; + + float GetTimeStamp() const; + + void SetPos(const Vec3_f& newPos); + + Vec3_f GetPos() const; + + void SetRot(const Quat_f& newRot); + + Quat_f GetRot() const; + + void SetScale(const Vec3_f& newScale); + + Vec3_f GetScale() const; + + void CalculateTransform(); + + Mat4_f GetTrans() const; + + static Mat4_f Interpolate(const KeyFrame& prev, const KeyFrame& next, const float percentage); + }; +} diff --git a/include/ehs/io/model/Mesh.h b/include/ehs/io/model/Mesh.h new file mode 100644 index 0000000..af3421c --- /dev/null +++ b/include/ehs/io/model/Mesh.h @@ -0,0 +1,80 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Array.h" +#include "Vertex.h" +#include "ehs/BaseObj.h" + +namespace ehs +{ + class Mesh final : public BaseObj + { + protected: + UInt_64 hashId; + Str_8 id; + Array vertices; + Array indices; + + public: + Mesh(); + + Mesh(Str_8 id, Array vertices, Array indices); + + Mesh(Str_8 id, Array vertices); + + Mesh(Mesh&& mesh) noexcept; + + Mesh(const Mesh& mesh); + + Mesh& operator=(Mesh&& mesh) noexcept; + + Mesh& operator=(const Mesh& mesh); + + void Release(); + + UInt_64 GetHashId() const; + + void SetId(Str_8 newId); + + Str_8 GetId() const; + + void SetVertices(const Array& newVertices); + + const Array& GetVertices() const; + + Array& GetVertices(); + + void SetIndices(const Array& newIndices); + + bool HasIndices() const; + + const Array& GetIndices() const; + + Array& GetIndices(); + + void Calculate(); + + private: + static void Calculate(Vertex_f& vert1, Vertex_f& vert2, Vertex_f& vert3); + }; + + const Mesh portraitGui("PortraitGui", + { + {{0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f}}, + {{0.0f, 1.0f, 1.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f}}, + {{1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f}}, + {{1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f}} + }, + {0, 1, 2, 3, 2, 1} + ); + + const Mesh portrait("Portrait", + { + {{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f}}, + {{-0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f}}, + {{0.5f, -0.5f, 0.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f}}, + {{0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f}} + }, + {0, 1, 2, 3, 2, 1} + ); +} \ No newline at end of file diff --git a/include/ehs/io/model/Model.h b/include/ehs/io/model/Model.h new file mode 100644 index 0000000..1bc3488 --- /dev/null +++ b/include/ehs/io/model/Model.h @@ -0,0 +1,80 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Array.h" +#include "ehs/io/File.h" +#include "Mesh.h" +#include "Bone.h" +#include "Animation.h" + +namespace ehs +{ + enum class ModelEncoding : UInt_8 + { + EHM + }; + + class Model : public BaseObj + { + protected: + UInt_64 hashId; + Str_8 id; + Array meshes; + Bone skeleton; + Array animations; + + public: + Model(); + + Model(const Str_8& filePath); + + Model(Str_8 id, Array meshes, Bone skeleton, Array animations); + + Model(Str_8 id, Array meshes, Bone skeleton); + + Model(Str_8 id, Array meshes); + + Model(Model&& model) noexcept; + + Model(const Model& model) = default; + + Model& operator=(Model&& model) noexcept; + + Model& operator=(const Model& model) = default; + + void Release(); + + UInt_64 GetHashId() const; + + void SetId(Str_8 newId); + + Str_8 GetId() const; + + const Array& GetMeshes() const; + + Array& GetMeshes(); + + Mesh* GetMesh(UInt_64 inHashId); + + Mesh* GetMesh(const Str_8& inId); + + const Bone& GetSkeleton() const; + + Bone& GetSkeleton(); + + Animation* GetAnimation(UInt_64 inHashId); + + const Array& GetAnimations() const; + + Array& GetAnimations(); + + void Calculate(); + + void Export(const Str_8& filePath, ModelEncoding encoding); + + private: + void ToEHM(File& file); + + void FromEHM(File& file); + }; +} diff --git a/include/ehs/io/model/PropertyChange.h b/include/ehs/io/model/PropertyChange.h new file mode 100644 index 0000000..13130a5 --- /dev/null +++ b/include/ehs/io/model/PropertyChange.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ehs/EHS.h" + +namespace ehs +{ + enum class ChangeType : UInt_8 + { + X_AXIS_POS, + Y_AXIS_POS, + Z_AXIS_POS, + X_AXIS_SCALE, + Y_AXIS_SCALE, + Z_AXIS_SCALE, + X_AXIS_ROT, + Y_AXIS_ROT, + Z_AXIS_ROT, + W_AXIS_ROT, + INVALID + }; + + class PropertyChange + { + public: + ChangeType type; + float value; + + PropertyChange(); + + PropertyChange(const ChangeType type, const float value); + }; +} \ No newline at end of file diff --git a/include/ehs/io/model/Vertex.h b/include/ehs/io/model/Vertex.h new file mode 100644 index 0000000..fb625bd --- /dev/null +++ b/include/ehs/io/model/Vertex.h @@ -0,0 +1,50 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Vec4.h" +#include "ehs/Vec3.h" +#include "ehs/Vec2.h" +#include "ehs/Color4.h" + +namespace ehs +{ + template + class Vertex + { + public: + Vec3 pos; + Vec3 normal; + Vec2 uv; + Vec3 tan; + Vec3 bTan; + Vec4 bones; + Vec4 weights; + + Vertex() = default; + + Vertex(const Vec3& pos) + : pos(pos), bones{0, 0, 0, 0}, weights{0.0f, 0.0f, 0.0f, 0.0f} + { + } + + Vertex(const Vec3& pos, const Vec3& normal) + : pos(pos), normal(normal), bones{0, 0, 0, 0}, weights{0.0f, 0.0f, 0.0f, 0.0f} + { + } + + Vertex(const Vec3& pos, const Vec3& normal, const Vec2& uv) + : pos(pos), normal(normal), uv(uv), bones{0, 0, 0, 0}, weights{0.0f, 0.0f, 0.0f, 0.0f} + { + } + + Vertex(const Vertex& vert) + : pos(vert.pos), normal(vert.normal), uv(vert.uv), bones(vert.bones), weights(vert.weights) + { + } + + Vertex& operator=(const Vertex& vert) = default; + }; + + typedef Vertex Vertex_d; + typedef Vertex Vertex_f; +} \ No newline at end of file diff --git a/include/ehs/io/socket/BaseTCP.h b/include/ehs/io/socket/BaseTCP.h new file mode 100644 index 0000000..81a7cb1 --- /dev/null +++ b/include/ehs/io/socket/BaseTCP.h @@ -0,0 +1,112 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "Request.h" +#include "Response.h" + +#include "Socket.h" + +namespace ehs +{ + class BaseTCP + { + protected: + AddrType addrType; + Str_8 localAddr; + unsigned short localPort; + Str_8 remoteHostName; + Str_8 remoteAddr; + unsigned short remotePort; + bool connection; + bool bound; + bool listening; + bool connected; + + public: + static const UInt_16 HTTPS_Port = 443; + static const UInt_16 HTTP_Port = 80; + static const UInt_16 MaxHeaderSize = 8192; + + virtual ~BaseTCP() = default; + + BaseTCP(); + + BaseTCP(const AddrType addrType); + + BaseTCP(BaseTCP&& tcp) noexcept; + + BaseTCP(const BaseTCP& tcp); + + BaseTCP& operator=(BaseTCP&& tcp) noexcept; + + BaseTCP& operator=(const BaseTCP& tcp); + + virtual void Initialize() = 0; + + virtual void Release() = 0; + + virtual void Bind(const Str_8& address, unsigned short port) = 0; + + virtual void Listen() = 0; + + virtual BaseTCP* Accept() = 0; + + virtual void Connect(const Str_8& address, const unsigned short port) = 0; + + virtual UInt_64 Send(const Byte* const buffer, const UInt_32 size) = 0; + + virtual UInt_64 Receive(Byte* const buffer, const UInt_32 size) = 0; + + void SendStr(const Str_8& str); + + /// Sends a HTTP response to the connected endpoint. + /// @param [in] res The response to send. + void SendRes(const Response& res); + + /// Sends a HTTP request to the connected endpoint. + /// @param [in] req The request to send. + void SendReq(Request& req); + + /// Receives a HTTP response from the connected endpoint. + /// @returns The response received. + Response RecvRes(); + + /// Receives a HTTP request from the connected endpoint. + /// @returns The request received. + Request RecvReq(); + + AddrType GetAddressType() const; + + Str_8 GetLocalAddress() const; + + unsigned short GetLocalPort() const; + + Str_8 GetRemoteAddress() const; + + unsigned short GetRemotePort() const; + + bool IsConnection() const; + + bool IsBound() const; + + bool IsListening() const; + + bool IsConnected() const; + + virtual void SetBlocking(const bool blocking) = 0; + + virtual bool IsBlocking() const = 0; + + virtual bool IsValid() const = 0; + + private: + Str_8 RecvHeader(); + + Str_8 RecvBody(const UInt_64 contentLength); + + UInt_64 RecvChunkSize(); + + Str_8 RecvChunk(const UInt_64 chunkSize); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/BaseUDP.h b/include/ehs/io/socket/BaseUDP.h new file mode 100644 index 0000000..f65b926 --- /dev/null +++ b/include/ehs/io/socket/BaseUDP.h @@ -0,0 +1,54 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "Socket.h" + +namespace ehs +{ + class BaseUDP + { + protected: + AddrType type; + Str_8 address; + UInt_16 port; + bool bound; + + public: + virtual ~BaseUDP() = default; + + BaseUDP(); + + BaseUDP(AddrType type); + + BaseUDP(BaseUDP&& udp) noexcept; + + BaseUDP(const BaseUDP& udp); + + BaseUDP& operator=(BaseUDP&& udp) noexcept; + + BaseUDP& operator=(const BaseUDP& udp); + + virtual void Release() = 0; + + virtual void Bind(AddrType type, const Str_8& address, UInt_16 port) = 0; + + virtual UInt_64 Send(AddrType type, const Str_8& address, UInt_16 port, const Byte* data, UInt_64 size) = 0; + + virtual UInt_64 Receive(AddrType* type, Str_8* address, UInt_16* port, Byte* data, UInt_64 size) = 0; + + bool IsBound() const; + + virtual void SetBlocking(bool blocking) = 0; + + virtual bool IsBlocking() const = 0; + + AddrType GetLocalAddressType() const; + + Str_8 GetLocalAddress() const; + + UInt_16 GetLocalPort() const; + + virtual bool IsValid() const = 0; + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/DNS.h b/include/ehs/io/socket/DNS.h new file mode 100644 index 0000000..2e670e7 --- /dev/null +++ b/include/ehs/io/socket/DNS.h @@ -0,0 +1,14 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "Socket.h" + +namespace ehs +{ + class DNS + { + public: + static Str_8 Resolve(const AddrType addrType, const Str_8& hostName); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/Request.h b/include/ehs/io/socket/Request.h new file mode 100644 index 0000000..ada9678 --- /dev/null +++ b/include/ehs/io/socket/Request.h @@ -0,0 +1,164 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Vector.h" +#include "ehs/Str.h" +#include "ehs/json/Json.h" +#include "Socket.h" + +namespace ehs +{ + enum class Verb + { + POST, + GET, + PUT, + DEL + }; + + class Request + { + private: + Verb verb; + Str_8 rsrc; + Vector queries; + Vector header; + ContentType cType; + Str_8 body; + + public: + /// Default member initialization. + Request(); + + /// Initializes this request with a given verb and URI resource. + /// @param [in] verb The type of request to make. + /// @param [in] rsrc The URI endpoint to make the request at. + Request(const Verb verb, const Str_8& rsrc); + + /// Initializes this request with the raw request data. + /// @param [in] data The C-style string of the request. + /// @param [in] size The size of the given C-style string. + Request(const char* data, const UInt_64 size); + + /// Initializes this request with the raw request data. + /// @param [in] data The string of the request. + Request(const Str_8& data); + + /// Copies members from another object of the same type. + /// @param [in] req The object to copy from. + Request(const Request& req) = default; + + /// Copies members from another object of the same type. + /// @param [in] req The object to copy from. + /// @returns The request that has been assigned to. + Request& operator=(const Request& req); + + /// Retrieves the verb for the request. + /// @returns The result. + Verb GetVerb() const; + + /// Sets the content type for the body. + /// @param [in] cType The content type to use. + void SetContentType(const ContentType cType); + + /// Retrieves the content type for the body. + /// @returns The result. + ContentType GetContentType() const; + + /// Sets the URI resource. + /// @param [in] rsrc The resource. + void SetResource(const Str_8& rsrc); + + /// Retrieves the URI resource. + /// @returns The result. + Str_8 GetResource() const; + + /// Adds a query variable to the URI. + /// @param [in] var The variable identifier. + /// @param [in] value The value of the variable. + void AddQuery(const Str_8& var, const Str_8& value); + + /// Retrieves a query variable from the URI. + /// @param [in] var The variable identifier to look for. + /// @returns The value of the query variable. Empty if it was not found. + Str_8 GetQuery(const Str_8& var); + + /// Retrieves all the query variables from the URI in a vector object. + /// @returns The result. + Vector GetQueries() const; + + /// A helper method to automatically add the required header variables for basic authentication. + /// @param [in] id The username or id. + /// @param [in] secret The secret given by an API. + void BasicAuth(const Str_8& id, const Str_8& secret); + + /// A helper method to automatically add the required header variables for bearer authentication. + /// @param [in] token The token given by an API. + void BearerAuth(const Str_8& token); + + /// A helper method to automatically add the required header variables for bearer authentication. + /// @param [in] token The token given by an API. + /// @param [in] clientId The client id given by an API. + void BearerAuth(const Str_8& token, const Str_8& clientId); + + /// A helper method to automatically add the required header variables for bot authentication. + /// @param [in] token The token given by an API. + void BotAuth(const Str_8& token); + + /// Adds a header variable. + /// @param [in] var The variable identifier. + /// @param [in] value The value of the variable. + void AddToHeader(const Str_8& var, const Str_8& value); + + /// Retrieves a header variable. + /// @param [in] var The variable identifier to look for. + /// @returns The value of the header variable. Empty if it was not found. + Str_8 GetHeader(const Str_8& var) const; + + /// Retrieves all the header variables in a vector object. + /// @returns The result. + Vector GetHeader() const; + + /// Adds a body variable. + /// @param [in] var The variable identifier. + /// @param [in] value The value of the variable. + void AddToBody(const Str_8& var, const Str_8& value); + + /// Adds a value to the body. + /// @param [in] data The value to add. + void AddToBody(const Str_8& data); + + /// Sets the entire body. + /// @param [in] body The body to use. + void SetBody(const Str_8& body); + + /// Retrieves a body variable. + /// @param [in] var The variable identifier to look for. + /// @returns The value of the body variable. Empty if it was not found. + Str_8 GetVar(const Str_8& var) const; + + /// Retrieves the entire body. + /// @returns The result. + Str_8 GetBody() const; + + /// Retrieves the entire body as a Json. + /// @returns The result. + Json GetJson() const; + + /// Forms the raw result of the request to be sent. + /// @returns The result. + Str_8 FormResult() const; + + bool IsValid() const; + + private: + static Str_8 VerbToStr(const Verb verb); + + static Str_8 ContentTypeToStr(const ContentType cType); + + static ContentType StrToContentType(const Str_8& value); + + void ReadData(const Str_8& data); + + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/Response.h b/include/ehs/io/socket/Response.h new file mode 100644 index 0000000..956ed8b --- /dev/null +++ b/include/ehs/io/socket/Response.h @@ -0,0 +1,127 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Vector.h" +#include "ehs/Str.h" +#include "ehs/json/Json.h" +#include "Socket.h" + +namespace ehs +{ + class Response + { + private: + UInt_32 code; + Str_8 server; + ContentType cType; + Vector header; + Str_8 body; + + public: + /// Default member initialization. + Response(); + + /// Initializes this response with a given code and server identifier. + /// @param [in] code The code to give. + /// @param [in] server The server identifier. + Response(const UInt_32 code, const Str_8& server); + + /// Initializes this response with the raw response data. + /// @param [in] data The C-style string of the response. + /// @param [in] size The size of the given C-style string. + Response(const char* data, const UInt_64 size); + + /// Initializes this response with the raw response data. + /// @param [in] data The string of the response. + Response(const Str_8& data); + + /// Copies members from another object of the same type. + /// @param [in] res The object to copy from. + Response(const Response& res) = default; + + /// Copies members from another object of the same type. + /// @param [in] res The object to copy from. + /// @returns The response that has been assigned to. + Response& operator=(const Response& res); + + /// Sets the response code to send to the endpoint. + /// @param [in] code The code for success, error or info. + void SetCode(const UInt_32 code); + + /// Retrieves the response code. + /// @returns The result. + UInt_32 GetCode() const; + + /// Sets the server identifier. + /// @param [in] server The server identifier to use. + void SetServer(const Str_8& server); + + /// Retrieves the server identifier. + /// @returns The result. + Str_8 GetServer() const; + + /// Sets the content type for the body. + /// @param [in] cType The content type to use. + void SetContentType(const ContentType cType); + + /// Retrieves the content type for the body. + /// @returns The result. + ContentType GetContentType() const; + + /// Adds a header variable. + /// @param [in] var The variable identifier. + /// @param [in] value The value of the variable. + void AddToHeader(const Str_8& var, const Str_8& value); + + /// Retrieves a header variable. + /// @param [in] var The variable identifier to look for. + /// @returns The value of the header variable. Empty if it was not found. + Str_8 GetHeader(const Str_8& var) const; + + /// Retrieves all the header variables in a vector object. + /// @returns The result. + Vector GetHeader() const; + + /// Adds a body variable. + /// @param [in] var The variable identifier. + /// @param [in] value The value of the variable. + void AddToBody(const Str_8& var, const Str_8& value); + + /// Adds a value to the body. + /// @param [in] data The value to add. + void AddToBody(const Str_8& data); + + /// Sets the entire body. + /// @param [in] body The body to use. + void SetBody(const Str_8& body); + + /// Retrieves a body variable. + /// @param [in] var The variable identifier to look for. + /// @returns The value of the body variable. Empty if it was not found. + Str_8 GetVar(const Str_8& var) const; + + /// Retrieves the entire body. + /// @returns The result. + Str_8 GetBody() const; + + /// Retrieves the entire body as a Json. + /// @returns The result. + Json GetJson() const; + + /// Forms the raw result of the response to be sent. + /// @returns The result. + Str_8 FormResult() const; + + bool IsValid() const; + + private: + static Str_8 CodeToStr(const UInt_32 code); + + static Str_8 ContentTypeToStr(const ContentType cType); + + static ContentType StrToContentType(const Str_8& value); + + void ReadData(const Str_8& data); + + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/SSL.h b/include/ehs/io/socket/SSL.h new file mode 100644 index 0000000..34827c7 --- /dev/null +++ b/include/ehs/io/socket/SSL.h @@ -0,0 +1,56 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "TCP.h" +#include "Request.h" +#include "Response.h" + +typedef struct ssl_ctx_st SSL_CTX; +typedef struct ssl_st SSL; + +namespace ehs +{ + /// A class for handling the HTTP(S) TCP socket layer. + class SSL : public TCP + { + private: + SSL_CTX* ctx; + ::SSL* sslHdl; + + public: + ~SSL() override; + + SSL(); + + SSL(const AddrType type); + + SSL(TCP&& tcp) noexcept; + + SSL(const TCP& tcp); + + SSL(const SSL& ssl); + + SSL& operator=(const SSL& ssl); + + void Initialize() override; + + void Release() override; + + void Bind(const Str_8& address, unsigned short port) override; + + SSL* Accept() override; + + void Connect(const Str_8& address, const UInt_16 port) override; + + UInt_64 Send(const Byte* const buffer, const UInt_32 size) override; + + UInt_64 Receive(Byte* const buffer, const UInt_32 size) override; + + void UseCertificate(const Byte* data, const UInt_64 size); + + void UsePrivateKey(const Byte* data, const UInt_64 size); + + bool IsValid(); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/Socket.h b/include/ehs/io/socket/Socket.h new file mode 100644 index 0000000..f825875 --- /dev/null +++ b/include/ehs/io/socket/Socket.h @@ -0,0 +1,51 @@ +#pragma once + +#ifndef EHS_IPV4_HEADER + #define EHS_IPV4_HEADER 60 +#endif + +#ifndef EHS_IPV6_HEADER + #define EHS_IPV6_HEADER 40 +#endif + +#ifndef EHS_UDP_HEADER + #define EHS_UDP_HEADER 8 +#endif + +#ifndef EHS_IPV4_UDP_PAYLOAD + #define EHS_IPV4_UDP_PAYLOAD (EHS_UINT_16_MAX - EHS_IPV4_HEADER - EHS_UDP_HEADER) +#endif + +#ifndef EHS_IPV6_UDP_PAYLOAD + #define EHS_IPV6_UDP_PAYLOAD (EHS_UINT_16_MAX - EHS_IPV6_HEADER - EHS_UDP_HEADER) +#endif + +namespace ehs +{ + enum class AddrType + { + IPV6, + IPV4 + }; + + enum class ContentType + { + APP_MULTIPART_FORMDATA, + APP_FORMURLENCODED, + APP_JAVASCRIPT, + APP_JSON, + APP_XML, + TEXT_PLAIN, + TEXT_HTML, + TEXT_XML, + NONE + }; + +#if defined(EHS_OS_WINDOWS) + typedef UInt_64 Socket; + #define EHS_INVALID_SOCKET EHS_UINT_64_MAX +#elif defined(EHS_OS_LINUX) + typedef SInt_32 Socket; + #define EHS_INVALID_SOCKET (SInt_32)0xffffffff +#endif +} \ No newline at end of file diff --git a/include/ehs/io/socket/TCP.h b/include/ehs/io/socket/TCP.h new file mode 100644 index 0000000..012198d --- /dev/null +++ b/include/ehs/io/socket/TCP.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef EHS_OS_WINDOWS +#include "TCP_W32.h" +#else +#include "TCP_BSD.h" +#endif \ No newline at end of file diff --git a/include/ehs/io/socket/TCP_BSD.h b/include/ehs/io/socket/TCP_BSD.h new file mode 100644 index 0000000..f25e705 --- /dev/null +++ b/include/ehs/io/socket/TCP_BSD.h @@ -0,0 +1,94 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Log.h" + +#include "Socket.h" +#include "BaseTCP.h" + +namespace ehs +{ + /// A wrapper class for the transmission control protocol socket. + class TCP : public BaseTCP + { + protected: + Socket hdl; + + public: + /// Frees any native handles. + ~TCP() override; + + /// Default members initialization. + TCP(); + + TCP(const AddrType addrType); + + TCP(TCP&& tcp) noexcept; + + /// Copies some members from the given TCP object. + /// @param [in] tcp The TCP object to copy from. + TCP(const TCP& tcp); + + TCP& operator=(TCP&& tcp) noexcept; + + /// Copies some members from the given TCP object. + /// @param [in] tcp The TCP object to copy from. + /// @returns The TCP object that has been assigned to. + TCP& operator=(const TCP& tcp); + + void Initialize() override; + + /// Frees native handles and uninitializes them. + void Release() override; + + /// Binds the UDP socket to a local address and port. + /// @param [in] address The local IPv4 or IPv6 address to bind to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param [in] port The port to bind to. + /// @note Requires the port given to be forwarded if this is called. + void Bind(const Str_8& address, unsigned short port) override; + + /// Listens for incoming connections. Used for servers or PtP. + void Listen() override; + + /// Accepts an incoming connection. Used for servers or PtP. + /// @returns The accepted client object. + TCP* Accept() override; + + /// Connects to a TCP Socket that listens for incoming connections. Used for clients or PtP. + /// @param address The address of the listening TCP socket. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param port The port of the listening TCP socket. + void Connect(const Str_8& address, const unsigned short port) override; + + /// Sends data in a C-style array with raw functionality. Meaning no internal help outside of native functions besides error checking. + /// @param [in] buffer The C-style array to send. + /// @param [in] size The size of the given C-style array. + /// @returns The size of the data actually sent in bytes. + UInt_64 Send(const Byte* const buffer, const UInt_32 size) override; + + /// Receives data in a C-style array with raw functionality. Meaning no internal help outside of native functions besides error checking. + /// @param [out] buffer The C-style array to receive with. + /// @param [in] size The size of the given C-style array. + /// @returns The size of the data actually received in bytes. + UInt_64 Receive(Byte* const buffer, const UInt_32 size) override; + + /// Sets whether or not receiving data blocks the next task. + /// @param [in] blocking Whether or not to block. + void SetBlocking(const bool blocking) override; + + /// Retrieves whether or not this socket will block when receiving data. + /// @returns The result. + bool IsBlocking() const override; + + bool IsValid() const override; + + private: + void Bind_v6(const Str_8& address, unsigned short port); + + void Bind_v4(const Str_8& address, unsigned short port); + + void Connect_v6(const Str_8& address, unsigned short port); + + void Connect_v4(const Str_8& address, unsigned short port); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/TCP_W32.h b/include/ehs/io/socket/TCP_W32.h new file mode 100644 index 0000000..f25e705 --- /dev/null +++ b/include/ehs/io/socket/TCP_W32.h @@ -0,0 +1,94 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Log.h" + +#include "Socket.h" +#include "BaseTCP.h" + +namespace ehs +{ + /// A wrapper class for the transmission control protocol socket. + class TCP : public BaseTCP + { + protected: + Socket hdl; + + public: + /// Frees any native handles. + ~TCP() override; + + /// Default members initialization. + TCP(); + + TCP(const AddrType addrType); + + TCP(TCP&& tcp) noexcept; + + /// Copies some members from the given TCP object. + /// @param [in] tcp The TCP object to copy from. + TCP(const TCP& tcp); + + TCP& operator=(TCP&& tcp) noexcept; + + /// Copies some members from the given TCP object. + /// @param [in] tcp The TCP object to copy from. + /// @returns The TCP object that has been assigned to. + TCP& operator=(const TCP& tcp); + + void Initialize() override; + + /// Frees native handles and uninitializes them. + void Release() override; + + /// Binds the UDP socket to a local address and port. + /// @param [in] address The local IPv4 or IPv6 address to bind to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param [in] port The port to bind to. + /// @note Requires the port given to be forwarded if this is called. + void Bind(const Str_8& address, unsigned short port) override; + + /// Listens for incoming connections. Used for servers or PtP. + void Listen() override; + + /// Accepts an incoming connection. Used for servers or PtP. + /// @returns The accepted client object. + TCP* Accept() override; + + /// Connects to a TCP Socket that listens for incoming connections. Used for clients or PtP. + /// @param address The address of the listening TCP socket. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param port The port of the listening TCP socket. + void Connect(const Str_8& address, const unsigned short port) override; + + /// Sends data in a C-style array with raw functionality. Meaning no internal help outside of native functions besides error checking. + /// @param [in] buffer The C-style array to send. + /// @param [in] size The size of the given C-style array. + /// @returns The size of the data actually sent in bytes. + UInt_64 Send(const Byte* const buffer, const UInt_32 size) override; + + /// Receives data in a C-style array with raw functionality. Meaning no internal help outside of native functions besides error checking. + /// @param [out] buffer The C-style array to receive with. + /// @param [in] size The size of the given C-style array. + /// @returns The size of the data actually received in bytes. + UInt_64 Receive(Byte* const buffer, const UInt_32 size) override; + + /// Sets whether or not receiving data blocks the next task. + /// @param [in] blocking Whether or not to block. + void SetBlocking(const bool blocking) override; + + /// Retrieves whether or not this socket will block when receiving data. + /// @returns The result. + bool IsBlocking() const override; + + bool IsValid() const override; + + private: + void Bind_v6(const Str_8& address, unsigned short port); + + void Bind_v4(const Str_8& address, unsigned short port); + + void Connect_v6(const Str_8& address, unsigned short port); + + void Connect_v4(const Str_8& address, unsigned short port); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/UDP.h b/include/ehs/io/socket/UDP.h new file mode 100644 index 0000000..e439f17 --- /dev/null +++ b/include/ehs/io/socket/UDP.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef EHS_OS_WINDOWS +#include "UDP_W32.h" +#else +#include "UDP_BSD.h" +#endif \ No newline at end of file diff --git a/include/ehs/io/socket/UDP_BSD.h b/include/ehs/io/socket/UDP_BSD.h new file mode 100644 index 0000000..193d635 --- /dev/null +++ b/include/ehs/io/socket/UDP_BSD.h @@ -0,0 +1,82 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "BaseUDP.h" + +namespace ehs +{ + /// A wrapper class for the user datagram protocol socket. + class UDP : public BaseUDP + { + private: + Socket hdl; + + public: + /// Frees any native handles. + ~UDP() override; + + UDP(); + + /// Default members initialization. + UDP(AddrType type); + + UDP(UDP&& udp) noexcept; + + /// Copies some members from the given UDP object. + /// @param [in] udp The UDP object to copy from. + UDP(const UDP& udp); + + UDP& operator=(UDP&& udp) noexcept; + + /// Copies some members from the given UDP object. + /// @param [in] udp The UDP object to copy from. + /// @returns The UDP object that has been assigned to. + UDP& operator=(const UDP& udp); + + /// Frees native handles and uninitializes them. + void Release() override; + + /// Binds the UDP socket to a local address and port. + /// @param [in] address The local IPv4 or IPv6 address to bind to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param [in] port The port to bind to. + /// @note Requires the port given to be forwarded if this is called. + void Bind(AddrType type, const Str_8& address, UInt_16 port) override; + + /// Sends data using a C-style byte array. + /// @param [in] addr The remote Ipv4 or Ipv6 address to send to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param [in] port The remote port to send to. + /// @param [in] data The C-style byte array to send. + /// @param [in] size The size of the C-style byte array. + /// @note The size of data to be sent cannot exceed "UDP::maxPayloadIpv4" or "UDP::maxPayloadIpv6". + UInt_64 Send(AddrType type, const Str_8& address, UInt_16 port, const Byte* data, UInt_64 size) override; + + /// Receives data using the packet helper class. + /// @param [out] addr The Ipv4 or Ipv6 address of the sender. + /// @param [out] port The port of the sender. + /// @param [out] data The C-style byte array received. + /// @param [in] size The size of the pre-allocated C-style byte array. + /// @returns The size of the data received. + /// @warning The provided C-style byte array must be freed when finished using. + UInt_64 Receive(AddrType* type, Str_8* address, UInt_16* port, Byte* data, UInt_64 size) override; + + /// Sets whether or not receiving data blocks the next task. + /// @param [in] blocking Whether or not to block. + void SetBlocking(bool blocking) override; + + /// Retrieves whether or not this socket will block when receiving data. + /// @returns The result. + bool IsBlocking() const override; + + bool IsValid() const override; + + private: + void Bind_v6(const Str_8& address, UInt_16 port) const; + + void Bind_v4(const Str_8& address, UInt_16 port) const; + + UInt_64 Send_v6(const Str_8& address, UInt_16 port, const Byte* data, UInt_64 size); + + UInt_64 Send_v4(const Str_8& address, UInt_16 port, const Byte* data, UInt_64 size); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/UDP_W32.h b/include/ehs/io/socket/UDP_W32.h new file mode 100644 index 0000000..992efc0 --- /dev/null +++ b/include/ehs/io/socket/UDP_W32.h @@ -0,0 +1,82 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "BaseUDP.h" + +namespace ehs +{ + /// A wrapper class for the user datagram protocol socket. + class UDP : public BaseUDP + { + private: + Socket hdl; + + public: + /// Frees any native handles. + ~UDP() override; + + UDP(); + + /// Default members initialization. + UDP(const AddrType addrType); + + UDP(UDP&& udp) noexcept; + + /// Copies some members from the given UDP object. + /// @param [in] udp The UDP object to copy from. + UDP(const UDP& udp); + + UDP& operator=(UDP&& udp) noexcept; + + /// Copies some members from the given UDP object. + /// @param [in] udp The UDP object to copy from. + /// @returns The UDP object that has been assigned to. + UDP& operator=(const UDP& udp); + + /// Frees native handles and uninitializes them. + void Release() override; + + /// Binds the UDP socket to a local address and port. + /// @param [in] address The local IPv4 or IPv6 address to bind to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param [in] port The port to bind to. + /// @note Requires the port given to be forwarded if this is called. + void Bind(const Str_8& address, const UInt_16 port) override; + + /// Sends data using a C-style byte array. + /// @param [in] addr The remote Ipv4 or Ipv6 address to send to. Resolves domain names. The given address can be empty, "127.0.0.1", or "localhost" to automatically find the appropriate device. + /// @param [in] port The remote port to send to. + /// @param [in] data The C-style byte array to send. + /// @param [in] size The size of the C-style byte array. + /// @note The size of data to be sent cannot exceed "UDP::maxPayloadIpv4" or "UDP::maxPayloadIpv6". + UInt_64 Send(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size) override; + + /// Receives data using the packet helper class. + /// @param [out] addr The Ipv4 or Ipv6 address of the sender. + /// @param [out] port The port of the sender. + /// @param [out] data The C-style byte array received. + /// @param [in] size The size of the pre-allocated C-style byte array. + /// @returns The size of the data received. + /// @warning The provided C-style byte array must be freed when finished using. + UInt_64 Receive(Str_8* const addr, UInt_16* const port, Byte* const data, const UInt_64 size) override; + + /// Sets whether or not receiving data blocks the next task. + /// @param [in] blocking Whether or not to block. + void SetBlocking(const bool blocking) override; + + /// Retrieves whether or not this socket will block when receiving data. + /// @returns The result. + bool IsBlocking() const override; + + bool IsValid() const override; + + private: + void Bind_v6(const Str_8& address, const UInt_16 port); + + void Bind_v4(const Str_8& address, const UInt_16 port); + + UInt_64 Send_v6(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size); + + UInt_64 Send_v4(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/rest/Spotify.h b/include/ehs/io/socket/rest/Spotify.h new file mode 100644 index 0000000..2ae077d --- /dev/null +++ b/include/ehs/io/socket/rest/Spotify.h @@ -0,0 +1,113 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/Array.h" +#include "ehs/io/socket/SSL.h" + +namespace ehs +{ + enum class SpotifyState + { + TRACK, + CONTEXT, + OFF + }; + + struct Track + { + Array artists; + Str_8 name; + Str_8 id; + }; + + class Spotify + { + private: + SSL client; + Str_8 clientId; + Str_8 secret; + Str_8 redURI; + Array scopes; + bool forceVerify; + Str_8 token; + Str_8 rToken; + + public: + static const Str_8 trackUriPrefix; + + virtual ~Spotify(); + + Spotify(); + + Spotify(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array& scopes, const bool forceVerify); + + bool Authorize(); + + /// Sets the volume for a device. + /// @param [in] level The percentage to set the volume to. + /// @returns The response code. + UInt_32 SetVolume(const UInt_8 level); + + /// Resume playback for a device. + /// @returns The response code. + UInt_32 Play(); + + /// Pauses playback for a device. + /// @returns The response code. + UInt_32 Pause(); + + /// Repeats playback for a device. + /// @param [in] status The status to set it to. + /// @returns The response code. + UInt_32 SetRepeat(const SpotifyState state); + + /// Shuffles playback for a device. + /// @param [in] state The state to set shuffle to. + /// @returns The response code. + UInt_32 SetShuffle(const bool state); + + UInt_32 SearchTrack(Vector& artists, Str_8& id, Str_8& name); + + UInt_32 GetPlayingTrack(Vector& artists, Str_8& id, Str_8& name); + + UInt_32 GetQueue(Array& tracks); + + /// Adds a track to the queue for a device. + /// @param [in] uri The track id to add. + /// @returns The response code. + UInt_32 QueueTrack(const Str_8& id); + + UInt_32 AddTracks(const Str_8& playlistId, const Array& trackIds, const UInt_32 pos = 0); + + UInt_32 AddTrack(const Str_8& playlistId, const Str_8& trackId, const UInt_32 pos = 0); + + /// Skips to the next track. + /// @returns The response code. + UInt_32 Skip(); + + /// Skips to the previous track. + /// @returns The response code. + UInt_32 Previous(); + + /// Seeks to a position of the currently playing track in milliseconds. + /// @param [in] pos The position in milliseconds to seek to. + /// @returns The response code. + UInt_32 Seek(const UInt_32 pos); + + Str_8 GetClientId() const; + + Str_8 GetSecret() const; + + Str_8 GetRedURI() const; + + bool IsVerificationForced() const; + + bool IsActive() const; + + private: + void StartConnection(); + + bool ReAuthorize(); + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/rest/Twitch.h b/include/ehs/io/socket/rest/Twitch.h new file mode 100644 index 0000000..0d37eff --- /dev/null +++ b/include/ehs/io/socket/rest/Twitch.h @@ -0,0 +1,39 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/io/socket/SSL.h" + +namespace ehs +{ + class Twitch + { + private: + SSL client; + Str_8 clientId; + Str_8 secret; + Str_8 redURI; + Array scopes; + bool forceVerify; + Str_8 token; + + public: + virtual ~Twitch(); + + Twitch(); + + Twitch(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array& scopes, const bool forceVerify); + + bool Authorize(); + + Str_8 GetClientId() const; + + Str_8 GetSecret() const; + + Str_8 GetRedURI() const; + + bool IsVerificationForced() const; + + Str_8 GetToken() const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/socket/rest/TwitchChat.h b/include/ehs/io/socket/rest/TwitchChat.h new file mode 100644 index 0000000..c971e44 --- /dev/null +++ b/include/ehs/io/socket/rest/TwitchChat.h @@ -0,0 +1,53 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "ehs/io/socket/TCP.h" + +namespace ehs +{ + class TwitchChat + { + private: + TCP client; + Str_8 username; + Str_8 token; + Str_8 channel; + bool initialized; + + public: + ~TwitchChat(); + + TwitchChat(); + + TwitchChat(const Str_8& username); + + TwitchChat(const Str_8& username, const Str_8& token); + + TwitchChat(const TwitchChat& chat); + + TwitchChat& operator=(const TwitchChat& chat); + + void SetToken(const Str_8& newToken); + + void Initialize(); + + void UnInitialize(); + + void JoinChannel(const Str_8& newChannel); + + void LeaveChannel(); + + void SendPong(); + + void SendMsg(const Str_8& msg); + + void WhisperMsg(const Str_8& user, const Str_8& msg); + + Str_8 RecvMsg(); + + Str_8 GetUsername() const; + + Str_8 GetChannel() const; + }; +} \ No newline at end of file diff --git a/include/ehs/io/xdg-shell-client-protocol.h b/include/ehs/io/xdg-shell-client-protocol.h new file mode 100644 index 0000000..5f79269 --- /dev/null +++ b/include/ehs/io/xdg-shell-client-protocol.h @@ -0,0 +1,2307 @@ +/* Generated by wayland-scanner 1.22.0 */ + +#ifndef XDG_SHELL_CLIENT_PROTOCOL_H +#define XDG_SHELL_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_shell The xdg_shell protocol + * @section page_ifaces_xdg_shell Interfaces + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces + * - @subpage page_iface_xdg_positioner - child surface positioner + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface + * - @subpage page_iface_xdg_toplevel - toplevel surface + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell Copyright + *
+ *
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013      Rafael Antognolli
+ * Copyright © 2013      Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ * Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ * Copyright © 2015-2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct xdg_popup; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_wm_base; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +/** + * @page page_iface_xdg_wm_base xdg_wm_base + * @section page_iface_xdg_wm_base_desc Description + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + * @section page_iface_xdg_wm_base_api API + * See @ref iface_xdg_wm_base. + */ +/** + * @defgroup iface_xdg_wm_base The xdg_wm_base interface + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + */ +extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +/** + * @page page_iface_xdg_positioner xdg_positioner + * @section page_iface_xdg_positioner_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + * @section page_iface_xdg_positioner_api API + * See @ref iface_xdg_positioner. + */ +/** + * @defgroup iface_xdg_positioner The xdg_positioner interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + */ +extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +/** + * @page page_iface_xdg_surface xdg_surface + * @section page_iface_xdg_surface_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + * @section page_iface_xdg_surface_api API + * See @ref iface_xdg_surface. + */ +/** + * @defgroup iface_xdg_surface The xdg_surface interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + */ +extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +/** + * @page page_iface_xdg_toplevel xdg_toplevel + * @section page_iface_xdg_toplevel_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + * @section page_iface_xdg_toplevel_api API + * See @ref iface_xdg_toplevel. + */ +/** + * @defgroup iface_xdg_toplevel The xdg_toplevel interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + */ +extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +/** + * @page page_iface_xdg_popup xdg_popup + * @section page_iface_xdg_popup_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_xdg_popup_api API + * See @ref iface_xdg_popup. + */ +/** + * @defgroup iface_xdg_popup The xdg_popup interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface xdg_popup_interface; +#endif + +#ifndef XDG_WM_BASE_ERROR_ENUM +#define XDG_WM_BASE_ERROR_ENUM +enum xdg_wm_base_error { + /** + * given wl_surface has another role + */ + XDG_WM_BASE_ERROR_ROLE = 0, + /** + * xdg_wm_base was destroyed before children + */ + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, + /** + * the client didn’t respond to a ping event in time + */ + XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, +}; +#endif /* XDG_WM_BASE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_listener + */ +struct xdg_wm_base_listener { + /** + * check if the client is alive + * + * The ping event asks the client if it's still alive. Pass the + * serial specified in the event back to the compositor by sending + * a "pong" request back with the specified serial. See + * xdg_wm_base.pong. + * + * Compositors can use this to determine if the client is still + * alive. It's unspecified what will happen if the client doesn't + * respond to the ping request, or in what timeframe. Clients + * should try to respond in a reasonable amount of time. The + * “unresponsive” error is provided for compositors that wish + * to disconnect unresponsive clients. + * + * A compositor is free to ping in any way it wants, but a client + * must always respond to any xdg_wm_base object it created. + * @param serial pass this to the pong request + */ + void (*ping)(void *data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_wm_base + */ +static inline int +xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +#define XDG_WM_BASE_DESTROY 0 +#define XDG_WM_BASE_CREATE_POSITIONER 1 +#define XDG_WM_BASE_GET_XDG_SURFACE 2 +#define XDG_WM_BASE_PONG 3 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PONG_SINCE_VERSION 1 + +/** @ingroup iface_xdg_wm_base */ +static inline void +xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); +} + +/** @ingroup iface_xdg_wm_base */ +static inline void * +xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); +} + +static inline uint32_t +xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Destroy this xdg_wm_base object. + * + * Destroying a bound xdg_wm_base object while there are surfaces + * still alive created by this xdg_wm_base object instance is illegal + * and will result in a defunct_surfaces error. + */ +static inline void +xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_wm_base + * + * Create a positioner object. A positioner object is used to position + * surfaces relative to some parent surface. See the interface description + * and xdg_surface.get_popup for details. + */ +static inline struct xdg_positioner * +xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); + + return (struct xdg_positioner *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * This creates an xdg_surface for the given surface. While xdg_surface + * itself is not a role, the corresponding surface may only be assigned + * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is + * illegal to create an xdg_surface for a wl_surface which already has an + * assigned role and this will result in a role error. + * + * This creates an xdg_surface for the given surface. An xdg_surface is + * used as basis to define a role to a given surface, such as xdg_toplevel + * or xdg_popup. It also manages functionality shared between xdg_surface + * based surface roles. + * + * See the documentation of xdg_surface for more details about what an + * xdg_surface is and how it is used. + */ +static inline struct xdg_surface * +xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); + + return (struct xdg_surface *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_wm_base.ping + * and xdg_wm_base.error.unresponsive. + */ +static inline void +xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); +} + +#ifndef XDG_POSITIONER_ERROR_ENUM +#define XDG_POSITIONER_ERROR_ENUM +enum xdg_positioner_error { + /** + * invalid input provided + */ + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; +#endif /* XDG_POSITIONER_ERROR_ENUM */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM +#define XDG_POSITIONER_ANCHOR_ENUM +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_ANCHOR_ENUM */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM +#define XDG_POSITIONER_GRAVITY_ENUM +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_GRAVITY_ENUM */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_xdg_positioner + * constraint adjustments + * + * The constraint adjustment value define ways the compositor will adjust + * the position of the surface, if the unadjusted position would result + * in the surface being partly constrained. + * + * Whether a surface is considered 'constrained' is left to the compositor + * to determine. For example, the surface may be partly outside the + * compositor's defined 'work area', thus necessitating the child surface's + * position be adjusted until it is entirely inside the work area. + * + * The adjustments can be combined, according to a defined precedence: 1) + * Flip, 2) Slide, 3) Resize. + */ +enum xdg_positioner_constraint_adjustment { + /** + * don't move the child surface when constrained + * + * Don't alter the surface position even if it is constrained on + * some axis, for example partially outside the edge of an output. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + /** + * move along the x axis until unconstrained + * + * Slide the surface along the x axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the x + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the x axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + /** + * move along the y axis until unconstrained + * + * Slide the surface along the y axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the y + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the y axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + /** + * invert the anchor and gravity on the x axis + * + * Invert the anchor and gravity on the x axis if the surface is + * constrained on the x axis. For example, if the left edge of the + * surface is constrained, the gravity is 'left' and the anchor is + * 'left', change the gravity to 'right' and the anchor to 'right'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_x adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + /** + * invert the anchor and gravity on the y axis + * + * Invert the anchor and gravity on the y axis if the surface is + * constrained on the y axis. For example, if the bottom edge of + * the surface is constrained, the gravity is 'bottom' and the + * anchor is 'bottom', change the gravity to 'top' and the anchor + * to 'top'. + * + * The adjusted position is calculated given the original anchor + * rectangle and offset, but with the new flipped anchor and + * gravity values. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_y adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + /** + * horizontally resize the surface + * + * Resize the surface horizontally so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + /** + * vertically resize the surface + * + * Resize the surface vertically so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ + +#define XDG_POSITIONER_DESTROY 0 +#define XDG_POSITIONER_SET_SIZE 1 +#define XDG_POSITIONER_SET_ANCHOR_RECT 2 +#define XDG_POSITIONER_SET_ANCHOR 3 +#define XDG_POSITIONER_SET_GRAVITY 4 +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 +#define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 + + +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 + +/** @ingroup iface_xdg_positioner */ +static inline void +xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); +} + +/** @ingroup iface_xdg_positioner */ +static inline void * +xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); +} + +static inline uint32_t +xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); +} + +/** + * @ingroup iface_xdg_positioner + * + * Notify the compositor that the xdg_positioner will no longer be used. + */ +static inline void +xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the size of the surface that is to be positioned with the positioner + * object. The size is in surface-local coordinates and corresponds to the + * window geometry. See xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the anchor rectangle within the parent surface that the child + * surface will be placed relative to. The rectangle is relative to the + * window geometry as defined by xdg_surface.set_window_geometry of the + * parent surface. + * + * When the xdg_positioner object is used to position a child surface, the + * anchor rectangle may not extend outside the window geometry of the + * positioned child's parent surface. + * + * If a negative size is set the invalid_input error is raised. + */ +static inline void +xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines the anchor point for the anchor rectangle. The specified anchor + * is used derive an anchor point that the child surface will be + * positioned relative to. If a corner anchor is set (e.g. 'top_left' or + * 'bottom_right'), the anchor point will be at the specified corner; + * otherwise, the derived anchor point will be centered on the specified + * edge, or in the center of the anchor rectangle if no edge is specified. + */ +static inline void +xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); +} + +/** + * @ingroup iface_xdg_positioner + * + * Defines in what direction a surface should be positioned, relative to + * the anchor point of the parent surface. If a corner gravity is + * specified (e.g. 'bottom_right' or 'top_left'), then the child surface + * will be placed towards the specified gravity; otherwise, the child + * surface will be centered over the anchor point on any axis that had no + * gravity specified. If the gravity is not in the ‘gravity’ enum, an + * invalid_input error is raised. + */ +static inline void +xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify how the window should be positioned if the originally intended + * position caused the surface to be constrained, meaning at least + * partially outside positioning boundaries set by the compositor. The + * adjustment is set by constructing a bitmask describing the adjustment to + * be made when the surface is constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that the child + * surface should not change its position on that axis when constrained. + * + * If more than one bit for one axis is set, the order of how adjustments + * are applied is specified in the corresponding adjustment descriptions. + * + * The default adjustment is none. + */ +static inline void +xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); +} + +/** + * @ingroup iface_xdg_positioner + * + * Specify the surface position offset relative to the position of the + * anchor on the anchor rectangle and the anchor on the surface. For + * example if the anchor of the anchor rectangle is at (x, y), the surface + * has the gravity bottom|right, and the offset is (ox, oy), the calculated + * surface position will be (x + ox, y + oy). The offset position of the + * surface is the one used for constraint testing. See + * set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user interface + * element, while aligning the user interface element of the parent surface + * with some user interface element placed somewhere in the popup surface. + */ +static inline void +xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); +} + +/** + * @ingroup iface_xdg_positioner + * + * When set reactive, the surface is reconstrained if the conditions used + * for constraining changed, e.g. the parent window moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, followed by an + * xdg_surface.configure event. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information to + * determine the future state the popup should be constrained using. If + * this doesn't match the dimension of the parent the popup is eventually + * positioned against, the behavior is undefined. + * + * The arguments are given in the surface-local coordinate space. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the serial of an xdg_surface.configure event this positioner will be + * used in response to. The compositor may use this information together + * with set_parent_size to determine what future state the popup should be + * constrained using. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); +} + +#ifndef XDG_SURFACE_ERROR_ENUM +#define XDG_SURFACE_ERROR_ENUM +enum xdg_surface_error { + /** + * Surface was not fully constructed + */ + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + /** + * Surface was already constructed + */ + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + /** + * Attaching a buffer to an unconfigured surface + */ + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, + /** + * Invalid serial number when acking a configure event + */ + XDG_SURFACE_ERROR_INVALID_SERIAL = 4, + /** + * Width or height was zero or negative + */ + XDG_SURFACE_ERROR_INVALID_SIZE = 5, + /** + * Surface was destroyed before its role object + */ + XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, +}; +#endif /* XDG_SURFACE_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_listener + */ +struct xdg_surface_listener { + /** + * suggest a surface change + * + * The configure event marks the end of a configure sequence. A + * configure sequence is a set of one or more events configuring + * the state of the xdg_surface, including the final + * xdg_surface.configure event. + * + * Where applicable, xdg_surface surface roles will during a + * configure sequence extend this event as a latched state sent as + * events before the xdg_surface.configure event. Such events + * should be considered to make up a set of atomically applied + * configuration states, where the xdg_surface.configure commits + * the accumulated state. + * + * Clients should arrange their surface for the new states, and + * then send an ack_configure request with the serial sent in this + * configure event at some point before committing the new surface. + * + * If the client receives multiple configure events before it can + * respond to one, it is free to discard all but the last event it + * received. + * @param serial serial of the configure event + */ + void (*configure)(void *data, + struct xdg_surface *xdg_surface, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_surface + */ +static inline int +xdg_surface_add_listener(struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +#define XDG_SURFACE_DESTROY 0 +#define XDG_SURFACE_GET_TOPLEVEL 1 +#define XDG_SURFACE_GET_POPUP 2 +#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 +#define XDG_SURFACE_ACK_CONFIGURE 4 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 + +/** @ingroup iface_xdg_surface */ +static inline void +xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); +} + +/** @ingroup iface_xdg_surface */ +static inline void * +xdg_surface_get_user_data(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); +} + +static inline uint32_t +xdg_surface_get_version(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_surface); +} + +/** + * @ingroup iface_xdg_surface + * + * Destroy the xdg_surface object. An xdg_surface must only be destroyed + * after its role object has been destroyed, otherwise + * a defunct_role_object error is raised. + */ +static inline void +xdg_surface_destroy(struct xdg_surface *xdg_surface) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_toplevel object for the given xdg_surface and gives + * the associated wl_surface the xdg_toplevel role. + * + * See the documentation of xdg_toplevel for more details about what an + * xdg_toplevel is and how it is used. + */ +static inline struct xdg_toplevel * +xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); + + return (struct xdg_toplevel *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * This creates an xdg_popup object for the given xdg_surface and gives + * the associated wl_surface the xdg_popup role. + * + * If null is passed as a parent, a parent surface must be specified using + * some other protocol, before committing the initial state. + * + * See the documentation of xdg_popup for more details about what an + * xdg_popup is and how it is used. + */ +static inline struct xdg_popup * +xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); + + return (struct xdg_popup *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * The window geometry of a surface is its "visible bounds" from the + * user's perspective. Client-side decorations often have invisible + * portions like drop-shadows which should be ignored for the + * purposes of aligning, placing and constraining windows. + * + * The window geometry is double buffered, and will be applied at the + * time wl_surface.commit of the corresponding wl_surface is called. + * + * When maintaining a position, the compositor should treat the (x, y) + * coordinate of the window geometry as the top left corner of the window. + * A client changing the (x, y) window geometry coordinate should in + * general not alter the position of the window. + * + * Once the window geometry of the surface is set, it is not possible to + * unset it, and it will remain the same until set_window_geometry is + * called again, even if a new subsurface or buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface, and may extend outside + * of the wl_surface itself to mark parts of the subsurface tree as part of + * the window geometry. + * + * When applied, the effective window geometry will be the set window + * geometry clamped to the bounding rectangle of the combined + * geometry of the surface of the xdg_surface and the associated + * subsurfaces. + * + * The effective geometry will not be recalculated unless a new call to + * set_window_geometry is done and the new pending surface state is + * subsequently applied. + * + * The width and height of the effective window geometry must be + * greater than zero. Setting an invalid size will raise an + * invalid_size error. + */ +static inline void +xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_surface + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client + * must make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * For instance, for toplevel surfaces the compositor might use this + * information to move a surface to the top left only when the client has + * drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it + * can respond to one, it only has to ack the last configure event. + * Acking a configure event that was never sent raises an invalid_serial + * error. + * + * A client is not required to commit immediately after sending + * an ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before committing, but + * only the last request sent before a commit indicates which configure + * event the client really is responding to. + * + * Sending an ack_configure request consumes the serial number sent with + * the request, as well as serial numbers sent by all configure events + * sent on this xdg_surface prior to the configure event referenced by + * the committed serial. + * + * It is an error to issue multiple ack_configure requests referencing a + * serial from the same configure event, or to issue an ack_configure + * request referencing a serial from a configure event issued before the + * event identified by the last ack_configure request for the same + * xdg_surface. Doing so will raise an invalid_serial error. + */ +static inline void +xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); +} + +#ifndef XDG_TOPLEVEL_ERROR_ENUM +#define XDG_TOPLEVEL_ERROR_ENUM +enum xdg_toplevel_error { + /** + * provided value is not a valid variant of the resize_edge enum + */ + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, + /** + * invalid parent toplevel + */ + XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, + /** + * client provided an invalid min or max size + */ + XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, +}; +#endif /* XDG_TOPLEVEL_ERROR_ENUM */ + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM +/** + * @ingroup iface_xdg_toplevel + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM +#define XDG_TOPLEVEL_STATE_ENUM +/** + * @ingroup iface_xdg_toplevel + * types of state on the surface + * + * The different state values used on the surface. This is designed for + * state values like maximized, fullscreen. It is paired with the + * configure event to ensure that both the client and the compositor + * setting the state can be synchronized. + * + * States set in this way are double-buffered. They will get applied on + * the next commit. + */ +enum xdg_toplevel_state { + /** + * the surface is maximized + * the surface is maximized + * + * The surface is maximized. The window geometry specified in the + * configure event must be obeyed by the client, or the + * xdg_wm_base.invalid_surface_state error is raised. + * + * The client should draw without shadow or other decoration + * outside of the window geometry. + */ + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + * the surface is fullscreen + * + * The surface is fullscreen. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. For a surface to cover the whole fullscreened area, + * the geometry dimensions must be obeyed by the client. For more + * details, see xdg_toplevel.set_fullscreen. + */ + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + * the surface is being resized + * + * The surface is being resized. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. Clients that have aspect ratio or cell sizing + * configuration can use a smaller size, however. + */ + XDG_TOPLEVEL_STATE_RESIZING = 3, + /** + * the surface is now activated + * the surface is now activated + * + * Client window decorations should be painted as if the window + * is active. Do not assume this means that the window actually has + * keyboard or pointer focus. + */ + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + /** + * the surface’s left edge is tiled + * + * The window is currently in a tiled layout and the left edge is + * considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + /** + * the surface’s right edge is tiled + * + * The window is currently in a tiled layout and the right edge + * is considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + /** + * the surface’s top edge is tiled + * + * The window is currently in a tiled layout and the top edge is + * considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + /** + * the surface’s bottom edge is tiled + * + * The window is currently in a tiled layout and the bottom edge + * is considered to be adjacent to another part of the tiling grid. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, + /** + * surface repaint is suspended + * + * The surface is currently not ordinarily being repainted; for + * example because its content is occluded by another window, or + * its outputs are switched off due to screen locking. + * @since 6 + */ + XDG_TOPLEVEL_STATE_SUSPENDED = 9, +}; +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 +#endif /* XDG_TOPLEVEL_STATE_ENUM */ + +#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +enum xdg_toplevel_wm_capabilities { + /** + * show_window_menu is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, + /** + * set_maximized and unset_maximized are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, + /** + * set_fullscreen and unset_fullscreen are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, + /** + * set_minimized is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, +}; +#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_listener + */ +struct xdg_toplevel_listener { + /** + * suggest a surface change + * + * This configure event asks the client to resize its toplevel + * surface or to change its state. The configured state should not + * be applied immediately. See xdg_surface.configure for details. + * + * The width and height arguments specify a hint to the window + * about how its surface should be resized in window geometry + * coordinates. See set_window_geometry. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. This may happen when the + * compositor needs to configure the state of the surface but + * doesn't have any information about any previous or expected + * dimension. + * + * The states listed in the event specify how the width/height + * arguments should be interpreted, and possibly how it should be + * drawn. + * + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + */ + void (*configure)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states); + /** + * surface wants to be closed + * + * The close event is sent by the compositor when the user wants + * the surface to be closed. This should be equivalent to the user + * clicking the close button in client-side decorations, if your + * application has any. + * + * This is only a request that the user intends to close the + * window. The client may choose to ignore this request, or show a + * dialog to ask the user to save their data, etc. + */ + void (*close)(void *data, + struct xdg_toplevel *xdg_toplevel); + /** + * recommended window geometry bounds + * + * The configure_bounds event may be sent prior to a + * xdg_toplevel.configure event to communicate the bounds a window + * geometry size is recommended to constrain to. + * + * The passed width and height are in surface coordinate space. If + * width and height are 0, it means bounds is unknown and + * equivalent to as if no configure_bounds event was ever sent for + * this surface. + * + * The bounds can for example correspond to the size of a monitor + * excluding any panels or other shell components, so that a + * surface isn't created in a way that it cannot fit. + * + * The bounds may change at any point, and in such a case, a new + * xdg_toplevel.configure_bounds will be sent, followed by + * xdg_toplevel.configure and xdg_surface.configure. + * @since 4 + */ + void (*configure_bounds)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height); + /** + * compositor capabilities + * + * This event advertises the capabilities supported by the + * compositor. If a capability isn't supported, clients should hide + * or disable the UI elements that expose this functionality. For + * instance, if the compositor doesn't advertise support for + * minimized toplevels, a button triggering the set_minimized + * request should not be displayed. + * + * The compositor will ignore requests it doesn't support. For + * instance, a compositor which doesn't advertise support for + * minimized will ignore set_minimized requests. + * + * Compositors must send this event once before the first + * xdg_surface.configure event. When the capabilities change, + * compositors must send this event again and then send an + * xdg_surface.configure event. + * + * The configured state should not be applied immediately. See + * xdg_surface.configure for details. + * + * The capabilities are sent as an array of 32-bit unsigned + * integers in native endianness. + * @param capabilities array of 32-bit capabilities + * @since 5 + */ + void (*wm_capabilities)(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities); +}; + +/** + * @ingroup iface_xdg_toplevel + */ +static inline int +xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +#define XDG_TOPLEVEL_DESTROY 0 +#define XDG_TOPLEVEL_SET_PARENT 1 +#define XDG_TOPLEVEL_SET_TITLE 2 +#define XDG_TOPLEVEL_SET_APP_ID 3 +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 +#define XDG_TOPLEVEL_MOVE 5 +#define XDG_TOPLEVEL_RESIZE 6 +#define XDG_TOPLEVEL_SET_MAX_SIZE 7 +#define XDG_TOPLEVEL_SET_MIN_SIZE 8 +#define XDG_TOPLEVEL_SET_MAXIMIZED 9 +#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 +#define XDG_TOPLEVEL_SET_FULLSCREEN 11 +#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 +#define XDG_TOPLEVEL_SET_MINIMIZED 13 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 + +/** @ingroup iface_xdg_toplevel */ +static inline void +xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); +} + +/** @ingroup iface_xdg_toplevel */ +static inline void * +xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); +} + +static inline uint32_t +xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); +} + +/** + * @ingroup iface_xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ +static inline void +xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set the "parent" of this surface. This surface should be stacked + * above the parent surface and all other ancestor surfaces. + * + * Parent surfaces should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the dialog + * is raised. + * + * Setting a null parent for a child surface unsets its parent. Setting + * a null parent for a surface which currently has no parent is a no-op. + * + * Only mapped surfaces can have child surfaces. Setting a parent which + * is not mapped is equivalent to setting a null parent. If a surface + * becomes unmapped, its children's parent is set to the parent of + * the now-unmapped surface. If the now-unmapped surface has no parent, + * its children's parent is unset. If the now-unmapped surface becomes + * mapped again, its parent-child relationship is not restored. + * + * The parent toplevel must not be one of the child toplevel's + * descendants, and the parent must be different from the child toplevel, + * otherwise the invalid_parent protocol error is raised. + */ +static inline void +xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ +static inline void +xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group multiple + * surfaces together, or to determine how to launch a new application. + * + * For D-Bus activatable applications, the app ID is used as the D-Bus + * service name. + * + * The compositor shell will try to group application surfaces together + * by their app ID. As a best practice, it is suggested to select app + * ID's that match the basename of the application's .desktop file. + * For example, "org.freedesktop.FooViewer" where the .desktop file is + * "org.freedesktop.FooViewer.desktop". + * + * Like other properties, a set_app_id request can be sent after the + * xdg_toplevel has been mapped to update the property. + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] https://standards.freedesktop.org/desktop-entry-spec/ + */ +static inline void +xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Clients implementing client-side decorations might want to show + * a context menu when right-clicking on the decorations, giving the + * user a menu that they can use to maximize or minimize the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu items + * the window menu contains, or even if a window menu will be drawn + * at all. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. + */ +static inline void +xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive move (touch, + * pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed serial + * is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, such as + * updating a pointer cursor, during the move. There is no guarantee + * that the device focus will return when the move is completed. + */ +static inline void +xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user action + * like a button press, key press, or touch down event. The passed + * serial is used to determine the type of interactive resize (touch, + * pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the "resize" + * enum value for more details about what is required. The client + * must also acknowledge configure events using "ack_configure". After + * the resize is completed, the client will receive another "configure" + * event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is no + * guarantee that the device focus will return when the resize is + * completed. + * + * The edges parameter specifies how the surface should be resized, and + * is one of the values of the resize_edge enum. Values not matching + * a variant of the enum will cause the invalid_resize_edge protocol error. + * The compositor may use this information to update the surface position + * for example when dragging the top left corner. The compositor may also + * use this information to adapt its behavior, e.g. choose an appropriate + * cursor image. + */ +static inline void +xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor does + * not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. + * As a result, a client wishing to reset the maximum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width or height will result in a + * invalid_size error. + */ +static inline void +xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor does + * not try to configure the window below this size. + * + * The width and height arguments are in window geometry coordinates. + * See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered. They will get applied + * on the next commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. + * As a result, a client wishing to reset the minimum size + * to an unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. Using + * strictly negative values for width and height will result in a + * invalid_size error. + */ +static inline void +xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the compositor + * will respond by emitting a configure event. Whether this configure + * actually sets the window maximized is subject to compositor policies. + * The client must then update its content, drawing in the configured + * state. The client must also acknowledge the configure when committing + * the new content (see ack_configure). + * + * It is up to the compositor to decide how and where to maximize the + * surface, for example which output and what region of the screen should + * be used. + * + * If the surface was already maximized, the compositor will still emit + * a configure event with the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the compositor + * will respond by emitting a configure event. Whether this actually + * un-maximizes the window is subject to compositor policies. + * If available and applicable, the compositor will include the window + * geometry dimensions the window had prior to being maximized in the + * configure event. The client must then update its content, drawing it in + * the configured state. The client must also acknowledge the configure + * when committing the new content (see ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before maximizing, if + * applicable. + * + * If the surface was already not maximized, the compositor will still + * emit a configure event without the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no direct + * effect. It may alter the state the surface is returned to when + * unmaximized unless overridden by the compositor. + */ +static inline void +xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface fullscreen. + * + * After requesting that the surface should be fullscreened, the + * compositor will respond by emitting a configure event. Whether the + * client is actually put into a fullscreen state is subject to compositor + * policies. The client must also acknowledge the configure when + * committing the new content (see ack_configure). + * + * The output passed by the request indicates the client's preference as + * to which display it should be set fullscreen on. If this value is NULL, + * it's up to the compositor to choose which display will be used to map + * this surface. + * + * If the surface doesn't cover the whole output, the compositor will + * position the surface in the center of the output and compensate with + * with border fill covering the rest of the output. The content of the + * border fill is undefined, but should be assumed to be in some way that + * attempts to blend into the surrounding area (e.g. solid black). + * + * If the fullscreened surface is not opaque, the compositor must make + * sure that other screen content not part of the same surface tree (made + * up of subsurfaces, popups or similarly coupled surfaces) are not + * visible below the fullscreened surface. + */ +static inline void +xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Make the surface no longer fullscreen. + * + * After requesting that the surface should be unfullscreened, the + * compositor will respond by emitting a configure event. + * Whether this actually removes the fullscreen state of the client is + * subject to compositor policies. + * + * Making a surface unfullscreen sets states for the surface based on the following: + * * the state(s) it may have had before becoming fullscreen + * * any state(s) decided by the compositor + * * any state(s) requested by the client while the surface was fullscreen + * + * The compositor may include the previous window geometry dimensions in + * the configure event, if applicable. + * + * The client must also acknowledge the configure when committing the new + * content (see ack_configure). + */ +static inline void +xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ +static inline void +xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +#ifndef XDG_POPUP_ERROR_ENUM +#define XDG_POPUP_ERROR_ENUM +enum xdg_popup_error { + /** + * tried to grab after being mapped + */ + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; +#endif /* XDG_POPUP_ERROR_ENUM */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_listener + */ +struct xdg_popup_listener { + /** + * configure the popup surface + * + * This event asks the popup surface to configure itself given + * the configuration. The configured state should not be applied + * immediately. See xdg_surface.configure for details. + * + * The x and y arguments represent the position the popup was + * placed at given the xdg_positioner rule, relative to the upper + * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ + void (*configure)(void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * popup interaction is done + * + * The popup_done event is sent out when a popup is dismissed by + * the compositor. The client should destroy the xdg_popup object + * at this point. + */ + void (*popup_done)(void *data, + struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); +}; + +/** + * @ingroup iface_xdg_popup + */ +static inline int +xdg_popup_add_listener(struct xdg_popup *xdg_popup, + const struct xdg_popup_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, + (void (**)(void)) listener, data); +} + +#define XDG_POPUP_DESTROY 0 +#define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 + +/** @ingroup iface_xdg_popup */ +static inline void +xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); +} + +/** @ingroup iface_xdg_popup */ +static inline void * +xdg_popup_get_user_data(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); +} + +static inline uint32_t +xdg_popup_get_version(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_popup); +} + +/** + * @ingroup iface_xdg_popup + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, the + * xdg_wm_base.not_the_topmost_popup protocol error will be sent. + */ +static inline void +xdg_popup_destroy(struct xdg_popup *xdg_popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_popup + * + * This request makes the created popup take an explicit grab. An explicit + * grab will be dismissed when the user dismisses the popup, or when the + * client destroys the xdg_popup. This can be done by the user clicking + * outside the surface, using the keyboard, or even locking the screen + * through closing the lid or a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user action like a + * button press, key press, or touch down event. The serial number of the + * event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel surface or + * another xdg_popup with an explicit grab. If the parent is another + * xdg_popup it means that the popups are nested, with this popup now being + * the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were created + * in, e.g. the only popup you are allowed to destroy at all times is the + * topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss every + * nested grabbing popup as well. When a compositor dismisses popups, it + * will follow the same dismissing order as required from the client. + * + * If the topmost grabbing popup is destroyed, the grab will be returned to + * the parent of the popup, if that parent previously had an explicit grab. + * + * If the parent is a grabbing popup which has already been dismissed, this + * popup will be immediately dismissed. If the parent is a popup that did + * not take an explicit grab, an error will be raised. + * + * During a popup grab, the client owning the grab will receive pointer + * and touch events for all their surfaces as normal (similar to an + * "owner-events" grab in X11 parlance), while the top most grabbing popup + * will always have keyboard focus. + */ +static inline void +xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_popup + * + * Reposition an already-mapped popup. The popup will be placed given the + * details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any parameters set + * by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not take + * effect until the corresponding configure event is acknowledged by the + * client. See xdg_popup.repositioned for details. The token itself is + * opaque, and has no other special meaning. + * + * If multiple reposition requests are sent, the compositor may skip all + * but the last one. + * + * If the popup is repositioned in response to a configure event for its + * parent, the client should send an xdg_positioner.set_parent_configure + * and possibly an xdg_positioner.set_parent_size request to allow the + * compositor to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is being + * resized, but not in response to a configure event, the client should + * send an xdg_positioner.set_parent_size request. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ehs/json/Json.h b/include/ehs/json/Json.h new file mode 100644 index 0000000..d0bcdf1 --- /dev/null +++ b/include/ehs/json/Json.h @@ -0,0 +1,68 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "JsonBase.h" +#include "JsonObj.h" +#include "JsonArray.h" +#include "JsonBool.h" +#include "JsonNum.h" +#include "JsonStr.h" +#include "JsonVar.h" + +namespace ehs +{ + class Json + { + private: + JsonBase* value; + + public: + virtual ~Json(); + + Json(); + + Json(const JsonBase& value); + + Json(const JsonObj& value); + + Json(const JsonArray& value); + + Json(const JsonBool& value); + + Json(const JsonNum& value); + + Json(const JsonStr& value); + + Json(const char* data, const UInt_64 size, const UInt_64 extra); + + Json(const Str_8& data, const UInt_64 extra); + + Json(Json&& json) noexcept; + + Json(const Json &json); + + Json& operator=(Json&& json) noexcept; + + Json& operator=(const Json& json); + + JsonBase* GetValue(); + + JsonBase* RetrieveValue(const Str_8& access); + + Str_8 ToStr(const bool compact) const; + + private: + static Vector ParseAccess(const Str_8& access); + + void ParseValue(JsonVar* var, const Char_8** begin, const Char_8* end, const UInt_64 extra); + + JsonVar ParseVar(const Char_8** begin, const Char_8* end, const UInt_64 extra); + + void ParseObject(JsonObj* obj, const Char_8** begin, const Char_8* end, const UInt_64 extra); + + void ParseArray(JsonArray* arr, const Char_8** begin, const Char_8* end, const UInt_64 extra); + + void Parse(const Str_8& data, const UInt_64 extra); + }; +} \ No newline at end of file diff --git a/include/ehs/json/JsonArray.h b/include/ehs/json/JsonArray.h new file mode 100644 index 0000000..3f56683 --- /dev/null +++ b/include/ehs/json/JsonArray.h @@ -0,0 +1,77 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "JsonBase.h" + +namespace ehs +{ + class JsonObj; + class JsonBool; + class JsonNum; + class JsonStr; + + class JsonArray : public JsonBase + { + private: + UInt_64 size; + UInt_64 extra; + UInt_64 rawSize; + JsonBase** data; + + public: + virtual ~JsonArray(); + + JsonArray(); + + JsonArray(const UInt_64 extra); + + JsonArray(const UInt_64 size, const UInt_64 extra); + + JsonArray(JsonArray&& ja) noexcept; + + JsonArray(const JsonArray& ja); + + JsonArray& operator=(JsonArray&& ja) noexcept; + + JsonArray& operator=(const JsonArray& ja); + + operator JsonBase* const *() const; + + operator JsonBase**(); + + UInt_64 RawSize() const; + + UInt_64 Extra() const; + + UInt_64 Size() const; + + void Insert(const UInt_64 index, const JsonBase* const value); + + void Push(const JsonBase* const value); + + void Push(const JsonBase& value); + + void Push(const JsonObj& value); + + void Push(const JsonArray& value); + + void Push(const JsonBool& value); + + void Push(const bool value); + + void Push(const JsonNum& value); + + void Push(const float value); + + void Push(const JsonStr& value); + + void Push(const Char_8* value, const UInt_64 size = 0); + + void Push(const Str_8& value); + + void Pop(); + + Str_8 ToStr(const UInt_64 level, const bool compact) const; + }; +} \ No newline at end of file diff --git a/include/ehs/json/JsonBase.h b/include/ehs/json/JsonBase.h new file mode 100644 index 0000000..7322096 --- /dev/null +++ b/include/ehs/json/JsonBase.h @@ -0,0 +1,34 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +namespace ehs +{ + enum class JsonType + { + NULLOBJ, + OBJ, + ARRAY, + BOOL, + NUM, + STR + }; + + class JsonBase + { + private: + JsonType type; + + public: + JsonBase(); + + JsonBase(const JsonType type); + + JsonBase(const JsonBase& base) = default; + + JsonType GetType() const; + + virtual Str_8 ToStr(const UInt_64 level, const bool compact) const; + }; +} \ No newline at end of file diff --git a/include/ehs/json/JsonBool.h b/include/ehs/json/JsonBool.h new file mode 100644 index 0000000..f0c1735 --- /dev/null +++ b/include/ehs/json/JsonBool.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +#include "JsonBase.h" + +namespace ehs +{ + class JsonBool : public JsonBase + { + public: + bool value; + + JsonBool(); + + JsonBool(const bool value); + + JsonBool(const JsonBool& jb) = default; + + operator bool() const; + + operator bool&(); + + Str_8 ToStr(const UInt_64 level, const bool compact) const; + }; +} \ No newline at end of file diff --git a/include/ehs/json/JsonNum.h b/include/ehs/json/JsonNum.h new file mode 100644 index 0000000..bfb2695 --- /dev/null +++ b/include/ehs/json/JsonNum.h @@ -0,0 +1,45 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +#include "JsonBase.h" + +namespace ehs +{ + class JsonNum : public JsonBase + { + public: + float value; + + JsonNum(); + + JsonNum(const SInt_64 value); + + JsonNum(const UInt_64 value); + + JsonNum(const SInt_32 value); + + JsonNum(const UInt_32 value); + + JsonNum(const SInt_16 value); + + JsonNum(const UInt_16 value); + + JsonNum(const SInt_8 value); + + JsonNum(const UInt_8 value); + + JsonNum(const double value); + + JsonNum(const float value); + + JsonNum(const JsonNum& jn) = default; + + operator float() const; + + operator float&(); + + Str_8 ToStr(const UInt_64 level, const bool compact) const; + }; +} \ No newline at end of file diff --git a/include/ehs/json/JsonObj.h b/include/ehs/json/JsonObj.h new file mode 100644 index 0000000..dfb5941 --- /dev/null +++ b/include/ehs/json/JsonObj.h @@ -0,0 +1,63 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +#include "JsonBase.h" + +namespace ehs +{ + class JsonVar; + + class JsonObj : public JsonBase + { + protected: + UInt_64 size; + UInt_64 extra; + UInt_64 rawSize; + JsonVar* vars; + + public: + virtual ~JsonObj(); + + JsonObj(); + + JsonObj(const UInt_64 size, const UInt_64 extra); + + JsonObj(const UInt_64 extra); + + JsonObj(JsonObj&& value) noexcept; + + JsonObj(const JsonObj& value); + + JsonObj& operator=(JsonObj&& value) noexcept; + + JsonObj& operator=(const JsonObj& value); + + operator const JsonVar*() const; + + operator JsonVar*(); + + UInt_64 Size() const; + + UInt_64 Extra() const; + + UInt_64 RawSize() const; + + bool HasVar(const UInt_64 hashId) const; + + bool HasVar(const Str_8& identifier) const; + + bool AddVar(const JsonVar& var); + + const JsonVar* GetVar(const UInt_64 hashId) const; + + const JsonVar* GetVar(const Str_8& identifier) const; + + JsonVar* GetVar(const UInt_64 hashId); + + JsonVar* GetVar(const Str_8& identifier); + + Str_8 ToStr(const UInt_64 level, const bool compact) const; + }; +} \ No newline at end of file diff --git a/include/ehs/json/JsonStr.h b/include/ehs/json/JsonStr.h new file mode 100644 index 0000000..f5cc501 --- /dev/null +++ b/include/ehs/json/JsonStr.h @@ -0,0 +1,33 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +#include "JsonBase.h" + +namespace ehs +{ + class JsonStr : public JsonBase + { + public: + Str_8 value; + + JsonStr(); + + JsonStr(Str_8 value); + + JsonStr(const Char_8* value, const UInt_64 size = 0); + + JsonStr(JsonStr&& js) noexcept; + + JsonStr(const JsonStr& js) = default; + + JsonStr& operator=(JsonStr&& js) noexcept; + + operator Str_8() const; + + operator Str_8&(); + + Str_8 ToStr(const UInt_64 level, const bool compact) const; + }; +} \ No newline at end of file diff --git a/include/ehs/json/JsonVar.h b/include/ehs/json/JsonVar.h new file mode 100644 index 0000000..14210c8 --- /dev/null +++ b/include/ehs/json/JsonVar.h @@ -0,0 +1,109 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +namespace ehs +{ + class JsonBase; + class JsonObj; + class JsonArray; + class JsonBool; + class JsonNum; + class JsonStr; + + class JsonVar + { + private: + UInt_64 hashId; + Str_8 id; + JsonBase* value; + + public: + virtual ~JsonVar(); + + JsonVar(); + + JsonVar(Str_8 id); + + JsonVar(Str_8 id, const JsonBase* const value); + + JsonVar(Str_8 id, const JsonBase& value); + + JsonVar(Str_8 id, const JsonObj& value); + + JsonVar(Str_8 id, const JsonArray& value); + + JsonVar(Str_8 id, const JsonBool& value); + + JsonVar(Str_8 id, const bool boolean); + + JsonVar(Str_8 id, const JsonNum& value); + + JsonVar(Str_8 id, const SInt_64 num); + + JsonVar(Str_8 id, const UInt_64 num); + + JsonVar(Str_8 id, const SInt_32 num); + + JsonVar(Str_8 id, const UInt_32 num); + + JsonVar(Str_8 id, const SInt_16 num); + + JsonVar(Str_8 id, const UInt_16 num); + + JsonVar(Str_8 id, const SInt_8 num); + + JsonVar(Str_8 id, const UInt_8 num); + + JsonVar(Str_8 id, const double num); + + JsonVar(Str_8 id, const float num); + + JsonVar(Str_8 id, const JsonStr& value); + + JsonVar(Str_8 id, const Char_8* str, const UInt_64 size = 0); + + JsonVar(Str_8 id, Str_8 str); + + JsonVar(JsonVar&& var) noexcept; + + JsonVar(const JsonVar& var); + + JsonVar& operator=(JsonVar&& var) noexcept; + + JsonVar& operator=(const JsonVar& var); + + UInt_64 GetHashId() const; + + Str_8 GetId() const; + + void SetValue(const JsonBase* const newValue); + + void SetValue(const JsonBase& newValue); + + void SetValue(const JsonObj& newValue); + + void SetValue(const JsonArray& newValue); + + void SetValue(const JsonBool& newValue); + + void SetValue(const bool newValue); + + void SetValue(const JsonNum& newValue); + + void SetValue(const float newValue); + + void SetValue(const JsonStr& newValue); + + void SetValue(const Char_8* newValue, const UInt_64 size = 0); + + void SetValue(const Str_8& newValue); + + const JsonBase* GetValue() const; + + JsonBase* GetValue(); + + Str_8 ToStr(const UInt_64 level, const bool compact) const; + }; +} \ No newline at end of file diff --git a/include/ehs/system/Architecture.h b/include/ehs/system/Architecture.h new file mode 100644 index 0000000..f854eb1 --- /dev/null +++ b/include/ehs/system/Architecture.h @@ -0,0 +1,11 @@ +#pragma once + +#if defined(_M_AMD64) || defined(_M_X64) || defined(__x86_64__) + #define EHS_LITTLE_ENDIAN + #define EHS_ARCH_X64 +#elif defined(_M_ARM64) || defined(__aarch64__) +#define EHS_LITTLE_ENDIAN + #define EHS_ARCH_ARM64 +#else + #error Unsupported architecture. +#endif \ No newline at end of file diff --git a/include/ehs/system/BaseMutex.h b/include/ehs/system/BaseMutex.h new file mode 100644 index 0000000..10229df --- /dev/null +++ b/include/ehs/system/BaseMutex.h @@ -0,0 +1,34 @@ +#pragma once + +#include "ehs/EHS.h" + +namespace ehs +{ + class BaseMutex + { + protected: + bool initialized; + bool locked; + + public: + virtual ~BaseMutex(); + + BaseMutex(); + + BaseMutex(const BaseMutex& mutex); + + BaseMutex& operator=(const BaseMutex& mutex); + + virtual void Initialize(); + + virtual void UnInitialize(); + + bool IsInitialized() const; + + virtual void Lock(); + + virtual void Unlock(); + + bool IsLocked() const; + }; +} \ No newline at end of file diff --git a/include/ehs/system/BaseOpen.h b/include/ehs/system/BaseOpen.h new file mode 100644 index 0000000..217eadb --- /dev/null +++ b/include/ehs/system/BaseOpen.h @@ -0,0 +1,35 @@ +#pragma once + +#include "ehs/Str.h" + +namespace ehs +{ + class BaseOpen + { + protected: + Str_8 filePath; + + public: + BaseOpen(); + + BaseOpen(Str_8 filePath); + + BaseOpen(BaseOpen&& bo) noexcept; + + BaseOpen(const BaseOpen& bo); + + BaseOpen& operator=(BaseOpen&& bo) noexcept; + + BaseOpen& operator=(const BaseOpen& bo); + + virtual void Initialize() = 0; + + virtual void Release() = 0; + + virtual void* Retrieve(Str_8 symbol) = 0; + + Str_8 GetFilePath() const; + + virtual bool IsInitialize() const = 0; + }; +} \ No newline at end of file diff --git a/include/ehs/system/BaseSemaphore.h b/include/ehs/system/BaseSemaphore.h new file mode 100644 index 0000000..4ef1eaa --- /dev/null +++ b/include/ehs/system/BaseSemaphore.h @@ -0,0 +1,43 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +namespace ehs +{ + class BaseSemaphore + { + private: + Str_8 name; + UInt_32 initial; + + public: + BaseSemaphore(); + + BaseSemaphore(Str_8 name, const UInt_32 initial); + + BaseSemaphore(const UInt_32 initial); + + BaseSemaphore(BaseSemaphore&& sem) noexcept; + + BaseSemaphore(const BaseSemaphore& sem); + + BaseSemaphore& operator=(BaseSemaphore&& sem) noexcept; + + BaseSemaphore& operator=(const BaseSemaphore& sem); + + virtual void Initialize() = 0; + + virtual void Release() = 0; + + virtual void Signal(const UInt_32 inc) = 0; + + virtual bool Wait(const UInt_32 timeout) = 0; + + Str_8 GetName() const; + + UInt_32 GetInitial() const; + + virtual bool IsValid() const = 0; + }; +} diff --git a/include/ehs/system/BaseSystem.h b/include/ehs/system/BaseSystem.h new file mode 100644 index 0000000..4818367 --- /dev/null +++ b/include/ehs/system/BaseSystem.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +namespace ehs +{ + class BaseSystem + { + public: + static void OpenURI(const Str_8& uri); + }; +} \ No newline at end of file diff --git a/include/ehs/system/CPU.h b/include/ehs/system/CPU.h new file mode 100644 index 0000000..e88d69d --- /dev/null +++ b/include/ehs/system/CPU.h @@ -0,0 +1,220 @@ +#pragma once + +#include "ehs/Types.h" +#include "Architecture.h" +#include "OS.h" +#include "ehs/Str.h" +#include "ehs/Array.h" + +namespace ehs +{ + enum class Architecture : UInt_8 + { + X64, + X86, + ARM64, + ARM, + UNKNOWN + }; + + enum class Endianness : UInt_8 + { + LE, + BE + }; + + struct TSC + { + UInt_32 coreId = 0; + UInt_32 highCount = 0; + UInt_32 lowCount = 0; + }; + + class CPU + { + private: + #ifdef EHS_OS_LINUX + static UInt_64 TSC_Freq; + #endif + + public: + static Architecture GetArchitecture(); + + static UInt_8 PointerSize(); + + static Endianness GetEndianness(); + + static void RDTSCP(TSC* tsc); + + static UInt_64 GetTSC_Freq(); + + static UInt_64 GetTSC(); + + /// Retrieves the CPU manufacturer id as a non null-terminated ASCII string. + /// @param[out] input A twelve byte character array representing the manufacturer id. + static void GetManufacturer(Char_8* input); + + static UInt_32 GetInfoBits(); + + static UInt_8 GetSteppingId(); + + static UInt_8 GetModelId(); + + static UInt_8 GetFamilyId(); + + static UInt_8 GetProcessorTypeId(); + + static UInt_8 GetExtModelId(); + + static UInt_8 GetExtFamilyId(); + + static UInt_32 GetFeatureBits_1(); + + static bool HasFPU(); + + static bool HasVME(); + + static bool HasDE(); + + static bool HasPSE(); + + static bool HasTSC(); + + static bool HasMSR(); + + static bool HasPAE(); + + static bool HasMCE(); + + static bool HasCX8(); + + static bool HasAPIC(); + + static bool HasSEP(); + + static bool HasMTRR(); + + static bool HasPGE(); + + static bool HasMCA(); + + static bool HasCMOV(); + + static bool HasPAT(); + + static bool HasPSE_36(); + + static bool HasPSN(); + + static bool HasCLFSH(); + + static bool HasDS(); + + static bool HasACPI(); + + static bool HasMMX(); + + static bool HasFXSR(); + + static bool HasSSE(); + + static bool HasSSE2(); + + static bool HasSS(); + + static bool HasHTT(); + + static bool HasTM(); + + static bool HasIA64(); + + static bool HasPBE(); + + static UInt_32 GetFeatureBits_2(); + + static bool HasSSE3(); + + static bool HasPCLMULQDQ(); + + static bool HasDTES64(); + + static bool HasMONITOR(); + + static bool HasDS_CPL(); + + static bool HasVMX(); + + static bool HasSMX(); + + static bool HasEST(); + + static bool HasTM2(); + + static bool HasSSSE3(); + + static bool HasCNXT_ID(); + + static bool HasSDBG(); + + static bool HasFMA(); + + static bool HasCX16(); + + static bool HasXTPR(); + + static bool HasPDCM(); + + static bool HasPCID(); + + static bool HasDCA(); + + static bool HasSSE4_1(); + + static bool HasSSE4_2(); + + static bool HasX2APIC(); + + static bool HasMOVBE(); + + static bool HasPOPCNT(); + + static bool HasTSC_DEADLINE(); + + static bool HasAES(); + + static bool HasXSAVE(); + + static bool HasOSXSAVE(); + + static bool HasAVX(); + + static bool HasF16C(); + + static bool HasRDRND(); + + static bool HasHYPERVISOR(); + + static UInt_32 GetExtFeatureBits_1(); + + static bool HasAVX2(); + + static bool HasRDSEED(); + + static bool HasADX(); + + static UInt_32 GetExtFeatureBits_2(); + + static UInt_32 GetExtFeatureBits_3(); + + /// Retrieves the CPU brand as a null-terminated ASCII string. + /// @param[out] input A 48 byte character array representing the brand. + static void GetBrand(Char_8* input); + + //static Str_8 ToStr(); + + private: + static UInt_64 RetrieveTSC_Freq(); + + static UInt_64 CalculateTSC_Freq(); + }; +} \ No newline at end of file diff --git a/include/ehs/system/FileSystem.h b/include/ehs/system/FileSystem.h new file mode 100644 index 0000000..6f41550 --- /dev/null +++ b/include/ehs/system/FileSystem.h @@ -0,0 +1,17 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +namespace ehs +{ + class FileSystem + { + public: + static void SetWorkingDir(const Str_8& dir); + + static Str_8 GetWorkingDir(); + + static void SetOwner(const Str_8& dir, const UInt_32 userId, const UInt_32 groupId); + }; +} diff --git a/include/ehs/system/Mutex.h b/include/ehs/system/Mutex.h new file mode 100644 index 0000000..900e4a2 --- /dev/null +++ b/include/ehs/system/Mutex.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ehs/EHS.h" + +#if defined(EHS_OS_WINDOWS) + #include "Mutex_W32.h" +#elif defined(EHS_OS_LINUX) + #include "Mutex_PT.h" +#endif \ No newline at end of file diff --git a/include/ehs/system/Mutex_PT.h b/include/ehs/system/Mutex_PT.h new file mode 100644 index 0000000..422b2aa --- /dev/null +++ b/include/ehs/system/Mutex_PT.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ehs/EHS.h" +#include "BaseMutex.h" + +#include + +namespace ehs +{ + class Mutex : public BaseMutex + { + private: + pthread_mutex_t hdl; + + public: + ~Mutex() override; + + Mutex(); + + Mutex(const Mutex& mutex); + + Mutex& operator=(const Mutex& mutex); + + void Initialize() override; + + void UnInitialize() override; + + void Lock() override; + + void Unlock() override; + }; +} \ No newline at end of file diff --git a/include/ehs/system/Mutex_W32.h b/include/ehs/system/Mutex_W32.h new file mode 100644 index 0000000..8e1a383 --- /dev/null +++ b/include/ehs/system/Mutex_W32.h @@ -0,0 +1,30 @@ +#pragma once + +#include "ehs/EHS.h" +#include "BaseMutex.h" + +namespace ehs +{ + class Mutex : public BaseMutex + { + private: + HANDLE hdl; + + public: + ~Mutex() override; + + Mutex(); + + Mutex(const Mutex& mutex); + + Mutex& operator=(const Mutex& mutex); + + void Initialize() override; + + void UnInitialize() override; + + void Lock() override; + + void Unlock() override; + }; +} \ No newline at end of file diff --git a/include/ehs/system/OS.h b/include/ehs/system/OS.h new file mode 100644 index 0000000..fe5db29 --- /dev/null +++ b/include/ehs/system/OS.h @@ -0,0 +1,27 @@ +#pragma once + +#if defined(_WIN32) || defined(_WIN64) +#define EHS_OS_WINDOWS + #define EHS_FILE __FILE__ + #define EHS_FUNC __FUNCTION__ + #define EHS_LINE __LINE__ + + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + #include + + typedef void* Handle; +#elif defined(__linux__) + #define EHS_OS_LINUX + #define EHS_FILE __FILE__ + #define EHS_FUNC __FUNCTION__ + #define EHS_LINE __LINE__ + +typedef int Handle; +#endif \ No newline at end of file diff --git a/include/ehs/system/Open.h b/include/ehs/system/Open.h new file mode 100644 index 0000000..ae63673 --- /dev/null +++ b/include/ehs/system/Open.h @@ -0,0 +1,9 @@ +#pragma once + +#include "OS.h" + +#if defined(EHS_OS_WINDOWS) + #include "Open_W32.h" +#elif defined(EHS_OS_LINUX) + #include "Open_UNX.h" +#endif \ No newline at end of file diff --git a/include/ehs/system/Open_UNX.h b/include/ehs/system/Open_UNX.h new file mode 100644 index 0000000..ce895be --- /dev/null +++ b/include/ehs/system/Open_UNX.h @@ -0,0 +1,35 @@ +#pragma once + +#include "BaseOpen.h" + +namespace ehs +{ + class Open : public BaseOpen + { + private: + void* hdl; + + public: + ~Open(); + + Open(); + + Open(Str_8 filePath); + + Open(Open&& o) noexcept; + + Open(const Open& o); + + Open& operator=(Open&& o) noexcept; + + Open& operator=(const Open& o); + + void Initialize() override; + + void Release() override; + + void* Retrieve(Str_8 symbol) override; + + bool IsInitialize() const override; + }; +} diff --git a/include/ehs/system/Open_W32.h b/include/ehs/system/Open_W32.h new file mode 100644 index 0000000..acf00b8 --- /dev/null +++ b/include/ehs/system/Open_W32.h @@ -0,0 +1,16 @@ +// +// Created by karutoh on 11/14/23. +// + +#ifndef OPEN_W32_H +#define OPEN_W32_H + +namespace ehs { + +class Open_W32 { + +}; + +} // lwe + +#endif //OPEN_W32_H diff --git a/include/ehs/system/Semaphore.h b/include/ehs/system/Semaphore.h new file mode 100644 index 0000000..afc6294 --- /dev/null +++ b/include/ehs/system/Semaphore.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef EHS_OS_WINDOWS +#include "Semaphore_W32.h" +#else +#include "Semaphore_P.h" +#endif \ No newline at end of file diff --git a/include/ehs/system/Semaphore_P.h b/include/ehs/system/Semaphore_P.h new file mode 100644 index 0000000..6ada2de --- /dev/null +++ b/include/ehs/system/Semaphore_P.h @@ -0,0 +1,44 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "BaseSemaphore.h" + +#include + +namespace ehs +{ + class Semaphore : public BaseSemaphore + { + private: + sem_t hdl; + bool valid; + + public: + ~Semaphore(); + + Semaphore(); + + Semaphore(const Str_8& name, const UInt_32 initial); + + Semaphore(const UInt_32 initial); + + Semaphore(Semaphore&& sem) noexcept; + + Semaphore(const Semaphore& sem); + + Semaphore& operator=(Semaphore&& sem) noexcept; + + Semaphore& operator=(const Semaphore& sem); + + void Initialize() override; + + void Release() override; + + bool Wait(const UInt_32 timeout) override; + + void Signal(const UInt_32 inc) override; + + bool IsValid() const override; + }; +} diff --git a/include/ehs/system/Semaphore_W32.h b/include/ehs/system/Semaphore_W32.h new file mode 100644 index 0000000..8735e3c --- /dev/null +++ b/include/ehs/system/Semaphore_W32.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" +#include "BaseSemaphore.h" + +namespace ehs +{ + class Semaphore : public BaseSemaphore + { + private: + HANDLE hdl; + + public: + ~Semaphore(); + + Semaphore(); + + Semaphore(Str_8 name, const UInt_32 initial); + + Semaphore(const UInt_32 initial); + + Semaphore(Semaphore&& sem) noexcept; + + Semaphore(const Semaphore& sem); + + Semaphore& operator=(Semaphore&& sem) noexcept; + + Semaphore& operator=(const Semaphore& sem); + + void Initialize() override; + + void Release() override; + + void Signal(const UInt_32 inc) override; + + bool Wait(const UInt_32 timeout) override; + + bool IsValid() const override; + }; +} diff --git a/include/ehs/system/System.h b/include/ehs/system/System.h new file mode 100644 index 0000000..3be8116 --- /dev/null +++ b/include/ehs/system/System.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ehs/EHS.h" + +#if defined(EHS_OS_WINDOWS) +#include "System_W32.h" +#elif defined(EHS_OS_LINUX) +#include "System_LNX.h" +#endif \ No newline at end of file diff --git a/include/ehs/system/System_LNX.h b/include/ehs/system/System_LNX.h new file mode 100644 index 0000000..e09dbe6 --- /dev/null +++ b/include/ehs/system/System_LNX.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ehs/EHS.h" +#include "BaseSystem.h" + +namespace ehs +{ + class System : public BaseSystem + { + public: + static void OpenURI(const Str_8& uri); + }; +} \ No newline at end of file diff --git a/include/ehs/system/System_W32.h b/include/ehs/system/System_W32.h new file mode 100644 index 0000000..8bf8109 --- /dev/null +++ b/include/ehs/system/System_W32.h @@ -0,0 +1,12 @@ +#pragma once + +#include "BaseSystem.h" + +namespace ehs +{ + class System : public BaseSystem + { + public: + static void OpenURI(const Str_8& uri); + }; +} \ No newline at end of file diff --git a/include/ehs/system/Thread.h b/include/ehs/system/Thread.h new file mode 100644 index 0000000..e73a4b7 --- /dev/null +++ b/include/ehs/system/Thread.h @@ -0,0 +1,148 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Serializer.h" +#include "ehs/Array.h" +#include "ehs/Log.h" +#include "ehs/Str.h" + +#ifdef EHS_OS_WINDOWS + #include +#endif + +namespace ehs +{ + #if defined(EHS_OS_WINDOWS) + #define EHS_INVALID_THREAD nullptr + typedef void* THandle; + #elif defined(EHS_OS_LINUX) + #define EHS_INVALID_THREAD 0 + typedef UInt_64 THandle; + #endif + + class Thread + { + private: + static UInt_32 mainId; + + #ifdef EHS_OS_WINDOWS + static Handle mainTaskHdl; + static UInt_32 mainTaskIndex; + #endif + + UInt_64 stackSize; + THandle hdl; + UInt_32 id; + + #ifdef EHS_OS_WINDOWS + Handle taskHdl; + UInt_32 taskIndex; + #endif + + public: + /// Frees any native handles. + ~Thread(); + + /// Default members initialization. + Thread(const UInt_64 stackSize = 0); + + /// Copies some members from the given thread object. + /// @param [in] thread The thread object to copy from. + Thread(const Thread& thread); + + /// Copies some members from the given thread object. + /// @param [in] thread The thread object to copy from. + /// @returns The thread that has been assigned to. + Thread& operator=(const Thread& thread); + + /// Creates a thread handle and starts it with the given method. + /// @param cb The function to run on the thread. + /// @param args Raw data to send over to the thread. + void Start(UInt_32 (*cb)(void*), void* args); + + /// Blocks the calling thread until the referenced thread is finished. + /// @param timeout The time to wait for before moving on. + /// @note Pass "EHS_INFINITE" to wait until the thread is finished. + bool Join(const unsigned int timeout = EHS_INFINITE); + + /// Detaches the referenced thread, removing ownership. + void Detach(); + + /// Retrieves the given stack size available to the referenced thread. + /// @returns The stack size. + UInt_64 GetStackSize() const; + + /// Retrieves the native thread handle. + /// @returns The native handle. + THandle GetHandle() const; + + /// Retrieves the thread's id. + /// @returns The id. + UInt_32 GetId() const; + + /// Checks whether or not the calling thread is the referenced thread. + /// @returns The result. + bool IsCurrent() const; + + bool IsValid() const; + + #ifdef EHS_OS_WINDOWS + /// Adjusts the thread's performance based on the type of task provided, that's going to be executed. + /// @param[in] task A task name from the provided list in the registry. + /// @note A list of tasks are in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks. + void SetTaskType_32(const Str_32& task); + + /// Adjusts the thread's performance based on the type of task provided, that's going to be executed. + /// @param[in] task A task name from the provided list in the registry. + /// @note A list of tasks are in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks. + void SetTaskType_16(const Str_16& task); + + /// Adjusts the thread's performance based on the type of task provided, that's going to be executed. + /// @param[in] task A task name from the provided list in the registry. + /// @note A list of tasks are in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks. + void SetTaskType_8(const Str_8& task); + + void RevertTaskType(); + #endif + + /// Gets the main thread id. + static UInt_32 GetMainId(); + + /// Retrieves the calling thread's id. + /// @returns The id. + static UInt_64 GetCurrentId(); + + #ifdef EHS_OS_WINDOWS + /// Adjusts the main thread's performance based on the type of task provided, that's going to be executed. + /// @param[in] task A task name from the provided list in the registry. + /// @note A list of tasks are in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks. + static void SetMainTaskType_32(const Str_32& task); + + /// Adjusts the main thread's performance based on the type of task provided, that's going to be executed. + /// @param[in] task A task name from the provided list in the registry. + /// @note A list of tasks are in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks. + static void SetMainTaskType_16(const Str_16& task); + + /// Adjusts the main thread's performance based on the type of task provided, that's going to be executed. + /// @param[in] task A task name from the provided list in the registry. + /// @note A list of tasks are in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Multimedia\SystemProfile\Tasks. + static void SetMainTaskType_8(const Str_8& task); + + static void RevertMainTaskType(); + #endif + + /// Causes the calling thread to wait until the given time using a while loop and the CPU clock. + /// @param seconds The time in seconds to wait for. + /// @returns The total elapsed time slept for due to this not being a 100% accurate. + /// @warning Use "SleepFor" instead unless accuracy is required because this does not do other tasks while sleeping. + static float HardSleepFor(const float seconds); + + /// Causes the calling thread to wait until the given time using native sleep calls. + /// @param miliseconds The time in miliseconds to wait for. + /// @warning This is not at all accurate. + static void SleepFor(const UInt_32 miliseconds); + + private: + static void* Redirect(void* args); + }; +} \ No newline at end of file diff --git a/include/ehs/system/User.h b/include/ehs/system/User.h new file mode 100644 index 0000000..f91b633 --- /dev/null +++ b/include/ehs/system/User.h @@ -0,0 +1,15 @@ +#pragma once + +#include "ehs/EHS.h" +#include "ehs/Str.h" + +namespace ehs +{ + class User + { + public: + static void GetId(UInt_32* const real, UInt_32* const effective, UInt_32* const saved); + + static Str_8 GetName(); + }; +} \ No newline at end of file diff --git a/src/Base64.cpp b/src/Base64.cpp new file mode 100644 index 0000000..852bfb1 --- /dev/null +++ b/src/Base64.cpp @@ -0,0 +1,104 @@ +#include "ehs/Base64.h" + +namespace ehs +{ + const char Base64::ascii[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + Str_8 Base64::Encode(const Str_8 input) + { + UInt_64 input_length = input.Size(); + + // Calculate the output length + UInt_64 output_length = 4 * ((input_length + 2) / 3); + + // Allocate memory for the output + Str_8 result(output_length); + + // Loop through the input and fill the output + for (int i = 0, j = 0; i < input_length;) { + + // Take first byte and shift right by 2 bits + UInt_32 octet_a = i < input_length ? input[i++] : 0; + UInt_32 octet_b = i < input_length ? input[i++] : 0; + UInt_32 octet_c = i < input_length ? input[i++] : 0; + + UInt_32 triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + // Encode the 24-bits into four 6-bits integers + result[j++] = ascii[(triple >> 3 * 6) & 0x3F]; + result[j++] = ascii[(triple >> 2 * 6) & 0x3F]; + result[j++] = ascii[(triple >> 1 * 6) & 0x3F]; + result[j++] = ascii[(triple >> 0 * 6) & 0x3F]; + } + + // Add padding '=' + if (input_length % 3 == 1) { + result[output_length - 1] = '='; + result[output_length - 2] = '='; + } else if (input_length % 3 == 2) { + result[output_length - 1] = '='; + } + + return result; + } + + Str_8 Base64::Decode(const Str_8 input) + { + UInt_64 in_len = input.Size(); + int i = 0; + int j = 0; + int in_ = 0; + char char_array_4[4], char_array_3[3]; + Str_8 ret; + + while (in_len-- && ( input[in_] != '=') && IsBase64(input[in_])) + { + char_array_4[i++] = input[in_]; in_++; + + if (i ==4) + { + for (i = 0; i <4; i++) + char_array_4[i] = Find(char_array_4[i]); + + char_array_3[0] = ( char_array_4[0] << 2 ) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + + i = 0; + } + } + + if (i) + { + for (j = 0; j < i; j++) + char_array_4[j] = Find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + + for (j = 0; (j < i - 1); j++) + ret += char_array_3[j]; + } + + return ret; + } + + char Base64::Find(const char c) + { + for (char i = 0; i < (char)sizeof(ascii); ++i) + { + if (ascii[i] == c) + return i; + } + + return EHS_SINT_8_MAX; + } + + bool Base64::IsBase64(const char c) + { + return (c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96 && c < 123) || (c == '+') || (c == '/'); + } +} \ No newline at end of file diff --git a/src/BaseObj.cpp b/src/BaseObj.cpp new file mode 100644 index 0000000..b905213 --- /dev/null +++ b/src/BaseObj.cpp @@ -0,0 +1,154 @@ +#include "ehs/BaseObj.h" + +namespace ehs +{ + BaseObj::~BaseObj() + { + delete[] hierarchy; + } + + BaseObj::BaseObj() + : hierarchy(nullptr), hierarchySize(0) + { + AddType("BaseObj"); + } + + BaseObj::BaseObj(BaseObj&& base) noexcept + : hierarchy(base.hierarchy), hierarchySize(base.hierarchySize) + { + base.hierarchy = nullptr; + base.hierarchySize = 0; + } + + BaseObj::BaseObj(const BaseObj& base) + : hierarchy(new Type[base.hierarchySize]), hierarchySize(base.hierarchySize) + { + for (UInt_64 i = 0; i < hierarchySize; i++) + hierarchy[i] = base.hierarchy[i]; + } + + BaseObj& BaseObj::operator=(BaseObj&& base) noexcept + { + if (this == &base) + return *this; + + delete[] hierarchy; + + hierarchy = base.hierarchy; + hierarchySize = base.hierarchySize; + + base.hierarchy = nullptr; + base.hierarchySize = 0; + + return *this; + } + + BaseObj& BaseObj::operator=(const BaseObj& base) + { + if (this == &base) + return *this; + + delete[] hierarchy; + + hierarchy = new Type[base.hierarchySize]; + for (UInt_64 i = 0; i < base.hierarchySize; i++) + hierarchy[i] = base.hierarchy[i]; + + hierarchySize = base.hierarchySize; + + return *this; + } + + bool BaseObj::operator==(const BaseObj& base) const + { + if (hierarchySize != base.hierarchySize) + return false; + + for (UInt_64 i = 0; i < hierarchySize; i++) + if (hierarchy[i] != base.hierarchy[i]) + return false; + + return true; + } + + bool BaseObj::operator!=(const BaseObj& base) const + { + if (hierarchySize != base.hierarchySize) + return true; + + for (UInt_64 i = 0; i < hierarchySize; i++) + if (hierarchy[i] != base.hierarchy[i]) + return true; + + return false; + } + + const Type* BaseObj::GetHierarchy() const + { + return hierarchy; + } + + UInt_64 BaseObj::GetHierarchySize() const + { + return hierarchySize; + } + + bool BaseObj::HasType(UInt_64 typeHashId) const + { + for (UInt_64 i = 0; i < hierarchySize; i++) + if (hierarchy[i] == typeHashId) + return true; + + return false; + } + + bool BaseObj::HasType(const Char_8* typeId) const + { + return HasType(Type::GenHash(typeId, Type::CalcSize(typeId))); + } + + Type BaseObj::GetType() const + { + return hierarchy[0]; + } + + UInt_64 BaseObj::GetTypeIdSize() const + { + return hierarchy[0].GetSize(); + } + + const Char_8* BaseObj::GetTypeId() const + { + return hierarchy[0].GetId(); + } + + UInt_64 BaseObj::GetTypeHashId() const + { + return hierarchy[0].GetHashId(); + } + + BaseObj* BaseObj::Clone() const + { + return new BaseObj(*this); + } + + void BaseObj::AddType(const Char_8* const id) + { + Type newType(id); + + for (UInt_64 i = 0; i < hierarchySize; i++) + if (hierarchy[i] == newType) + return; + + Type* result = new Type[hierarchySize + 1]; + + result[0] = (Type&&)newType; + + for (UInt_64 i = 0; i < hierarchySize; i++) + result[i + 1] = (Type&&)hierarchy[i]; + + hierarchySize++; + delete[] hierarchy; + hierarchy = result; + } +} \ No newline at end of file diff --git a/src/Color3.cpp b/src/Color3.cpp new file mode 100644 index 0000000..451375c --- /dev/null +++ b/src/Color3.cpp @@ -0,0 +1,100 @@ +#include "ehs/Color3.h" +#include "ehs/Math.h" + +namespace ehs +{ + Color3::Color3() + : r(0.0f), g(0.0f), b(0.0f) + { + } + + Color3::Color3(const float scalar) + : r(Math::Clamp(scalar, 0.0f, 1.0f)), g(Math::Clamp(scalar, 0.0f, 1.0f)), b(Math::Clamp(scalar, 0.0f, 1.0f)) + { + } + + Color3::Color3(const float r, const float g, const float b) + : r(Math::Clamp(r, 0.0f, 1.0f)), g(Math::Clamp(g, 0.0f, 1.0f)), b(Math::Clamp(b, 0.0f, 1.0f)) + { + } + + Color3::Color3(const Color3& color) + : r(color.r), g(color.g), b(color.b) + { + } + + Color3& Color3::operator=(const float scalar) + { + r = scalar; + g = scalar; + b = scalar; + + return *this; + } + + Color3& Color3::operator=(const Color3& color) + { + if (this == &color) + return *this; + + r = color.r; + g = color.g; + b = color.b; + + return *this; + } + + bool Color3::operator==(const Color3& color) const + { + return r == color.r && g == color.g && b == color.b; + } + + bool Color3::operator!=(const Color3& color) const + { + return r != color.r || g != color.g || b != color.b; + } + + float Color3::operator[](const UInt_64 i) const + { + switch (i) + { + case 0: + return r; + case 1: + return g; + case 2: + return b; + default: + return r; + } + } + + float& Color3::operator[](const UInt_64 i) + { + switch (i) + { + case 0: + return r; + case 1: + return g; + case 2: + return b; + default: + return r; + } + } + + Color3& Color3::operator*=(const Color3& color) + { + r *= color.r; + g *= color.g; + b *= color.b; + + return *this; + } + + Color3 Color3::operator*(const Color3& color) const + { + return {r * color.r, g * color.g, b * color.b}; + } +} \ No newline at end of file diff --git a/src/Color4.cpp b/src/Color4.cpp new file mode 100644 index 0000000..9567a4d --- /dev/null +++ b/src/Color4.cpp @@ -0,0 +1,121 @@ +#include "ehs/Color4.h" +#include "ehs/Math.h" + +namespace ehs +{ + Color4::Color4() + : r(0.0f), g(0.0f), b(0.0f), a(1.0f) + { + } + + Color4::Color4(const float scalar) + : r(Math::Clamp(scalar, 0.0f, 1.0f)), g(Math::Clamp(scalar, 0.0f, 1.0f)), b(Math::Clamp(scalar, 0.0f, 1.0f)), a(1.0f) + { + } + + Color4::Color4(const Color3& color) + : r(color.r), g(color.g), b(color.b), a(1.0f) + { + } + + Color4::Color4(const float r, const float g, const float b, const float a) + : r(Math::Clamp(r, 0.0f, 1.0f)), g(Math::Clamp(g, 0.0f, 1.0f)), b(Math::Clamp(b, 0.0f, 1.0f)), a(Math::Clamp(a, 0.0f, 1.0f)) + { + } + + Color4::Color4(const Color4& color) + : r(color.r), g(color.g), b(color.b), a(color.a) + { + } + + Color4& Color4::operator=(const float scalar) + { + r = scalar; + g = scalar; + b = scalar; + a = 1.0f; + + return *this; + } + + Color4& Color4::operator=(const Color3& color) + { + r = color.r; + g = color.g; + b = color.b; + a = 1.0f; + + return *this; + } + + Color4& Color4::operator=(const Color4& color) + { + if (this == &color) + return *this; + + r = color.r; + g = color.g; + b = color.b; + a = color.a; + + return *this; + } + + bool Color4::operator==(const Color4& color) const + { + return r == color.r && g == color.g && b == color.b && a == color.a; + } + + bool Color4::operator!=(const Color4& color) const + { + return r != color.r || g != color.g || b != color.b || a != color.a; + } + + float Color4::operator[](const UInt_64 i) const + { + switch (i) + { + case 0: + return r; + case 1: + return g; + case 2: + return b; + case 3: + return a; + default: + return r; + } + } + + float& Color4::operator[](const UInt_64 i) + { + switch (i) + { + case 0: + return r; + case 1: + return g; + case 2: + return b; + case 3: + return a; + default: + return r; + } + } + + Color4& Color4::operator*=(const Color4& color) + { + r *= color.r; + g *= color.g; + b *= color.b; + + return *this; + } + + Color4 Color4::operator*(const Color4& color) const + { + return {r * color.r, g * color.g, b * color.b, a}; + } +} \ No newline at end of file diff --git a/src/Data.cpp b/src/Data.cpp new file mode 100644 index 0000000..ffbd0b3 --- /dev/null +++ b/src/Data.cpp @@ -0,0 +1,5 @@ +#include "ehs/Data.h" + +namespace ehs +{ +} \ No newline at end of file diff --git a/src/DataType.cpp b/src/DataType.cpp new file mode 100644 index 0000000..119cfa7 --- /dev/null +++ b/src/DataType.cpp @@ -0,0 +1,77 @@ +#include "ehs/DataType.h" + +namespace ehs +{ + DataType FromAudioBitDepth(const UInt_16 bitDepth) + { + switch (bitDepth / 8) + { + case 1: + return DataType::SINT_8; + case 2: + return DataType::SINT_16; + case 3: + return DataType::SINT_24; + case 4: + return DataType::SINT_32; + default: + return DataType::SINT_8; + } + } + + UInt_8 ToByteDepth(const DataType type) + { + switch (type) + { + case DataType::LDOUBLE: + return 16; + case DataType::DOUBLE: + case DataType::SINT_64: + case DataType::UINT_64: + return 8; + case DataType::FLOAT: + case DataType::SINT_32: + case DataType::UINT_32: + return 4; + case DataType::SINT_24: + case DataType::UINT_24: + return 3; + case DataType::SINT_16: + case DataType::UINT_16: + return 2; + case DataType::SINT_8: + case DataType::UINT_8: + return 1; + default: + return 0; + } + } + + UInt_8 ToBitDepth(const DataType type) + { + switch (type) + { + case DataType::LDOUBLE: + return 128; + case DataType::DOUBLE: + case DataType::SINT_64: + case DataType::UINT_64: + return 64; + case DataType::FLOAT: + case DataType::SINT_32: + case DataType::UINT_32: + return 32; + case DataType::SINT_24: + case DataType::UINT_24: + return 24; + case DataType::SINT_16: + case DataType::UINT_16: + return 16; + case DataType::SINT_8: + case DataType::UINT_8: + return 8; + default: + return 0; + } + } +} \ No newline at end of file diff --git a/src/EHS.cpp b/src/EHS.cpp new file mode 100644 index 0000000..e2a49a7 --- /dev/null +++ b/src/EHS.cpp @@ -0,0 +1,666 @@ +#include "ehs/EHS.h" +#include "ehs/Log.h" +#include "ehs/Version.h" +#include "ehs/GarbageCollector.h" +#include "ehs/io/audio/Audio.h" +#include "ehs/io/img/Img.h" +#include "ehs/io/img/PNG.h" +#include "ehs/io/RIFF.h" + +#include + +#if defined(EHS_OS_LINUX) + #include +#endif + +namespace ehs +{ + constexpr Char_32 name_32[] = U"Event Horizon Suite"; + constexpr Char_16 name_16[] = L"Event Horizon Suite"; + constexpr Char_8 name_8[] = "Event Horizon Suite"; + constexpr Char_32 acronym_32[] = U"EHS"; + constexpr Char_16 acronym_16[] = L"EHS"; + constexpr Char_8 acronym_8[] = "EHS"; + constexpr Char_32 versionId_32[] = U"Release"; + constexpr Char_16 versionId_16[] = L"Release"; + constexpr Char_8 versionId_8[] = "Release"; + + Str_8 appName; + Str_8 appVerId; + Version appVer; + + const Char_32* GetName_32() + { + return name_32; + } + + const Char_16* GetName_16() + { + return name_16; + } + + const Char_8* GetName_8() + { + return name_8; + } + + Str_8 GetAppName_8() + { + return appName; + } + + const Char_32* GetAcronym_32() + { + return acronym_32; + } + + const Char_16* GetAcronym_16() + { + return acronym_16; + } + + const Char_8* GetAcronym_8() + { + return acronym_8; + } + + const Char_32* GetVersionId_32() + { + return versionId_32; + } + + const Char_16* GetVersionId_16() + { + return versionId_16; + } + + const Char_8* GetVersionId_8() + { + return versionId_8; + } + + Str_8 GetAppVersionId_8() + { + return appVerId; + } + + Version GetVersion() + { + return {1, 2, 0}; + } + + Version GetAppVersion() + { + return appVer; + } + + bool DecodeWAV(const ehs::AudioCodec* const codec, ehs::Serializer& in, ehs::Audio* out) + { + RIFF riff(in); + + if (riff.GetType() != "WAVE") + { + EHS_LOG_INT("Error", 0, "Data is not in WAVE format."); + return false; + } + + RIFF_Chunk fmt = riff.GetChunk("fmt "); + if (!fmt.IsValid()) + { + EHS_LOG_INT("Error", 1, "Wave does not have a format chunk."); + return false; + } + + Serializer<> fmtSer = fmt.GetData(); + + RIFF_Chunk dChunk = riff.GetChunk("data"); + if (!dChunk.IsValid()) + { + EHS_LOG_INT("Error", 2, "Wave does not have a data chunk."); + return false; + } + + UInt_16 compression = fmtSer.Read(); + if (compression == 0x2) + { + EHS_LOG_INT("Error", 3, "Microsoft ADPCM compression unsupported."); + return false; + } + else if (compression == 0x6) + { + EHS_LOG_INT("Error", 4, "ITU G.711 a-law compression unsupported."); + return false; + } + else if (compression == 0x7) + { + EHS_LOG_INT("Error", 5, "ITU G.711 µ-law compression unsupported."); + return false; + } + else if (compression == 0x11) + { + EHS_LOG_INT("Error", 6, "IMA ADPCM compression unsupported."); + return false; + } + else if (compression == 0x16) + { + EHS_LOG_INT("Error", 7, "TU G.723 ADPCM (Yamaha) compression unsupported."); + return false; + } + else if (compression == 0x31) + { + EHS_LOG_INT("Error", 8, "GSM 6.10 compression unsupported."); + return false; + } + else if (compression == 0x40) + { + EHS_LOG_INT("Error", 9, "ITU G.721 ADPCM compression unsupported."); + return false; + } + else if (compression == 0x50) + { + EHS_LOG_INT("Error", 10, "MPEG compression unsupported."); + return false; + } + else if (compression == 0xFFFF) + { + EHS_LOG_INT("Error", 11, "Experimental compression unsupported."); + return false; + } + else if (compression != 0x1 && compression != 0x3) + { + EHS_LOG_INT("Error", 12, "Wave has unknown compression of " + Str_8::FromNum(compression) + "."); + return false; + } + + UInt_16 channels = fmtSer.Read(); + UInt_32 sampleRate = fmtSer.Read(); + fmtSer.SetOffset(fmtSer.GetOffset() + 6); + UInt_8 byteDepth = (UInt_8)(fmtSer.Read() / 8); + + DataType dataType; + if (byteDepth == 1) + dataType = DataType::SINT_8; + else if (byteDepth == 2) + dataType = DataType::SINT_16; + else if (byteDepth == 3) + dataType = DataType::SINT_24; + else if (byteDepth == 4 && compression == 0x3) + dataType = DataType::FLOAT; + else if (byteDepth == 4) + dataType = DataType::SINT_32; + else if (byteDepth == 8) + dataType = DataType::SINT_64; + else + return false; + + UInt_64 size = dChunk.GetData().Size(); + UInt_64 frames = size / byteDepth / channels; + + *out = std::move(Audio(out->GetId(), sampleRate, dataType, channels, frames)); + + Serializer<> dataSer = dChunk.GetData(); + + for (UInt_32 i = 0; i < dataSer.Size(); i += byteDepth) + { + if (byteDepth == 1) + { + *(SInt_8*)&(*out)[i] = dataSer.Read(); + if ((*out)[i] > *(SInt_8*)out->GetPeak()) + out->SetPeak(sizeof(SInt_8), &(*out)[i]); + } + else if (byteDepth == 2) + { + *(SInt_16*)&(*out)[i] = dataSer.Read(); + if (*(SInt_16*)&(*out)[i] > *(SInt_16*)out->GetPeak()) + out->SetPeak(sizeof(SInt_16), &(*out)[i]); + } + else if (byteDepth == 3) + { + *(SInt_16*)&(*out)[i + 1] = dataSer.Read(); + (*out)[i] = dataSer.Read(); + + SInt_32 signal = 0; + signal |= (*out)[i]; + signal |= (*out)[i + 1] << 8; + signal |= (*out)[i + 2] << 16; + + SInt_32 peak = 0; + peak |= out->GetPeak()[0]; + peak |= out->GetPeak()[1] << 8; + peak |= out->GetPeak()[2] << 16; + + if (signal > peak) + out->SetPeak(3, &(*out)[i]); + } + else if (byteDepth == 4 && compression == 0x3) + { + *(float*)&(*out)[i] = dataSer.Read(); + if (*(float*)&(*out)[i] > *(float*)out->GetPeak()) + out->SetPeak(sizeof(float), &(*out)[i]); + } + else if (byteDepth == 4) + { + *(SInt_32*)&(*out)[i] = dataSer.Read(); + if (*(SInt_32*)&(*out)[i] > *(SInt_32*)out->GetPeak()) + out->SetPeak(sizeof(SInt_32), &(*out)[i]); + } + else if (byteDepth == 8) + { + *(SInt_64*)&(*out)[i] = dataSer.Read(); + if (*(SInt_64*)&(*out)[i] > *(SInt_64*)out->GetPeak()) + out->SetPeak(sizeof(SInt_64), &(*out)[i]); + } + } + + return true; + } + + bool EncodeEHA(const ehs::AudioCodec* const codec, ehs::Serializer& out, const ehs::Audio* in) + { + Serializer result(codec->GetEndianness()); + result.WriteVersion({1, 0, 0}); + result.Write(in->GetSampleRate()); + result.Write(in->GetDataType()); + result.Write(in->GetByteDepth()); + result.Write(in->GetChannels()); + result.Write(in->GetFrameCount()); + + UInt_64 size = in->GetSize(); + UInt_8 byteDepth = in->GetByteDepth(); + + result.Resize(result.Size() + size + byteDepth); + Util::Copy(&result[result.GetOffset()], &in[0], size); + result.SetOffset(result.GetOffset() + size); + + Util::Copy(&result[result.GetOffset()], in->GetPeak(), byteDepth); + + return true; + } + + bool DecodeEHA(const ehs::AudioCodec* const codec, ehs::Serializer& in, ehs::Audio* out) + { + Version version = in.ReadVersion(); + if (version != Version(1, 0, 0)) + { + EHS_LOG_INT("Error", 0, "Incompatible audio file version."); + return false; + } + + UInt_64 sampleRate = in.Read(); + DataType dataType = in.Read(); + UInt_8 byteDepth = in.Read(); + UInt_8 channels = in.Read(); + UInt_64 frames = in.Read(); + + *out = Audio(out->GetId(), sampleRate, dataType, channels, frames); + + UInt_64 size = out->GetSize(); + Util::Copy(&(*out)[0], &in[in.GetOffset()], size); + in.SetOffset(in.GetOffset() + size); + + out->SetPeak(byteDepth, &in[in.GetOffset()]); + + return true; + } + + bool DecodePNG(const ehs::ImgCodec* const codec, ehs::Serializer& in, ehs::Img* out) + { + PNG png(out->GetId(), in); + + PNG_Chunk* ihdr = png.GetChunk("IHDR"); + Serializer* ihdrData = ihdr->GetData(); + + UInt_32 width = ihdrData->Read(); + UInt_32 height = ihdrData->Read(); + UInt_8 bitDepth = ihdrData->Read(); + + UInt_8 colorType = ihdrData->Read(); + if (colorType == 3) + { + EHS_LOG_INT("Error", 1, "Color type of " + Str_8::FromNum(colorType) + " is unsupported."); + return false; + } + + UInt_8 channels = 1; + if (colorType == 2) + channels = 3; + else if (colorType == 4) + channels = 2; + else if (colorType == 6) + channels = 4; + + *out = Img(out->GetId(), bitDepth, channels, {width, height}); + + UInt_8 compression = ihdrData->Read(); + if (compression) + { + EHS_LOG_INT("Error", 2, "Compression method of " + Str_8::FromNum(compression) + " is unsupported."); + return false; + } + + UInt_8 filter = ihdrData->Read(); + if (filter) + { + EHS_LOG_INT("Error", 3, "Filter method of " + Str_8::FromNum(filter) + " is unsupported."); + return false; + } + + UInt_8 interlaced = ihdrData->Read(); + if (interlaced) + { + EHS_LOG_INT("Error", 4, "Interlacing method of " + Str_8::FromNum(interlaced) + " is unsupported."); + return false; + } + + UInt_32 scanline = width * (bitDepth / 8) * channels; + UInt_32 scanLineF = scanline + 1; + UInt_32 bufferSize = scanline * height + height; + + Byte* buffer = new Byte[bufferSize]; + + PNG_Chunk* idat = png.GetChunk("IDAT"); + Serializer* idatData = idat->GetData(); + + z_stream strm = {}; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = idatData->Size(); + strm.next_in = *idatData; + strm.avail_out = bufferSize; + strm.next_out = buffer; + + int code = inflateInit(&strm); + if (code != Z_OK) + { + EHS_LOG_INT("Error", 5, "Failed to initialize zlib inflate with error #" + Str_8::FromNum(code) + "."); + delete[] buffer; + return false; + } + + do + { + code = inflate(&strm, Z_NO_FLUSH); + if (code != Z_STREAM_END && code != Z_OK) + { + EHS_LOG_INT("Error", 6, "Failed to zlib inflate with error #" + Str_8::FromNum(code) + "."); + delete[] buffer; + return false; + } + } while (strm.avail_out); + + code = inflateEnd(&strm); + if (code != Z_OK) + { + EHS_LOG_INT("Error", 7, "Failed to uninitialize zlib inflate with error #" + Str_8::FromNum(code) + "."); + delete[] buffer; + return false; + } + + for (UInt_32 i = 0, o = 0; i < bufferSize; i += scanLineF, o += scanline) + { + UInt_8 fCode = buffer[i]; + if (fCode == 0) + PNG::FilterNone(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline); + else if (fCode == 1) + PNG::FilterSub(&buffer[i + 1], &(*out)[o], bitDepth, channels, scanline); + else if (fCode == 2) + PNG::FilterUp(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); + else if (fCode == 3) + PNG::FilterAverage(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); + else if (fCode == 4) + PNG::FilterPaeth(&buffer[i + 1], &(*out)[o - scanline], bitDepth, channels, scanline); + } + + delete[] buffer; + + return true; + } + + bool EncodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer& out, const ehs::Img* in) + { + UInt_8 channels = in->GetChannels(); + Vec2_u64 resolution = in->GetResolution(); + + UInt_32 px_len = resolution.x * resolution.y * channels; + UInt_32 px_end = px_len - channels; + + Byte index[256]; + for (UInt_64 i = 0; i < 64; ++i) + *(UInt_32*)&index[i * 4] = 0; + + Byte prevPixel[4] = {0, 0, 0, 255}; + Byte pixel[4] = {0, 0, 0, 255}; + + Serializer result(Endianness::BE, resolution.x * resolution.y * (channels + 1) + 22); + + result.Write('q'); + result.Write('o'); + result.Write('i'); + result.Write('f'); + result.Write(resolution.x); + result.Write(resolution.y); + result.Write(in->GetChannels()); + result.Write(1); + + for (UInt_32 px_pos = 0, run = 0; px_pos < px_len; px_pos += channels) + { + if (channels == 4) + { + *(UInt_32*)pixel = *(UInt_32*)&(*in)[px_pos]; + } + else + { + pixel[0] = (*in)[px_pos]; + pixel[1] = (*in)[px_pos + 1]; + pixel[2] = (*in)[px_pos + 2]; + } + + if (*(UInt_32*)pixel == *(UInt_32*)prevPixel) + { + run++; + if (run == 62 || px_pos == px_end) + { + result.Write(0xc0 | (run - 1)); + run = 0; + } + } + else + { + if (run > 0) + { + result.Write(0xc0 | (run - 1)); + run = 0; + } + + UInt_32 index_pos = (prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels; + + if (*(UInt_32*)&index[index_pos] == *(UInt_32*)pixel) + { + result.Write(0x00 | (index_pos / channels)); + } + else + { + *(UInt_32*)&index[index_pos] = *(UInt_32*)pixel; + + if (pixel[3] == prevPixel[3]) + { + SInt_8 vr = pixel[0] - prevPixel[0]; + SInt_8 vg = pixel[1] - prevPixel[1]; + SInt_8 vb = pixel[2] - prevPixel[2]; + + SInt_8 vg_r = vr - vg; + SInt_8 vg_b = vb - vg; + + if ( + vr > -3 && vr < 2 && + vg > -3 && vg < 2 && + vb > -3 && vb < 2 + ) + { + result.Write(0x40 | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2)); + } + else if ( + vg_r > -9 && vg_r < 8 && + vg > -33 && vg < 32 && + vg_b > -9 && vg_b < 8 + ) + { + result.Write(0x80 | (vg + 32)); + result.Write((vg_r + 8) << 4 | (vg_b + 8)); + } + else + { + result.Write(0xfe); + result.Write(pixel[0]); + result.Write(pixel[1]); + result.Write(pixel[2]); + } + } + else + { + result.Write(0xff); + result.SetEndianness(CPU::GetEndianness()); + result.Write(*(UInt_32*)pixel); + result.SetEndianness(Endianness::BE); + } + } + } + + *(UInt_32*)prevPixel = *(UInt_32*)pixel; + } + + result.Write(0x100000000000000); + + return true; + } + + bool DecodeQOI(const ehs::ImgCodec* const codec, ehs::Serializer& in, ehs::Img* out) + { + Str_8 imgType = in.ReadStr(4); + if (imgType != "qoif") + { + EHS_LOG_INT("Error", 0, "Given data is not in the qoif format."); + return false; + } + + UInt_64 width = in.Read(); + UInt_64 height = in.Read(); + + UInt_8 channels = in.Read(); + channels = 4; + UInt_8 space = in.Read(); + UInt_8 bitDepth = 8; + + UInt_64 size = width * channels * height; + + *out = Img(out->GetId(), bitDepth, channels, {width, height}); + + Byte prevPixel[4] = {0, 0, 0, 255}; + + Byte index[256]; + for (UInt_64 i = 0; i < 64; ++i) + *(UInt_32*)&index[i * 4] = 0; + + UInt_32 chunksLen = in.Size() - 8; + for (UInt_32 pos = 0, run = 0; pos < size; pos += channels) + { + if (run > 0) + --run; + else if (in.GetOffset() < chunksLen) + { + UInt_32 chunkType = (UInt_32)in.Read(); + if (chunkType == 0xfe) // RGB + { + prevPixel[0] = in.Read(); // R-value + prevPixel[1] = in.Read(); // G-value + prevPixel[2] = in.Read(); // B-value + } + else if (chunkType == 0xff) // RGBA + { + *(UInt_32*)prevPixel = in.Read(); + } + else if ((chunkType & 0xc0) == 0x00) // Index + { + *(UInt_32*)prevPixel = *(UInt_32*)&index[chunkType * channels]; + } + else if ((chunkType & 0xc0) == 0x40) // Diff + { + prevPixel[0] += ((chunkType >> 4) & 0x03) - 2; // R-value + prevPixel[1] += ((chunkType >> 2) & 0x03) - 2; // G-value + prevPixel[2] += (chunkType & 0x03) - 2; // B-value + } + else if ((chunkType & 0xc0) == 0x80) // Luma + { + UInt_32 mod = (UInt_32)in.Read(); + UInt_32 vg = (chunkType & 0x3f) - 32; + prevPixel[0] += vg - 8 + ((mod >> 4) & 0x0f); // R-value + prevPixel[1] += vg; // G-value + prevPixel[2] += vg - 8 + (mod & 0x0f); // B-value + } + else if ((chunkType & 0xc0) == 0xc0) // Run + run = (chunkType & 0x3f); + + *(UInt_32*)&index[(prevPixel[0] * 3 + prevPixel[1] * 5 + prevPixel[2] * 7 + prevPixel[3] * 11) % 64 * channels] = *(UInt_32*)prevPixel; + } + + if (channels == 4) + { + *((UInt_32*)&(*out)[pos]) = *(UInt_32*)prevPixel; + } + else + { + (*out)[pos] = prevPixel[0]; + (*out)[pos + 1] = prevPixel[1]; + (*out)[pos + 2] = prevPixel[2]; + } + } + + return true; + } +} + +int main() +{ + ehs::Audio::AddCodec({ + "Waveform Audio", + "wav", + ehs::Endianness::LE, + nullptr, + ehs::DecodeWAV + }); + + ehs::Audio::AddCodec({ + "Event Horizon Audio", + "eha", + ehs::Endianness::LE, + ehs::EncodeEHA, + ehs::DecodeEHA + }); + + ehs::Img::AddCodec({ + "Portable Network Graphic", + "png", + ehs::Endianness::BE, + nullptr, + ehs::DecodePNG + }); + + ehs::Img::AddCodec({ + "Quite OK Image", + "qoi", + ehs::Endianness::BE, + ehs::EncodeQOI, + ehs::DecodeQOI + }); + + ehs::GarbageCollector::Start(); + + const ehs::SInt_32 code = Main(&ehs::appName, &ehs::appVerId, &ehs::appVer); + if (code) + EHS_LOG("Warning", 0, "Executable exited with code #" + ehs::Str_8::FromNum(code) + "."); + + ehs::GarbageCollector::Stop(); + + return code; +} \ No newline at end of file diff --git a/src/GarbageCollector.cpp b/src/GarbageCollector.cpp new file mode 100644 index 0000000..67be746 --- /dev/null +++ b/src/GarbageCollector.cpp @@ -0,0 +1,133 @@ +#include "ehs/GarbageCollector.h" + +namespace ehs +{ + UInt_32 GarbageCollectionThread(void* params) + { + while (GarbageCollector::IsRunning()) + { + GarbageCollector::Poll(); + Thread::SleepFor(50); + } + + GarbageCollector::Dump(); + + return 0; + } + + Vector GarbageCollector::garbage(0, 1000); + UInt_64 GarbageCollector::max = 10; + Thread GarbageCollector::thread; + Mutex GarbageCollector::mutex; + bool GarbageCollector::running = false; + + bool GarbageCollector::Has(const BaseObj* obj) + { + for (UInt_64 i = 0; i < garbage.Size(); ++i) + if (garbage[i] == obj) + return true; + + return false; + } + + void GarbageCollector::Start() + { + if (running) + return; + + mutex.Initialize(); + + thread.Start(GarbageCollectionThread, nullptr); + + running = true; + } + + void GarbageCollector::Stop() + { + if (!running) + return; + + running = false; + + thread.Join(); + } + + void GarbageCollector::Add(BaseObj* obj) + { + if (!obj) + return; + + /* + if (Has(obj)) + { + EHS_LOG_INT("Warning", 1, obj->GetTypeId() + " object with address of, \"" + Str_8::FromNum((USize)obj) + "\" is already in garbage."); + return; + } + */ + + garbage.Push(obj); + } + + void GarbageCollector::SetMax(const UInt_64 newMax) + { + max = newMax; + } + + UInt_64 GarbageCollector::GetMax() + { + return max; + } + + void GarbageCollector::SetStride(const UInt_64 newStride) + { + Dump(); + + garbage = Vector(0, newStride); + } + + UInt_64 GarbageCollector::GetStride() + { + return garbage.Stride(); + } + + UInt_64 GarbageCollector::Size() + { + return garbage.Size(); + } + + void GarbageCollector::Poll() + { + if (running) + mutex.Lock(); + + UInt_64 i = 0; + + while (i < garbage.Size()) + { + garbage.Swap(i, garbage.End()); + delete garbage.Pop(); + } + + if (running) + mutex.Unlock(); + } + + void GarbageCollector::Dump() + { + if (running) + mutex.Lock(); + + for (UInt_64 i = 0; i < garbage.Size(); ++i) + delete garbage[i]; + + garbage.Clear(); + + if (running) + mutex.Unlock(); + } + + bool GarbageCollector::IsRunning() + { + return running; + } +} diff --git a/src/HRNG_ARM64.cpp b/src/HRNG_ARM64.cpp new file mode 100644 index 0000000..2c5a066 --- /dev/null +++ b/src/HRNG_ARM64.cpp @@ -0,0 +1,124 @@ +#include "ehs/HRNG.h" + +namespace ehs +{ + UInt_64 HRNG::GenerateSeed_u64() + { + return 0; + } + + UInt_64 HRNG::Generate_u64(const UInt_64 min, const UInt_64 max) + { + return 0; + } + + UInt_64 HRNG::Generate_u64() + { + return 0; + } + + SInt_64 HRNG::GenerateSeed_s64() + { + return 0; + } + + SInt_64 HRNG::Generate_s64(const SInt_64 min, const SInt_64 max) + { + return 0; + } + + SInt_64 HRNG::Generate_s64() + { + return 0; + } + + UInt_32 HRNG::GenerateSeed_u32() + { + return 0; + } + + UInt_32 HRNG::Generate_u32(const UInt_32 min, const UInt_32 max) + { + return 0; + } + + UInt_32 HRNG::Generate_u32() + { + return 0; + } + + SInt_32 HRNG::GenerateSeed_s32() + { + return 0; + } + + SInt_32 HRNG::Generate_s32(const SInt_32 min, const SInt_32 max) + { + return 0; + } + + SInt_32 HRNG::Generate_s32() + { + return 0; + } + + UInt_32 HRNG::GenerateSeed_u16() + { + return 0; + } + + UInt_16 HRNG::Generate_u16(const UInt_16 min, const UInt_16 max) + { + return 0; + } + + UInt_16 HRNG::Generate_u16() + { + return 0; + } + + SInt_16 HRNG::GenerateSeed_s16() + { + return 0; + } + + SInt_16 HRNG::Generate_s16(const SInt_16 min, const SInt_16 max) + { + return 0; + } + + SInt_16 HRNG::Generate_s16() + { + return 0; + } + + UInt_8 HRNG::GenerateSeed_u8() + { + return 0; + } + + UInt_8 HRNG::Generate_u8(const UInt_8 min, const UInt_8 max) + { + return 0; + } + + UInt_8 HRNG::Generate_u8() + { + return 0; + } + + SInt_8 HRNG::GenerateSeed_s8() + { + return 0; + } + + SInt_8 HRNG::Generate_s8(const SInt_8 min, const SInt_8 max) + { + return 0; + } + + SInt_8 HRNG::Generate_s8() + { + return 0; + } +} \ No newline at end of file diff --git a/src/HRNG_GCC.asm b/src/HRNG_GCC.asm new file mode 100644 index 0000000..575446b --- /dev/null +++ b/src/HRNG_GCC.asm @@ -0,0 +1,169 @@ +global _ZN3ehs4HRNG16GenerateSeed_u64Ev +global _ZN3ehs4HRNG12Generate_u64Emm +global _ZN3ehs4HRNG12Generate_u64Ev +global _ZN3ehs4HRNG16GenerateSeed_s64Ev +global _ZN3ehs4HRNG12Generate_s64Ell +global _ZN3ehs4HRNG12Generate_s64Ev +global _ZN3ehs4HRNG16GenerateSeed_u32Ev +global _ZN3ehs4HRNG12Generate_u32Ejj +global _ZN3ehs4HRNG12Generate_u32Ev +global _ZN3ehs4HRNG16GenerateSeed_s32Ev +global _ZN3ehs4HRNG12Generate_s32Eii +global _ZN3ehs4HRNG12Generate_s32Ev +global _ZN3ehs4HRNG16GenerateSeed_u16Ev +global _ZN3ehs4HRNG12Generate_u16Ett +global _ZN3ehs4HRNG12Generate_u16Ev +global _ZN3ehs4HRNG16GenerateSeed_s16Ev +global _ZN3ehs4HRNG12Generate_s16Ess +global _ZN3ehs4HRNG12Generate_s16Ev +global _ZN3ehs4HRNG15GenerateSeed_u8Ev +global _ZN3ehs4HRNG11Generate_u8Ehh +global _ZN3ehs4HRNG11Generate_u8Ev +global _ZN3ehs4HRNG15GenerateSeed_s8Ev +global _ZN3ehs4HRNG11Generate_s8Eaa +global _ZN3ehs4HRNG11Generate_s8Ev + +section .text + _ZN3ehs4HRNG16GenerateSeed_u64Ev: + RDSEED RAX + RET + + _ZN3ehs4HRNG12Generate_u64Emm: + RDRAND RAX + SUB RSI, RDI + MOV R8, RSI + XOR RDX, RDX + DIV R8 + MOV RAX, RDX + ADD RAX, RDI + RET + + _ZN3ehs4HRNG12Generate_u64Ev: + RDRAND RAX + RET + + _ZN3ehs4HRNG16GenerateSeed_s64Ev: + RDSEED RAX + RET + + _ZN3ehs4HRNG12Generate_s64Ell: + RDRAND RAX + SUB RSI, RDI + MOV R8, RSI + XOR RDX, RDX + DIV R8 + MOV RAX, RDX + ADD RAX, RDI + RET + + _ZN3ehs4HRNG12Generate_s64Ev: + RDRAND RAX + RET + + _ZN3ehs4HRNG16GenerateSeed_u32Ev: + RDSEED EAX + RET + + _ZN3ehs4HRNG12Generate_u32Ejj: + RDRAND EAX + SUB ESI, EDI + MOV R8D, ESI + XOR EDX, EDX + DIV R8D + MOV EAX, EDX + ADD EAX, EDI + RET + + _ZN3ehs4HRNG12Generate_u32Ev: + RDRAND EAX + RET + + _ZN3ehs4HRNG16GenerateSeed_s32Ev: + RDSEED EAX + RET + + _ZN3ehs4HRNG12Generate_s32Eii: + RDRAND EAX + SUB ESI, EDI + MOV R8D, ESI + XOR EDX, EDX + DIV R8D + MOV EAX, EDX + ADD EAX, EDI + RET + + _ZN3ehs4HRNG12Generate_s32Ev: + RDRAND EAX + RET + + _ZN3ehs4HRNG16GenerateSeed_u16Ev: + RDSEED AX + RET + + _ZN3ehs4HRNG12Generate_u16Ett: + RDRAND AX + SUB SI, DI + MOV R8W, SI + XOR DX, DX + DIV R8W + MOV AX, DX + ADD AX, DI + RET + + _ZN3ehs4HRNG12Generate_u16Ev: + RDRAND AX + RET + + _ZN3ehs4HRNG16GenerateSeed_s16Ev: + RDSEED AX + RET + + _ZN3ehs4HRNG12Generate_s16Ess: + RDRAND AX + SUB SI, DI + MOV R8W, SI + XOR DX, DX + DIV R8W + MOV AX, DX + ADD AX, DI + RET + + _ZN3ehs4HRNG12Generate_s16Ev: + RDRAND AX + RET + + _ZN3ehs4HRNG15GenerateSeed_u8Ev: + RDSEED AX + RET + + _ZN3ehs4HRNG11Generate_u8Ehh: + RDRAND AX + XOR AH, AH + SUB SIL, DIL + MOV R8B, SIL + DIV R8B + MOV AL, AH + ADD AL, DIL + RET + + _ZN3ehs4HRNG11Generate_u8Ev: + RDRAND AX + RET + + _ZN3ehs4HRNG15GenerateSeed_s8Ev: + RDSEED AX + RET + + _ZN3ehs4HRNG11Generate_s8Eaa: + RDRAND AX + XOR AH, AH + SUB SIL, DIL + MOV R8B, SIL + DIV R8B + MOV AL, AH + ADD AL, DIL + RET + + _ZN3ehs4HRNG11Generate_s8Ev: + RDRAND AX + RET \ No newline at end of file diff --git a/src/HRNG_MSVC.asm b/src/HRNG_MSVC.asm new file mode 100644 index 0000000..3c2de7f --- /dev/null +++ b/src/HRNG_MSVC.asm @@ -0,0 +1,169 @@ +global ?GenerateSeed_u64@HRNG@ehs@@SA_KXZ +global ?Generate_u64@HRNG@ehs@@SA_K_K0@Z +global ?Generate_u64@HRNG@ehs@@SA_KXZ +global ?GenerateSeed_s64@HRNG@ehs@@SA_JXZ +global ?Generate_s64@HRNG@ehs@@SA_J_J0@Z +global ?Generate_s64@HRNG@ehs@@SA_JXZ +global ?GenerateSeed_u32@HRNG@ehs@@SAIXZ +global ?Generate_u32@HRNG@ehs@@SAIII@Z +global ?Generate_u32@HRNG@ehs@@SAIXZ +global ?GenerateSeed_s32@HRNG@ehs@@SAHXZ +global ?Generate_s32@HRNG@ehs@@SAHHH@Z +global ?Generate_s32@HRNG@ehs@@SAHXZ +global ?GenerateSeed_u16@HRNG@ehs@@SAIXZ +global ?Generate_u16@HRNG@ehs@@SAGGG@Z +global ?Generate_u16@HRNG@ehs@@SAGXZ +global ?GenerateSeed_s16@HRNG@ehs@@SAFXZ +global ?Generate_s16@HRNG@ehs@@SAFFF@Z +global ?Generate_s16@HRNG@ehs@@SAFXZ +global ?GenerateSeed_u8@HRNG@ehs@@SAEXZ +global ?Generate_u8@HRNG@ehs@@SAEEE@Z +global ?Generate_u8@HRNG@ehs@@SAEXZ +global ?GenerateSeed_s8@HRNG@ehs@@SACXZ +global ?Generate_s8@HRNG@ehs@@SACCC@Z +global ?Generate_s8@HRNG@ehs@@SACXZ + +section .text + ?GenerateSeed_u64@HRNG@ehs@@SA_KXZ: + RDSEED RAX + RET + + ?Generate_u64@HRNG@ehs@@SA_K_K0@Z: + RDRAND RAX + MOV R8, RDX + SUB R8, RCX + XOR RDX, RDX + DIV R8 + MOV RAX, RDX + ADD RAX, RCX + RET + + ?Generate_u64@HRNG@ehs@@SA_KXZ: + RDRAND RAX + RET + + ?GenerateSeed_s64@HRNG@ehs@@SA_JXZ: + RDSEED RAX + RET + + ?Generate_s64@HRNG@ehs@@SA_J_J0@Z: + RDRAND RAX + MOV R8, RDX + SUB R8, RCX + XOR RDX, RDX + DIV R8 + MOV RAX, RDX + ADD RAX, RCX + RET + + ?Generate_s64@HRNG@ehs@@SA_JXZ: + RDRAND RAX + RET + + ?GenerateSeed_u32@HRNG@ehs@@SAIXZ: + RDSEED EAX + RET + + ?Generate_u32@HRNG@ehs@@SAIII@Z: + RDRAND EAX + MOV R8D, EDX + SUB R8D, ECX + XOR EDX, EDX + DIV R8D + MOV EAX, EDX + ADD EAX, ECX + RET + + ?Generate_u32@HRNG@ehs@@SAIXZ: + RDRAND EAX + RET + + ?GenerateSeed_s32@HRNG@ehs@@SAHXZ: + RDSEED EAX + RET + + ?Generate_s32@HRNG@ehs@@SAHHH@Z: + RDRAND EAX + MOV R8D, EDX + SUB R8D, ECX + XOR EDX, EDX + DIV R8D + MOV EAX, EDX + ADD EAX, ECX + RET + + ?Generate_s32@HRNG@ehs@@SAHXZ: + RDRAND EAX + RET + + ?GenerateSeed_u16@HRNG@ehs@@SAIXZ: + RDSEED AX + RET + + ?Generate_u16@HRNG@ehs@@SAGGG@Z: + RDRAND AX + MOV R8W, DX + SUB R8W, CX + XOR DX, DX + DIV R8W + MOV AX, DX + ADD AX, CX + RET + + ?Generate_u16@HRNG@ehs@@SAGXZ: + RDRAND AX + RET + + ?GenerateSeed_s16@HRNG@ehs@@SAFXZ: + RDSEED AX + RET + + ?Generate_s16@HRNG@ehs@@SAFFF@Z: + RDRAND AX + MOV R8W, DX + SUB R8W, CX + XOR DX, DX + DIV R8W + MOV AX, DX + ADD AX, CX + RET + + ?Generate_s16@HRNG@ehs@@SAFXZ: + RDRAND AX + RET + + ?GenerateSeed_u8@HRNG@ehs@@SAEXZ: + RDSEED AX + RET + + ?Generate_u8@HRNG@ehs@@SAEEE@Z: + RDRAND AX + MOV R8W, DX + SUB R8W, CX + XOR DX, DX + DIV R8W + MOV AX, DX + ADD AX, CX + RET + + ?Generate_u8@HRNG@ehs@@SAEXZ: + RDRAND AX + RET + + ?GenerateSeed_s8@HRNG@ehs@@SACXZ: + RDSEED AX + RET + + ?Generate_s8@HRNG@ehs@@SACCC@Z: + RDRAND AX + MOV R8W, DX + SUB R8W, CX + XOR DX, DX + DIV R8W + MOV AX, DX + ADD AX, CX + RET + + ?Generate_s8@HRNG@ehs@@SACXZ: + RDRAND AX + RET \ No newline at end of file diff --git a/src/Log.cpp b/src/Log.cpp new file mode 100644 index 0000000..0b0e769 --- /dev/null +++ b/src/Log.cpp @@ -0,0 +1,149 @@ +#include "ehs/Log.h" + +namespace ehs +{ + void (*Log::logCb)(const Log&) = nullptr; + Log Log::lastLog; + + void Log::SetCallback(void (*newLogCb)(const Log&)) + { + logCb = newLogCb; + } + + void Log::Raise(const Log& log) + { + if (logCb) + logCb(log); + + lastLog = log; + } + + Log Log::GetLastLog() + { + Log result = lastLog; + lastLog = Log(); + return result; + } + + Log::Log() + : code(0) + { + } + + Log::Log(std::initializer_list tags, const UInt_64 code, const Str_8& msg) + : tags(tags.size()), code(code), msg(msg) + { + UInt_64 i = 0; + for (auto v = tags.begin(); v != tags.end(); ++v) + this->tags[i++] = *v; + } + + Log::Log(Array& tags, const UInt_64 code, const Str_8& msg) + : tags(tags), code(code), msg(msg) + { + } + + Log::Log(const Log& log) + : tags(log.tags), code(log.code), msg(log.msg) + { + } + + Log& Log::operator=(const Log& log) + { + if (this == &log) + return *this; + + tags = log.tags; + code = log.code; + msg = log.msg; + + return *this; + } + + /* + bool Log::operator==(const Log log) + { + return src == log.src && type == log.type && code == log.code && msg == log.msg; + } + + bool Log::operator!=(const Log log) + { + return src != log.src || type != log.type || code != log.code || msg != log.msg; + } + */ + + bool Log::HasTags(const std::initializer_list tags) const + { + UInt_64 i = 0; + UInt_64 c = 0; + + for (auto v = tags.begin(); v != tags.end(); ++v) + if (this->tags[i++].GetLower() == v->GetLower()) + ++c; + + if (c == tags.size()) + return true; + + return false; + } + + bool Log::HasTags(const Array &tags) const + { + UInt_64 c = 0; + + for (UInt_64 i = 0; i < tags.Size(); ++i) + if (this->tags[i].GetLower() == tags[c].GetLower()) + ++c; + + if (c == tags.Size()) + return true; + + return false; + } + + bool Log::HasTag(const Str_8& tag) const + { + for (UInt_64 i = 0; i < tags.Size(); ++i) + if (tags[i].GetLower() == tag.GetLower()) + return true; + + return false; + } + + Array Log::GetTags() const + { + return tags; + } + + UInt_64 Log::GetCode() const + { + return code; + } + + Str_8 Log::GetMsg() const + { + return msg; + } + + Str_8 Log::ToStr() const + { + Str_8 result = "["; + + for (UInt_64 i = 0; i < tags.Size(); ++i) + { + result += tags[i]; + + if (i != tags.Size() - 1) + result += ", "; + } + + result += "](" + Str_8::FromNum(code) + "): " + msg; + + return result; + } + + bool Log::IsValid() const + { + return tags.Size() && msg.Size(); + } +} \ No newline at end of file diff --git a/src/Math.cpp b/src/Math.cpp new file mode 100644 index 0000000..790a04e --- /dev/null +++ b/src/Math.cpp @@ -0,0 +1,91 @@ +#include "ehs/Math.h" +#include "ehs/system/Architecture.h" + +namespace ehs +{ + bool Math::AbsCmp(const float a, const float b) + { + return Abs(a - b) <= fltEpsilon; + } + + bool Math::AbsCmp(const double a, const double b) + { + return Abs(a - b) <= fltEpsilon; + } + + bool Math::RelCmp(const float a, const float b) + { + return Abs(a - b) <= fltEpsilon * Max(Abs(a), Abs(b)); + } + + bool Math::RelCmp(const double a, const double b) + { + return Abs(a - b) <= fltEpsilon * Max(Abs(a), Abs(b)); + } + + bool Math::ComCmp(const float a, const float b) + { + return Abs(a - b) <= fltEpsilon * Max(1.0f, Max(Abs(a), Abs(b))); + } + + bool Math::ComCmp(const double a, const double b) + { + return Abs(a - b) <= dblEpsilon * Max(1.0, Max(Abs(a), Abs(b))); + } + + double Math::Sqrt(const double from) + { + #if defined(EHS_ARCH_X64) + if (CPU::HasAVX()) + return Sqrt_AVX(from); + else if (CPU::HasSSE()) + return Sqrt_SSE2(from); + + double temp = 0.0; + double result = from / 2.0; + + while (result != temp) + { + temp = result; + result = (from / temp + temp) / 2.0; + } + + return result; + #elif defined(EHS_ARCH_ARM64) + return Sqrt_VFP4(from); + #endif + } + + float Math::Sqrt(const float from) + { + #if defined(EHS_ARCH_X64) + if (CPU::HasAVX()) + return Sqrt_AVX(from); + else if (CPU::HasSSE()) + return Sqrt_SSE(from); + + float temp = 0.0f; + float result = from / 2.0f; + + while (result != temp) + { + temp = result; + result = (from / temp + temp) / 2.0f; + } + + return result; + #elif defined(EHS_ARCH_ARM64) + return Sqrt_VFP4(from); + #endif + } + + float Math::Mod(const float from, const float divisor) + { + return from - Trunc(from / divisor) * divisor; + } + + double Math::Mod(const double from, const double divisor) + { + return from - Trunc(from / divisor) * divisor; + } +} \ No newline at end of file diff --git a/src/Math_GCC_AMD64.asm b/src/Math_GCC_AMD64.asm new file mode 100644 index 0000000..cba840c --- /dev/null +++ b/src/Math_GCC_AMD64.asm @@ -0,0 +1,61 @@ +global _ZN3ehs4Math8Sqrt_AVXEf +global _ZN3ehs4Math8Sqrt_AVXEd +global _ZN3ehs4Math8Sqrt_SSEEf +global _ZN3ehs4Math9Sqrt_SSE2Ed +global _ZN3ehs4Math4NearEf +global _ZN3ehs4Math4NearEd +global _ZN3ehs4Math5FloorEf +global _ZN3ehs4Math5FloorEd +global _ZN3ehs4Math4CeilEf +global _ZN3ehs4Math4CeilEd +global _ZN3ehs4Math5TruncEf +global _ZN3ehs4Math5TruncEd + +section .text + _ZN3ehs4Math8Sqrt_AVXEf: + VSQRTPS XMM0, XMM0 + RET + + _ZN3ehs4Math8Sqrt_AVXEd: + VSQRTPD XMM0, XMM0 + RET + + _ZN3ehs4Math8Sqrt_SSEEf: + SQRTPS XMM0, XMM0 + RET + + _ZN3ehs4Math9Sqrt_SSE2Ed: + SQRTPD XMM0, XMM0 + RET + + _ZN3ehs4Math4NearEf: + ROUNDPS XMM0, XMM0, 0 + RET + + _ZN3ehs4Math4NearEd: + ROUNDPD XMM0, XMM0, 0 + RET + + _ZN3ehs4Math5FloorEf: + ROUNDPS XMM0, XMM0, 1 + RET + + _ZN3ehs4Math5FloorEd: + ROUNDPD XMM0, XMM0, 1 + RET + + _ZN3ehs4Math4CeilEf: + ROUNDPS XMM0, XMM0, 2 + RET + + _ZN3ehs4Math4CeilEd: + ROUNDPD XMM0, XMM0, 2 + RET + + _ZN3ehs4Math5TruncEf: + ROUNDPS XMM0, XMM0, 3 + RET + + _ZN3ehs4Math5TruncEd: + ROUNDPD XMM0, XMM0, 3 + RET \ No newline at end of file diff --git a/src/Math_GCC_ARM64.s b/src/Math_GCC_ARM64.s new file mode 100644 index 0000000..71f47ee --- /dev/null +++ b/src/Math_GCC_ARM64.s @@ -0,0 +1,51 @@ +.global _ZN3ehs4Math9Sqrt_VFP4Ef +.global _ZN3ehs4Math9Sqrt_VFP4Ed +.global _ZN3ehs4Math4NearEf +.global _ZN3ehs4Math4NearEd +.global _ZN3ehs4Math5FloorEf +.global _ZN3ehs4Math5FloorEd +.global _ZN3ehs4Math4CeilEf +.global _ZN3ehs4Math4CeilEd +.global _ZN3ehs4Math5TruncEf +.global _ZN3ehs4Math5TruncEd + +.section .text + _ZN3ehs4Math9Sqrt_VFP4Ef: + FSQRT S0, S0 + RET + + _ZN3ehs4Math9Sqrt_VFP4Ed: + FSQRT D0, D0 + RET + + _ZN3ehs4Math4NearEf: + FRINTN S0, S0 + RET + + _ZN3ehs4Math4NearEd: + FRINTN D0, D0 + RET + + _ZN3ehs4Math5FloorEf: + FRINTM S0, S0 + RET + + _ZN3ehs4Math5FloorEd: + FRINTM D0, D0 + RET + + _ZN3ehs4Math4CeilEf: + FRINTP S0, S0 + RET + + _ZN3ehs4Math4CeilEd: + FRINTP D0, D0 + RET + + _ZN3ehs4Math5TruncEf: + FRINTZ S0, S0 + RET + + _ZN3ehs4Math5TruncEd: + FRINTZ D0, D0 + RET diff --git a/src/Math_MSVC_AMD64.asm b/src/Math_MSVC_AMD64.asm new file mode 100644 index 0000000..c151e74 --- /dev/null +++ b/src/Math_MSVC_AMD64.asm @@ -0,0 +1,61 @@ +global ?Sqrt_AVX@Math@ehs@@CAMM@Z +global ?Sqrt_AVX@Math@ehs@@CANN@Z +global ?Sqrt_SSE@Math@ehs@@CAMM@Z +global ?Sqrt_SSE2@Math@ehs@@CANN@Z +global ?Near@Math@ehs@@SAMM@Z +global ?Near@Math@ehs@@SANN@Z +global ?Floor@Math@ehs@@SAMM@Z +global ?Floor@Math@ehs@@SANN@Z +global ?Ceil@Math@ehs@@SAMM@Z +global ?Ceil@Math@ehs@@SANN@Z +global ?Trunc@Math@ehs@@SAMM@Z +global ?Trunc@Math@ehs@@SANN@Z + +section .text + ?Sqrt_AVX@Math@ehs@@CAMM@Z: + VSQRTPS XMM0, XMM0 + RET + + ?Sqrt_AVX@Math@ehs@@CANN@Z: + VSQRTPD XMM0, XMM0 + RET + + ?Sqrt_SSE@Math@ehs@@CAMM@Z: + SQRTPS XMM0, XMM0 + RET + + ?Sqrt_SSE2@Math@ehs@@CANN@Z: + SQRTPD XMM0, XMM0 + RET + + ?Near@Math@ehs@@SAMM@Z: + ROUNDPS XMM0, XMM0, 0 + RET + + ?Near@Math@ehs@@SANN@Z: + ROUNDPD XMM0, XMM0, 0 + RET + + ?Floor@Math@ehs@@SAMM@Z: + ROUNDPS XMM0, XMM0, 1 + RET + + ?Floor@Math@ehs@@SANN@Z: + ROUNDPD XMM0, XMM0, 1 + RET + + ?Ceil@Math@ehs@@SAMM@Z: + ROUNDPS XMM0, XMM0, 2 + RET + + ?Ceil@Math@ehs@@SANN@Z: + ROUNDPD XMM0, XMM0, 2 + RET + + ?Trunc@Math@ehs@@SAMM@Z: + ROUNDPS XMM0, XMM0, 3 + RET + + ?Trunc@Math@ehs@@SANN@Z: + ROUNDPD XMM0, XMM0, 3 + RET \ No newline at end of file diff --git a/src/PtrData.cpp b/src/PtrData.cpp new file mode 100644 index 0000000..30b7275 --- /dev/null +++ b/src/PtrData.cpp @@ -0,0 +1,64 @@ +#include "ehs/PtrData.h" + +namespace ehs +{ + Vector pointers; + + bool HasPtrData(void* data) + { + if (!data) + return false; + + for (UInt_64 i = 0; i < pointers.Size(); i++) + if (pointers[i].data == data) + return true; + + return false; + } + + void AddPtrData(void* data) + { + if (!data) + return; + + for (UInt_64 i = 0; i < pointers.Size(); i++) + { + if (pointers[i].data != data) + continue; + + pointers[i].references++; + + return; + } + + pointers.Push({1, data}); + } + + bool RemovePtrData(void* data) + { + if (!data) + return false; + + for (UInt_64 i = 0; i < pointers.Size(); i++) + { + if (pointers[i].data != data) + continue; + + if (pointers[i].references == 1) + { + pointers.Swap(i, pointers.End()); + pointers.Pop(); + + return true; + } + else + { + pointers[i].references--; + + return false; + } + } + + return false; + } +} \ No newline at end of file diff --git a/src/Range.cpp b/src/Range.cpp new file mode 100644 index 0000000..068a265 --- /dev/null +++ b/src/Range.cpp @@ -0,0 +1,5 @@ +// +// Created by Nelso on 3/29/2022. +// + +#include "ehs/Range.h" diff --git a/src/StrToHash.cpp b/src/StrToHash.cpp new file mode 100644 index 0000000..b895485 --- /dev/null +++ b/src/StrToHash.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +ehs::Int_32 Main(ehs::Str_8* appName, ehs::Str_8* appVerId, ehs::Version* appVer) +{ + *appName = "StrToHash"; + *appVerId = "Release"; + *appVer = {1, 0, 0}; + + ehs::Console::Attach(); + + ehs::Vector args = ehs::Console::GetArgs_8(); + + if (args.Size() > 1) + { + for (ehs::UInt_64 i = 1; i < args.Size(); ++i) + ehs::Console::Write_8("Result " + ehs::Str_8::FromNum(i) + ": " + ehs::Str_8::FromNum(args[i].Hash_64())); + } + else + { + ehs::Console::Write_8("String: ", false); + ehs::Str_8 in = ehs::Console::Read_8(); + ehs::Console::Write_8("Result: " + ehs::Str_8::FromNum(in.Hash_64())); + + ehs::Console::Read_8(); + } + + ehs::Console::Free(); + + return 0; +} diff --git a/src/Task.cpp b/src/Task.cpp new file mode 100644 index 0000000..e7d960d --- /dev/null +++ b/src/Task.cpp @@ -0,0 +1,216 @@ +#include "ehs/Task.h" + +namespace ehs +{ + UInt_32 TaskThread(void* args) + { + if (!args) + return 1; + + Serializer* data = (Serializer*)args; + Semaphore* available = data->Read(); + Semaphore* done = data->Read(); + Serializer** cbArgs = data->Read**>(); + TaskCb* callback = data->Read(); + + while (true) + { + if (!available->Wait(EHS_INFINITE)) + { + done->Signal(1); + return 1; + } + + if (!*callback) + { + done->Signal(1); + return 0; + } + + (*callback)(*cbArgs); + + done->Signal(1); + } + } + + Task::~Task() + { + if (!IsValid()) + return; + + if (!working) + { + *callback = nullptr; + available->Signal(1); + } + else + { + done->Wait(1000); + *callback = nullptr; + available->Signal(1); + } + + thread.Join(); + delete available; + delete done; + delete *cbArgs; + delete cbArgs; + delete callback; + } + + Task::Task() + : working(false), available(nullptr), done(nullptr), cbArgs(nullptr), callback(nullptr), threadArgs(nullptr) + { + Initialize(); + } + + Task::Task(Task&& task) noexcept + : working(task.working), available(task.available), done(task.done), cbArgs(task.cbArgs), + callback(task.callback), threadArgs(task.threadArgs), thread(std::move(task.thread)) + { + task.working = false; + task.available = nullptr; + task.done = nullptr; + task.cbArgs = nullptr; + task.callback = nullptr; + task.threadArgs = nullptr; + } + + Task::Task(const Task& task) + : working(false), available(nullptr), done(nullptr), cbArgs(nullptr), callback(nullptr), threadArgs(nullptr) + { + Initialize(); + } + + Task& Task::operator=(Task&& task) noexcept + { + if (this == &task) + return *this; + + Release(); + + working = task.working; + available = task.available; + done = task.done; + cbArgs = task.cbArgs; + callback = task.callback; + threadArgs = task.threadArgs; + thread = std::move(task.thread); + + task.working = false; + task.available = nullptr; + task.done = nullptr; + task.cbArgs = nullptr; + task.callback = nullptr; + task.threadArgs = nullptr; + task.thread = {}; + + return *this; + } + + Task& Task::operator=(const Task& task) + { + if (this == &task) + return *this; + + Release(); + + Initialize(); + + return *this; + } + + void Task::Revalidate() + { + if (!IsValid()) + return; + + Release(); + Initialize(); + } + + void Task::Initialize() + { + if (IsValid()) + return; + + working = false; + available = new Semaphore(0); + done = new Semaphore(0); + cbArgs = new Serializer*(new Serializer()); + callback = new TaskCb(nullptr); + + threadArgs = new Serializer(Endianness::LE); + threadArgs->Write(available); + threadArgs->Write(done); + threadArgs->Write(cbArgs); + threadArgs->Write(callback); + threadArgs->SetOffset(0); + + thread.Start(TaskThread, threadArgs); + } + + void Task::Release() + { + if (!IsValid()) + return; + + if (!working) + { + *callback = nullptr; + available->Signal(1); + } + else + { + done->Wait(1000); + *callback = nullptr; + available->Signal(1); + } + + working = false; + thread.Join(); + delete available; + available = nullptr; + delete done; + done = nullptr; + delete *cbArgs; + delete cbArgs; + cbArgs = nullptr; + delete callback; + callback = nullptr; + } + + bool Task::IsWorking() const + { + return working; + } + + void Task::GiveWork(Serializer args, TaskCb cb) + { + if (working) + { + EHS_LOG_INT("Warning", 0, "Attempted to give work while task is still working."); + return; + } + + **cbArgs = std::move(args); + *callback = cb; + working = true; + + available->Signal(1); + } + + void Task::WaitUntilDone() + { + if (!working) + return; + + done->Wait(EHS_INFINITE); + working = false; + } + + bool Task::IsValid() const + { + return thread.IsValid() && available && available->IsValid() && done && done->IsValid() && cbArgs && callback; + } +} \ No newline at end of file diff --git a/src/Type.cpp b/src/Type.cpp new file mode 100644 index 0000000..a860195 --- /dev/null +++ b/src/Type.cpp @@ -0,0 +1,136 @@ +#include "ehs/Type.h" + +namespace ehs +{ + Type::Type() + : size(0), id(nullptr), hashId(0) + { + } + + Type::Type(const Char_8* const id) + : size(CalcSize(id)), id(id), hashId(GenHash(id, size)) + { + } + + Type::Type(Type&& type) noexcept + : size(type.size), id(type.id), hashId(type.hashId) + { + type.size = 0; + type.id = nullptr; + type.hashId = 0; + } + + Type::Type(const Type& type) + : size(type.size), id(type.id), hashId(type.hashId) + { + } + + Type& Type::operator=(Type&& type) noexcept + { + if (this == &type) + return *this; + + size = type.size; + id = type.id; + hashId = type.hashId; + + type.size = 0; + type.id = nullptr; + type.hashId = 0; + + return *this; + } + + Type& Type::operator=(const Type& type) + { + if (this == &type) + return *this; + + size = type.size; + id = type.id; + hashId = type.hashId; + + return *this; + } + + bool Type::operator==(const Type& type) const + { + return hashId == type.hashId; + } + + bool Type::operator!=(const Type& type) const + { + return hashId != type.hashId; + } + + bool Type::operator==(const UInt_64 inHashId) const + { + return hashId == inHashId; + } + + bool Type::operator!=(const UInt_64 inHashId) const + { + return hashId != inHashId; + } + + bool Type::operator==(const Char_8* const inStr) const + { + if (size != CalcSize(inStr)) + return false; + + return Util::Compare(id, inStr, size); + } + + bool Type::operator!=(const Char_8* const inStr) const + { + if (size != CalcSize(inStr)) + return true; + + return !Util::Compare(id, inStr, size); + } + + UInt_64 Type::GetSize() const + { + return size; + } + + const Char_8* Type::GetId() const + { + return id; + } + + UInt_64 Type::GetHashId() const + { + return hashId; + } + + bool Type::IsValid() const + { + return size; + } + + UInt_64 Type::CalcSize(const Char_8* const id) + { + UInt_64 result = 0; + + while (id[result]) + result++; + + return result; + } + + UInt_64 Type::GenHash(const Char_8* const id, const UInt_64 size) + { + if (!size) + return 0; + + const Byte* const bytes = (Byte*)id; + + UInt_64 hash = 14695981039346656037ull; + + for (UInt_64 i = 0; i < size; ++i) + hash = (hash ^ bytes[i]) * 1099511628211; + + return hash; + } +} \ No newline at end of file diff --git a/src/URI.cpp b/src/URI.cpp new file mode 100644 index 0000000..46eaa3f --- /dev/null +++ b/src/URI.cpp @@ -0,0 +1,57 @@ +#include "ehs/URI.h" + +namespace ehs +{ + bool IsAN(const Char_8 c) + { + return (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122); + } + + Str_8 URI::Encode(const Str_8& in) + { + Str_8 result; + + UInt_64 offset = 0; + for (UInt_64 i = 0; i < in.Size(); ++i) + { + if (IsAN(in[i]) || in[i] == '-' || in[i] == '_' || in[i] == '.' || in[i] == '~') + continue; + + if (i != offset) + result.Push(&in[offset], i - offset); + + result.Push("%" + Str_8::NumToHex(in[i])); + offset = i + 1; + } + + if (offset < in.Size()) + result.Push(&in[offset], in.Size() - offset); + + return result; + } + + Str_8 URI::Decode(const Str_8& in) + { + Str_8 result; + + UInt_64 offset = 0; + for (UInt_64 i = 0; i < in.Size(); ++i) + { + if (in[i] != '%') + continue; + + if (i != offset) + result.Push(&in[offset], i - offset); + + result.Push(Str_8::HexToNum({&in[i + 1], 2})); + + i += 2; + offset = i + 1; + } + + if (offset < in.Size()) + result.Push(&in[offset], in.Size() - offset); + + return result; + } +} \ No newline at end of file diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..6274956 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,121 @@ +#include "ehs/Util.h" + +namespace ehs +{ + bool Util::Compare(const void* const a, const void* const b, const UInt_64 size) + { + Byte* aBytes = (Byte*)a; + Byte* bBytes = (Byte*)b; + UInt_64 remainder = size; + UInt_64 i = 0; + + while (i < size) + { + if (remainder >= sizeof(UInt_64)) + { + if (*(UInt_64*)&aBytes[i] != *(UInt_64*)&bBytes[i]) + return false; + + i += sizeof(UInt_64); + } + else if (remainder >= sizeof(UInt_32)) + { + if (*(UInt_32*)&aBytes[i] != *(UInt_32*)&bBytes[i]) + return false; + + i += sizeof(UInt_32); + } + else if (remainder >= sizeof(UInt_16)) + { + if (*(UInt_16*)&aBytes[i] != *(UInt_16*)&bBytes[i]) + return false; + + i += sizeof(UInt_16); + } + else + { + if (aBytes[i] != bBytes[i]) + return false; + + i += sizeof(Byte); + } + + remainder = size - i; + } + + return true; + } + + void Util::Copy(void* const out, const void* const in, const UInt_64 size) + { + Byte* outB = (Byte*)out; + Byte* inB = (Byte*)in; + UInt_64 remainder = size; + + UInt_64 i = 0; + while (i < size) + { + if (remainder >= sizeof(UInt_64)) + { + *(UInt_64*)&outB[i] = *(UInt_64*)&inB[i]; + i += sizeof(UInt_64); + } + else if (remainder >= sizeof(UInt_32)) + { + *(UInt_32*)&outB[i] = *(UInt_32*)&inB[i]; + i += sizeof(UInt_32); + } + else if (remainder >= sizeof(UInt_16)) + { + *(UInt_16*)&outB[i] = *(UInt_16*)&inB[i]; + i += sizeof(UInt_16); + } + else + { + outB[i++] = inB[i]; + } + + remainder = size - i; + } + } + + void Util::Fill(void* const out, const UInt_64 outSize, const void* const in, const UInt_64 inSize) + { + if (outSize % inSize) + return; + + for (UInt_64 i = 0; i < outSize; i += inSize) + Copy(&((Byte*)out)[i], in, inSize); + } + + void Util::Zero(void* const in, const UInt_64 size) + { + Byte* inB = (Byte*)in; + UInt_64 remainder = size; + UInt_64 i = 0; + while (i < size) + { + if (remainder >= sizeof(UInt_64)) + { + *(UInt_64*)&inB[i] = 0; + i += sizeof(UInt_64); + } + else if (remainder >= sizeof(UInt_32)) + { + *(UInt_32*)&inB[i] = 0; + i += sizeof(UInt_32); + } + else if (remainder >= sizeof(UInt_16)) + { + *(UInt_16*)&inB[i] = 0; + i += sizeof(UInt_16); + } + else + { + inB[i++] = 0; + } + + remainder = size - i; + } + } +} \ No newline at end of file diff --git a/src/Version.cpp b/src/Version.cpp new file mode 100644 index 0000000..33798ab --- /dev/null +++ b/src/Version.cpp @@ -0,0 +1,71 @@ +#include "ehs/Version.h" + +namespace ehs +{ + Version::Version() + : major(0), minor(0), patch(0) + { + } + + Version::Version(const UInt_32 major, const UInt_32 minor, const UInt_32 patch) + : major(major), minor(minor), patch(patch) + { + } + + Version::Version(const Version& version) + : major(version.major), minor(version.minor), patch(version.patch) + { + } + + Version& Version::operator=(const Version& version) + { + if (this == &version) + return *this; + + major = version.major; + minor = version.minor; + patch = version.patch; + + return *this; + } + + bool Version::operator==(const Version& version) const + { + return major == version.major && minor == version.minor && patch == version.patch; + } + + bool Version::operator!=(const Version& version) const + { + return major != version.major || minor != version.minor || patch != version.patch; + } + + unsigned int Version::operator[](const UInt_32 i) const + { + switch (i) + { + case 0: + return major; + case 1: + return minor; + case 2: + return patch; + default: + return major; + } + } + + unsigned int& Version::operator[](const UInt_32 i) + { + switch (i) + { + case 0: + return major; + case 1: + return minor; + case 2: + return patch; + default: + return major; + } + } +} \ No newline at end of file diff --git a/src/database/DVar.cpp b/src/database/DVar.cpp new file mode 100644 index 0000000..ae53a17 --- /dev/null +++ b/src/database/DVar.cpp @@ -0,0 +1,5 @@ +#include "ehs/database/DVar.h" + +namespace ehs +{ +} \ No newline at end of file diff --git a/src/io/BaseFile.cpp b/src/io/BaseFile.cpp new file mode 100644 index 0000000..4362cda --- /dev/null +++ b/src/io/BaseFile.cpp @@ -0,0 +1,607 @@ +#include "ehs/io/BaseFile.h" + +namespace ehs +{ + BaseFile::BaseFile() + : mode(Mode::READ_WRITE), disposition(Disposition::OPEN_PERSISTENT) + { + } + + BaseFile::BaseFile(const Str_8& filePath, const Mode mode, const Disposition disposition) + : path(filePath), fullName(ParseFullName_8(filePath)), + name(ParseName_8(fullName)), extension(ParseExt_8(fullName)), + mode(mode), disposition(disposition) + { + } + + BaseFile::BaseFile(BaseFile&& file) noexcept + : path(std::move(file.path)), fullName(std::move(file.fullName)), name(std::move(file.name)), + extension(std::move(file.extension)), mode(file.mode), disposition(file.disposition) + { + file.mode = Mode::READ_WRITE; + file.disposition = Disposition::OPEN_PERSISTENT; + } + + BaseFile& BaseFile::operator=(BaseFile&& file) noexcept + { + if (this == &file) + return *this; + + path = std::move(file.path); + fullName = std::move(file.fullName); + name = std::move(file.name); + extension = std::move(file.extension); + mode = file.mode; + disposition = file.disposition; + + file.mode = Mode::READ_WRITE; + file.disposition = Disposition::OPEN_PERSISTENT; + + return *this; + } + + void BaseFile::WriteStr_32(const Char_32* const str, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&((Byte*)str)[total], size - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(size) + ")."); + break; + } + + total += written; + } + while (total < size); + } + + void BaseFile::WriteStr_32(const Str_32& str) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&str.ToBytes()[total], str.Size(true) - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(str.Size()) + ")."); + break; + } + + total += written; + } + while (total < str.Size(true)); + } + + void BaseFile::WriteStr_16(const Char_16* const str, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&((Byte*)str)[total], size - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(size) + ")."); + break; + } + + total += written; + } + while (total < size); + } + + void BaseFile::WriteStr_16(const Str_16& str) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&str.ToBytes()[total], str.Size(true) - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(str.Size()) + ")."); + break; + } + + total += written; + } + while (total < str.Size(true)); + } + + void BaseFile::WriteStr_8(const Char_8* const str, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&((Byte*)str)[total], size - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(size) + ")."); + break; + } + + total += written; + } + while (total < size); + } + + void BaseFile::WriteStr_8(const Str_8& str) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&str.ToBytes()[total], str.Size(true) - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(str.Size()) + ")."); + break; + } + + total += written; + } + while (total < str.Size(true)); + } + + void BaseFile::WriteVector(const Vector& vec) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&vec[total], vec.Size() - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(vec.Size()) + ")."); + break; + } + + total += written; + } + while (total < vec.Size()); + } + + void BaseFile::WriteArray(const Array& arr) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&arr[total], arr.Size() - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(arr.Size()) + ")."); + break; + } + + total += written; + } + while (total < arr.Size()); + } + + void BaseFile::WriteSerializer_64(const Serializer& ser) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&ser[total], ser.Size() - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(ser.Size()) + ")."); + break; + } + + total += written; + } + while (total < ser.Size()); + } + + void BaseFile::WriteSerializer_32(const Serializer& ser) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 written = Write(&ser[total], ser.Size() - total); + if (!written) + { + EHS_LOG_INT("Error", 0, "Failed to write all data (" + Str_8::FromNum(total) + "/" + + Str_8::FromNum(ser.Size()) + ")."); + break; + } + + total += written; + } + while (total < ser.Size()); + } + + void BaseFile::ReadStr_32(Char_32* const buffer, UInt_64& size) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&((Byte*)buffer)[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + size = total; + } + + Str_32 BaseFile::ReadStr_32(const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return {}; + + Str_32 result(size / 4); + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&result.ToBytes()[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + result.Resize(total); + return result; + } + + void BaseFile::ReadStr_16(Char_16* const buffer, UInt_64& size) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&((Byte*)buffer)[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + size = total; + } + + Str_16 BaseFile::ReadStr_16(const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return {}; + + Str_16 result(size / 2); + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&result.ToBytes()[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + result.Resize(total); + return result; + } + + void BaseFile::ReadStr_8(Char_8* const buffer, UInt_64& size) + { + if (!IsValid() || IsMapped()) + return; + + UInt_64 total = 0; + + do + { + UInt_64 read = Read((Byte*)&buffer[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + size = total; + } + + Str_8 BaseFile::ReadStr_8(const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return {}; + + Str_8 result(size); + UInt_64 total = 0; + + do + { + UInt_64 read = Read((Byte*)&result[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + result.Resize(total); + return result; + } + + Vector BaseFile::ReadVector(const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return {}; + + Vector result(size, 0); + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&result[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + result.Resize(total); + return result; + } + + Array BaseFile::ReadArray(const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return {}; + + Array result(size); + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&result[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + result.Resize(total); + return result; + } + + Serializer BaseFile::ReadSerializer_64(const Endianness end, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return {}; + + Serializer result(end, size); + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&result[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + result.Resize(total); + return result; + } + + Serializer BaseFile::ReadSerializer_32(const Endianness end, const UInt_32 size) + { + if (!IsValid() || IsMapped()) + return {}; + + Serializer result(end, size); + UInt_64 total = 0; + + do + { + UInt_64 read = Read(&result[total], size - total); + if (!read) + break; + + total += read; + } + while (total < size); + + result.Resize(total); + return result; + } + + Str_8 BaseFile::GetPath() const + { + return path; + } + + Str_8 BaseFile::GetFullName() const + { + return fullName; + } + + Str_8 BaseFile::GetName() const + { + return name; + } + + Str_8 BaseFile::GetExtension() const + { + return extension; + } + + void BaseFile::Rename_32(const Str_32& filePath, const Str_32& newName) + { + } + + void BaseFile::Rename_16(const Str_16& filePath, const Str_16& newName) + { + } + + void BaseFile::Rename_8(const Str_8& filePath, const Str_8& newName) + { + } + + Str_32 BaseFile::ParseFullName_32(const Str_32& filePath) + { + UInt_64 index = 0; + + if (!filePath.Find(U"/", &index, SearchPattern::RIGHT_LEFT) && !filePath.Find(U"\\", &index, SearchPattern::RIGHT_LEFT)) + return filePath; + + return filePath.Sub(index); + } + + Str_16 BaseFile::ParseFullName_16(const Str_16& filePath) + { + UInt_64 index = 0; + + if (!filePath.Find(L"/", &index, SearchPattern::RIGHT_LEFT) && !filePath.Find(L"\\", &index, SearchPattern::RIGHT_LEFT)) + return filePath; + + return filePath.Sub(index); + } + + Str_8 BaseFile::ParseFullName_8(const Str_8& filePath) + { + UInt_64 index = 0; + + if (!filePath.Find("/", &index, SearchPattern::RIGHT_LEFT) && !filePath.Find("\\", &index, SearchPattern::RIGHT_LEFT)) + return filePath; + + return filePath.Sub(index); + } + + Str_32 BaseFile::ParseName_32(const Str_32& filePath) + { + UInt_64 index; + Str_32 file = filePath; + + if (file.Find(U"/", &index, SearchPattern::RIGHT_LEFT) || file.Find(U"\\", &index, SearchPattern::RIGHT_LEFT)) + file = file.Sub(index); + + if (!file.Find(U".", &index, SearchPattern::RIGHT_LEFT)) + return file; + + return file.Sub(0, index - 1); + } + + Str_16 BaseFile::ParseName_16(const Str_16& filePath) + { + UInt_64 index; + Str_16 file = filePath; + + if (file.Find(L"/", &index, SearchPattern::RIGHT_LEFT) || file.Find(L"\\", &index, SearchPattern::RIGHT_LEFT)) + file = file.Sub(index); + + if (!file.Find(L".", &index, SearchPattern::RIGHT_LEFT)) + return file; + + return file.Sub(0, index - 1); + } + + Str_8 BaseFile::ParseName_8(const Str_8& filePath) + { + UInt_64 index; + Str_8 file = filePath; + + if (file.Find("/", &index, SearchPattern::RIGHT_LEFT) || file.Find("\\", &index, SearchPattern::RIGHT_LEFT)) + file = file.Sub(index); + + if (!file.Find(".", &index, SearchPattern::RIGHT_LEFT)) + return file; + + return file.Sub(0, index - 1); + } + + Str_32 BaseFile::ParseExt_32(const Str_32& filePath) + { + UInt_64 index = 0; + + filePath.Find(U".", &index, SearchPattern::RIGHT_LEFT); + + return filePath.Sub(index); + } + + Str_16 BaseFile::ParseExt_16(const Str_16& filePath) + { + UInt_64 index = 0; + + filePath.Find(L".", &index, SearchPattern::RIGHT_LEFT); + + return filePath.Sub(index); + } + + Str_8 BaseFile::ParseExt_8(const Str_8& filePath) + { + UInt_64 index = 0; + + filePath.Find(".", &index, SearchPattern::RIGHT_LEFT); + + return filePath.Sub(index); + } +} diff --git a/src/io/BaseFileMonitor.cpp b/src/io/BaseFileMonitor.cpp new file mode 100644 index 0000000..81c247b --- /dev/null +++ b/src/io/BaseFileMonitor.cpp @@ -0,0 +1,49 @@ +#include "ehs/io/BaseFileMonitor.h" + +namespace ehs +{ + BaseFileMonitor::BaseFileMonitor(Str_8 filePath) + : filePath((Str_8&&)filePath) + { + } + + BaseFileMonitor::BaseFileMonitor(BaseFileMonitor&& fm) noexcept + : filePath((Str_8&&)fm.filePath) + { + } + + BaseFileMonitor::BaseFileMonitor(const BaseFileMonitor& fm) + : filePath(fm.filePath) + { + } + + BaseFileMonitor& BaseFileMonitor::operator=(BaseFileMonitor&& fm) noexcept + { + if (this == &fm) + return *this; + + filePath = (Str_8&&)fm.filePath; + + return *this; + } + + BaseFileMonitor& BaseFileMonitor::operator=(const BaseFileMonitor& fm) + { + if (this == &fm) + return *this; + + filePath = fm.filePath; + + return *this; + } + + Str_8 BaseFileMonitor::GetFilePath() const + { + return filePath; + } + + bool BaseFileMonitor::IsValid() const + { + return filePath.Size(); + } +} \ No newline at end of file diff --git a/src/io/BaseWindow.cpp b/src/io/BaseWindow.cpp new file mode 100644 index 0000000..db0e5ec --- /dev/null +++ b/src/io/BaseWindow.cpp @@ -0,0 +1,72 @@ +#include "ehs/io/BaseWindow.h" + +namespace ehs +{ + BaseWindow::BaseWindow() + : created(false), focused(false), cursorVisible(true), cursorConstrained(false), + state(WindowState::NONE) + { + } + + BaseWindow::BaseWindow(const BaseWindow &win) + : created(false), focused(false), cursorVisible(true), cursorConstrained(false), + state(WindowState::NONE) + { + } + + BaseWindow &BaseWindow::operator=(const BaseWindow &win) + { + if (this == &win) + return *this; + + created = false; + focused = false; + cursorPos = {}; + cursorVisible = true; + cursorConstrained = false; + state = WindowState::NONE; + ih = {}; + + return *this; + } + + bool BaseWindow::IsCreated() const + { + return created; + } + + bool BaseWindow::Poll() + { + return true; + } + + bool BaseWindow::HasFocus() const + { + return focused; + } + + Vec2_s32 BaseWindow::GetCursorPos() const + { + return cursorPos; + } + + bool BaseWindow::IsCursorVisible() const + { + return cursorVisible; + } + + bool BaseWindow::IsCursorConstrained() const + { + return cursorConstrained; + } + + WindowState BaseWindow::GetState() const + { + return state; + } + + const InputHandler* BaseWindow::GetInputHandler() const + { + return &ih; + } +} \ No newline at end of file diff --git a/src/io/COM.cpp b/src/io/COM.cpp new file mode 100644 index 0000000..ec49844 --- /dev/null +++ b/src/io/COM.cpp @@ -0,0 +1,134 @@ +#include "io/COM.h" +#include "ehs/Str.h" +#include "ehs/Log.h" + +namespace ehs +{ + COM::COM() + : port(0), baudRate(9600), byteSize(8), parity(Parity::NONE), stopBits(StopBits::ONE), hdl(nullptr), initialized(false) + { + } + + COM::COM(const UInt_8 port, const UInt_32 baudRate, const UInt_8 byteSize, const Parity parity, const StopBits stopBits) + : port(port), baudRate(baudRate), byteSize(byteSize), parity(parity), stopBits(stopBits), hdl(nullptr), initialized(false) + { + } + + void COM::Initialize() + { + if (initialized) + return; + + hdl = CreateFileW(L"\\\\.\\COM" + Str_16::FromNum(port), GENERIC_READ | GENERIC_WRITE, + 0, nullptr, OPEN_EXISTING, 0, + nullptr); + if (hdl == INVALID_HANDLE_VALUE) + { + EHS_LOG_INT("Error", 0, "Failed to create handle at COM" + Str_8::FromNum(port) + " with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + DCB dcb = {}; + dcb.DCBlength = sizeof(DCB); + + if (!GetCommState(hdl, &dcb)) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve COM" + Str_8::FromNum(port) + " state with error #" + Str_8::FromNum(GetLastError()) + "."); + UnInitialize(); + return; + } + + dcb.BaudRate = (DWORD)baudRate; + dcb.ByteSize = (BYTE)byteSize; + dcb.fParity = (DWORD)parity; + dcb.StopBits = (BYTE)stopBits; + dcb.fDtrControl = DTR_CONTROL_ENABLE; + + if (!SetCommState(hdl, &dcb)) + { + EHS_LOG_INT("Error", 2, "Failed to set COM" + Str_8::FromNum(port) + " state with error #" + Str_8::FromNum(GetLastError()) + "."); + UnInitialize(); + return; + } + + SetCommMask(hdl, EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY); + + PurgeComm(hdl, PURGE_RXCLEAR | PURGE_TXCLEAR); + + initialized = true; + } + + void COM::UnInitialize() + { + if (hdl) + { + if (!CloseHandle(hdl)) + EHS_LOG_INT("Error", 0, "Failed to close COM" + Str_8::FromNum(port) + " handle with error #" + Str_8::FromNum(GetLastError()) + "."); + + hdl = nullptr; + } + + initialized = false; + } + + UInt_32 COM::Wait() + { + UInt_32 event = 0; + + if (!WaitCommEvent(hdl, (DWORD*)&event, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed to wait for COM" + Str_8::FromNum(port) + " event with error #" + Str_8::FromNum(GetLastError()) + "."); + UnInitialize(); + } + + return event; + } + + void COM::Transmit(const Char_8 data) + { + if (!initialized) + return; + + if (!TransmitCommChar(hdl, data)) + { + EHS_LOG_INT("Error", 0, "Failed to transmit character to COM" + Str_8::FromNum(port) + " with error #" + + Str_8::FromNum(GetLastError()) + "."); + UnInitialize(); + } + } + + UInt_32 COM::Send(const Char_8* data, const UInt_32 size) + { + UInt_32 sent = 0; + + if (!WriteFile(hdl, (void*)data, (DWORD)size, (DWORD*)&sent, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed to receive data from COM" + Str_8::FromNum(port) + " with error #" + Str_8::FromNum(GetLastError()) + "."); + UnInitialize(); + } + + return sent; + } + + UInt_32 COM::Receive(const Char_8* data, const UInt_32 size) + { + UInt_32 received = 0; + + if (!ReadFile(hdl, (void*)data, (DWORD)size, (DWORD*)&received, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed to receive data from COM" + Str_8::FromNum(port) + " with error #" + Str_8::FromNum(GetLastError()) + "."); + UnInitialize(); + } + + return received; + } + + void COM::Flush() + { + if (!FlushFileBuffers(hdl)) + { + EHS_LOG_INT("Error", 0, "Failed to flush data for COM" + Str_8::FromNum(port) + " with error #" + Str_8::FromNum(GetLastError()) + "."); + UnInitialize(); + } + } +} \ No newline at end of file diff --git a/src/io/Console.cpp b/src/io/Console.cpp new file mode 100644 index 0000000..f980b91 --- /dev/null +++ b/src/io/Console.cpp @@ -0,0 +1,799 @@ +#include "ehs/io/Console.h" +#include "ehs/Log.h" +#include "ehs/io/File.h" + +#if defined(EHS_OS_LINUX) + #include + #include + #include +#endif + +namespace ehs +{ + ConsoleHdl Console::hdlOut = 0; + ConsoleHdl Console::hdlIn = 0; + + #if defined(EHS_OS_WINDOWS) + bool Console::isConsole = true; + #endif + + void Console::Attach() + { + #if defined(EHS_OS_WINDOWS) + if (!AttachConsole(ATTACH_PARENT_PROCESS)) + { + DWORD code = GetLastError(); + if (code == ERROR_INVALID_HANDLE) + return; + } + + hdlIn = GetStdHandle(STD_INPUT_HANDLE); + hdlOut = GetStdHandle(STD_OUTPUT_HANDLE); + + DWORD mode = 0; + + if (!GetConsoleMode(hdlOut, &mode)) + { + if (GetLastError() == ERROR_INVALID_HANDLE) + isConsole = false; + } + else + { + //DWORD code = WaitForSingleObject(hdlIn, 15000); + //if (code == WAIT_FAILED || code == WAIT_TIMEOUT || code == WAIT_ABANDONED) + // EHS_LOG_INT("Error", 0, "Failed to wait for console input."); + + isConsole = true; + } + #elif defined(EHS_OS_LINUX) + hdlOut = open("/dev/stdout", O_WRONLY | O_SYNC); + hdlIn = open("/dev/stdin", O_RDONLY); + #else + return; + #endif + } + + bool Console::Create() + { + #if defined(EHS_OS_WINDOWS) + if (!AllocConsole()) + return false; + + hdlIn = GetStdHandle(STD_INPUT_HANDLE); + hdlOut = GetStdHandle(STD_OUTPUT_HANDLE); + + if (WaitForSingleObject(hdlIn, EHS_INFINITE) == WAIT_FAILED) + EHS_LOG_INT("Error", 2, "Failed to wait for console input."); + + //if (!SetConsoleActiveScreenBuffer(hdlOut)) + // EHS_LOG_INT("Error", 3, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + isConsole = true; + #endif + + return true; + } + + void Console::Free() + { + #if defined(EHS_OS_WINDOWS) + if (!FreeConsole()) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + #elif defined(EHS_OS_LINUX) + int code = close(hdlOut); + if (code == -1) + EHS_LOG_INT("Error", 0, "Failed to free the console output with error #" + Str_8::FromNum(errno) + "."); + + code = close(hdlIn); + if (code == -1) + EHS_LOG_INT("Error", 0, "Failed to free the console input with error #" + Str_8::FromNum(errno) + "."); + #endif + + hdlOut = 0; + hdlIn = 0; + } + + bool Console::CanRead() + { + return hdlIn; + } + + bool Console::CanWrite() + { + return hdlOut; + } + + void Console::Write_32(const Str_32& str, const bool newLine) + { + #if defined(EHS_OS_WINDOWS) + if (isConsole) + { + Str_16 r = UTF::To_16(str); + if (newLine) + r += L"\r\n"; + + DWORD offset = 0; + + do + { + DWORD written = 0; + if (!WriteConsoleW(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + offset += written; + } + while (offset < r.Size()); + } + else + { + Str_8 r = UTF::To_8(str); + if (newLine) + r += "\r\n"; + + DWORD offset = 0; + + do + { + DWORD written = 0; + if (!WriteFile(hdlOut, &((Char_8*)r)[offset], (DWORD)r.Size(true) - offset, &written, nullptr)) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + offset += written; + } + while (offset < r.Size(true)); + } + #elif defined(EHS_OS_LINUX) + Str_32 result = str; + if (newLine) + result += U"\n"; + + UInt_64 offset = 0; + do { + ssize_t written = write(hdlOut, result, result.Size(true)); + if (written == -1) + { + EHS_LOG_INT("Error", 0, "Failed to write to console with error #" + Str_8::FromNum(errno) + "."); + return; + } + offset += written; + } + while (offset < result.Size(true)); + #else + return; + #endif + } + + void Console::Write_16(const Str_16& str, const bool newLine) + { + #if defined(EHS_OS_WINDOWS) + if (isConsole) + { + Str_16 r = str; + if (newLine) + r += L"\r\n"; + + DWORD offset = 0; + + do + { + DWORD written = 0; + if (!WriteConsoleW(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + offset += written; + } + while (offset < r.Size()); + } + else + { + Str_8 r = UTF::To_8(str); + if (newLine) + r += "\r\n"; + + DWORD offset = 0; + + do + { + DWORD written = 0; + if (!WriteFile(hdlOut, &((Char_8*)r)[offset], (DWORD)r.Size(true) - offset, &written, nullptr)) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + offset += written; + } + while (offset < r.Size(true)); + } + #elif defined(EHS_OS_LINUX) + Str_16 result = str; + if (newLine) + result += L"\n"; + + UInt_64 offset = 0; + do { + ssize_t written = write(hdlOut, result, result.Size(true)); + if (written == -1) + { + EHS_LOG_INT("Error", 0, "Failed to write to console with error #" + Str_8::FromNum(errno) + "."); + return; + } + offset += written; + } + while (offset < result.Size(true)); + #endif + } + + void Console::Write_8(const Str_8& str, const bool newLine) + { + #if defined(EHS_OS_WINDOWS) + if (isConsole) + { + Str_16 r = UTF::To_16(str); + if (newLine) + r += L"\r\n"; + + DWORD offset = 0; + + do + { + DWORD written = 0; + if (!WriteConsoleW(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + offset += written; + } + while (offset < r.Size()); + } + else + { + Str_8 r = str; + if (newLine) + r += "\r\n"; + + DWORD offset = 0; + + do + { + DWORD written = 0; + if (!WriteFile(hdlOut, &r[offset], (DWORD)r.Size() - offset, &written, nullptr)) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + offset += written; + } + while (offset < r.Size()); + } + #elif defined(EHS_OS_LINUX) + Str_8 result = str; + if (newLine) + result += "\n"; + + UInt_64 offset = 0; + do { + ssize_t written = write(hdlOut, result, result.Size()); + if (written == -1) + { + EHS_LOG_INT("Error", 0, "Failed to write to console with error #" + Str_8::FromNum(errno) + "."); + return; + } + offset += written; + } + while (offset < result.Size()); + #endif + } + + Str_32 Console::Read_32(const UInt_64 bufferSize) + { + if (!hdlIn) + return U""; + + #if defined(EHS_OS_WINDOWS) + if (isConsole) + { + Str_16 result; + + DWORD offset = 0; + + do + { + result.Resize(result.Size() + bufferSize); + + DWORD read = 0; + + if (!ReadConsoleW(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + return U""; + } + + offset += read; + } + while (result[offset - 1] != L'\n'); + + if (offset >= 2 && result[offset - 2] == L'\r' && result[offset - 1] == L'\n') + result.Resize(offset - 2); + else + result.Resize(offset - 1); + + return UTF::To_32(result); + } + else + { + Str_8 result; + + DWORD offset = 0; + + do + { + result.Resize(result.Size() + bufferSize); + + DWORD read = 0; + + if (!ReadFile(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + return U""; + } + + offset += read; + } + while (result[offset - 1] != '\n'); + + if (offset >= 2 && result[offset - 2] == '\r' && result[offset - 1] == '\n') + result.Resize(offset - 2); + else + result.Resize(offset - 1); + + return UTF::To_32(result); + } + #elif defined(EHS_OS_LINUX) + Str_32 result; + Str_32 input(bufferSize); + ssize_t read = 0; + + do + { + read = ::read(hdlIn, input, bufferSize); + if (read == -1) + { + EHS_LOG_INT("Error", 0, "Failed to read from console with error #" + Str_8::FromNum(errno) + "."); + return result; + } + result.Push(input, read); + } + while (input[read - 1] != U'\n'); + + return result; + #else + return {}; + #endif + } + + Str_16 Console::Read_16(const UInt_64 bufferSize) + { + if (!hdlIn) + return L""; + + #if defined(EHS_OS_WINDOWS) + if (isConsole) + { + Str_16 result; + + DWORD offset = 0; + + do + { + result.Resize(result.Size() + bufferSize); + + DWORD read = 0; + + if (!ReadConsoleW(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + return L""; + } + + offset += read; + } + while (result[offset - 1] != L'\n'); + + if (offset >= 2 && result[offset - 2] == L'\r' && result[offset - 1] == L'\n') + result.Resize(offset - 2); + else + result.Resize(offset - 1); + + return result; + } + else + { + Str_8 result; + + DWORD offset = 0; + + do + { + result.Resize(result.Size() + bufferSize); + + DWORD read = 0; + + if (!ReadFile(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + return L""; + } + + offset += read; + } + while (result[offset - 1] != '\n'); + + if (offset >= 2 && result[offset - 2] == '\r' && result[offset - 1] == '\n') + result.Resize(offset - 2); + else + result.Resize(offset - 1); + + return UTF::To_16(result); + } + #elif defined(EHS_OS_LINUX) + Str_16 result; + Str_16 input(bufferSize); + ssize_t read = 0; + + do + { + read = ::read(hdlIn, input, bufferSize); + if (read == -1) + { + EHS_LOG_INT("Error", 0, "Failed to read from console with error #" + Str_8::FromNum(errno) + "."); + return result; + } + result.Push(input, read); + } + while (input[read - 1] != L'\n'); + + return result; + #else + return {}; + #endif + } + + Str_8 Console::Read_8(const UInt_64 bufferSize) + { + if (!hdlIn) + return ""; + + #if defined(EHS_OS_WINDOWS) + if (isConsole) + { + Str_16 result; + + DWORD offset = 0; + + do + { + result.Resize(result.Size() + bufferSize); + + DWORD read = 0; + + if (!ReadConsoleW(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + return ""; + } + + offset += read; + } + while (result[offset - 1] != L'\n'); + + if (offset >= 2 && result[offset - 2] == L'\r' && result[offset - 1] == L'\n') + result.Resize(offset - 2); + else + result.Resize(offset - 1); + + return UTF::To_8(result); + } + else + { + Str_8 result; + + DWORD offset = 0; + + do + { + result.Resize(result.Size() + bufferSize); + + DWORD read = 0; + + if (!ReadFile(hdlIn, &result[offset], (DWORD)bufferSize, &read, nullptr)) + { + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + + return ""; + } + + offset += read; + } + while (result[offset - 1] != '\n'); + + if (offset >= 2 && result[offset - 2] == '\r' && result[offset - 1] == '\n') + result.Resize(offset - 2); + else + result.Resize(offset - 1); + + return result; + } + #elif defined(EHS_OS_LINUX) + Str_8 result; + Str_8 input(bufferSize); + ssize_t read = 0; + + do + { + read = ::read(hdlIn, input, bufferSize); + if (read == -1) + { + EHS_LOG_INT("Error", 0, "Failed to read from console with error #" + Str_8::FromNum(errno) + "."); + return result; + } + result.Push(input, read); + } + while (input[read - 1] != '\n'); + + return result.Sub(0, result.Size() - 1); + #else + return {}; + #endif + } + + void Console::Clear() + { + #if defined(EHS_OS_WINDOWS) + CONSOLE_SCREEN_BUFFER_INFO info = {}; + if (!GetConsoleScreenBufferInfo(hdlOut, &info)) + return; + + DWORD size = info.dwSize.X * info.dwSize.Y; + + DWORD written = 0; + if (!FillConsoleOutputCharacterW(hdlOut, L' ', size, {0, 0}, &written)) + return; + + if (!GetConsoleScreenBufferInfo(hdlOut, &info)) + return; + + if (!FillConsoleOutputAttribute(hdlOut, info.wAttributes, size, {0, 0}, &written)) + return; + + SetConsoleCursorPosition(hdlOut, {0, 0}); + #elif defined(EHS_OS_LINUX) + const Char_8 code[] = "\033[2J\033[1;1H"; + UInt_64 offset = 0; + do { + ssize_t written = write(hdlOut, code, sizeof(code)); + if (written == -1) + { + EHS_LOG_INT("Error", 0, "Failed to clear console with error #" + Str_8::FromNum(errno) + "."); + return; + } + offset += written; + } + while (offset < sizeof(code)); + #endif + } + + void Console::SetTitle_32(const Str_32& title) + { + #if defined(EHS_OS_WINDOWS) + if (!SetConsoleTitleW(UTF::To_16(title))) + EHS_LOG_INT("Error", 0, "Failed to set console title with error #" + Str_8::FromNum(GetLastError()) + "."); + #elif defined(EHS_OS_LINUX) + Str_32 code = U"\033]0;" + title + U"\007"; + UInt_64 offset = 0; + do { + ssize_t written = write(hdlOut, code, code.Size(true)); + if (written == -1) + { + EHS_LOG_INT("Error", 0, "Failed to set console title with error #" + Str_8::FromNum(errno) + "."); + return; + } + offset += written; + } + while (offset < code.Size(true)); + #endif + } + + void Console::SetTitle_16(const Str_16& title) + { + #if defined(EHS_OS_WINDOWS) + if (!SetConsoleTitleW(title)) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + #elif defined(EHS_OS_LINUX) + Str_16 code = L"\033]0;" + title + L"\007"; + UInt_64 offset = 0; + do { + ssize_t written = write(hdlOut, code, code.Size(true)); + if (written == -1) + { + EHS_LOG_INT("Error", 0, "Failed to set console title with error #" + Str_8::FromNum(errno) + "."); + return; + } + offset += written; + } + while (offset < code.Size(true)); + #endif + } + + void Console::SetTitle_8(const Str_8& title) + { + #if defined(EHS_OS_WINDOWS) + if (!SetConsoleTitleW(UTF::To_16(title))) + EHS_LOG_INT("Error", 0, "Failed with error #" + Str_8::FromNum(GetLastError()) + "."); + #elif defined(EHS_OS_LINUX) + Str_8 code = "\033]0;" + title + "\007"; + UInt_64 offset = 0; + do { + ssize_t written = write(hdlOut, code, code.Size()); + if (written == -1) + { + EHS_LOG_INT("Error", 0, "Failed to set console title with error #" + Str_8::FromNum(errno) + "."); + return; + } + offset += written; + } + while (offset < code.Size()); + #endif + } + + Str_32 Console::GetTitle_32() + { + #if defined(EHS_OS_WINDOWS) + Str_16 title(EHS_MAX_PATH); + + DWORD size = GetConsoleTitleW(&title[0], EHS_MAX_PATH); + + title.Resize(size); + + return UTF::To_32(title); + #else + return {}; + #endif + } + + Str_16 Console::GetTitle_16() + { + #if defined(EHS_OS_WINDOWS) + Str_16 title(EHS_MAX_PATH); + + DWORD size = GetConsoleTitleW(&title[0], EHS_MAX_PATH); + + title.Resize(size); + + return title; + #else + return {}; + #endif + } + + Str_8 Console::GetTitle_8() + { + #if defined(EHS_OS_WINDOWS) + Str_16 title(EHS_MAX_PATH); + + DWORD size = GetConsoleTitleW(&title[0], EHS_MAX_PATH); + + title.Resize(size); + + return UTF::To_8(title); + #else + return {}; + #endif + } + + Vector Console::GetArgs_32(const UInt_64 bufferSize) + { + #if defined(EHS_OS_WINDOWS) + return UTF::To_32(GetCommandLineW()).Split(U" "); + #elif defined(EHS_OS_LINUX) + File cmdFile("/proc/self/cmdline", Mode::READ, Disposition::OPEN); + Array data = cmdFile.ReadArray(bufferSize); + cmdFile.Release(); + + Vector args; + + UInt_64 o = 0; + for (UInt_64 i = 0; i < data.Size() - 1; i += sizeof(Char_8)) + { + if (data[i] != '\0') + continue; + + + + args.Push(UTF::To_32((Char_8*)&data[o], i - o)); + o = i + sizeof(Char_8); + } + + if (o < data.Size()) + args.Push(UTF::To_32((Char_8*)&data[o], data.Size() - o - 1)); + + return args; + #else + return {}; + #endif + } + + Vector Console::GetArgs_16(const UInt_64 bufferSize) + { + #if defined(EHS_OS_WINDOWS) + return Str_16(GetCommandLineW()).Split(L" "); + #elif defined(EHS_OS_LINUX) + File cmdFile("/proc/self/cmdline", Mode::READ, Disposition::OPEN); + Array data = cmdFile.ReadArray(bufferSize); + cmdFile.Release(); + + Vector args; + + UInt_64 o = 0; + for (UInt_64 i = 0; i < data.Size() - 1; i += sizeof(Char_8)) + { + if (data[i] != '\0') + continue; + + args.Push(UTF::To_16((Char_8*)&data[o], i - o)); + o = i + sizeof(Char_8); + } + + if (o < data.Size()) + args.Push(UTF::To_16((Char_8*)&data[o], data.Size() - o - 1)); + + return args; + #else + return {}; + #endif + } + + Vector Console::GetArgs_8(const UInt_64 bufferSize) + { + #if defined(EHS_OS_WINDOWS) + return UTF::To_8(GetCommandLineW()).Split(" "); + #elif defined(EHS_OS_LINUX) + File cmdFile("/proc/self/cmdline", Mode::READ, Disposition::OPEN); + Array data = cmdFile.ReadArray(bufferSize); + cmdFile.Release(); + + Vector args; + + UInt_64 o = 0; + for (UInt_64 i = 0; i < data.Size() - 1; i += sizeof(Char_8)) + { + if (data[i] != '\0') + continue; + + args.Push(Str_8((Char_8*)&data[o], i - o)); + o = i + sizeof(Char_8); + } + + if (o < data.Size()) + args.Push(Str_8((Char_8*)&data[o], data.Size() - o - 1)); + + return args; + #else + return {}; + #endif + } + + /* + void* Console::GetHandle() + { + #if defined(EHS_OS_WINDOWS) + void* hdl = FindWindowW(nullptr, GetTitle_16()); + if (hdl == nullptr) + EHS_LOG_INT("Error", 0, "Failed to retrieve native handle with error #" + Str_8::FromNum(GetLastError()) + "."); + + return hdl; + #else + return nullptr; + #endif + } + */ +} \ No newline at end of file diff --git a/src/io/FileMonitor_UNX.cpp b/src/io/FileMonitor_UNX.cpp new file mode 100644 index 0000000..f499c3a --- /dev/null +++ b/src/io/FileMonitor_UNX.cpp @@ -0,0 +1,174 @@ +#include "ehs/io/FileMonitor_UNX.h" +#include "ehs/Log.h" + +#include +#include +#include +#include + +#define BUF_LEN (1024 * (sizeof(inotify_event) + 16)) + +namespace ehs +{ + FileMonitor::~FileMonitor() + { + inotify_rm_watch( hdl, wd); + close(hdl); + } + + FileMonitor::FileMonitor() + : hdl(-1), wd(-1) + { + } + + FileMonitor::FileMonitor(Str_8 filePath) + : BaseFileMonitor((Str_8&&)filePath), hdl(-1), wd(-1) + { + FileMonitor::Initialize(); + } + + FileMonitor::FileMonitor(FileMonitor&& fm) noexcept + : BaseFileMonitor((BaseFileMonitor&&)fm), hdl(fm.hdl), wd(fm.wd) + { + fm.hdl = -1; + fm.wd = -1; + } + + FileMonitor::FileMonitor(const FileMonitor& fm) + : BaseFileMonitor(fm), hdl(-1), wd(-1) + { + FileMonitor::Initialize(); + } + + FileMonitor& FileMonitor::operator=(FileMonitor&& fm) noexcept + { + if (this == &fm) + return *this; + + Release(); + + BaseFileMonitor::operator=((BaseFileMonitor&&)fm); + + hdl = fm.hdl; + wd = fm.wd; + + fm.hdl = -1; + fm.wd = -1; + + return *this; + } + + FileMonitor& FileMonitor::operator=(const FileMonitor& fm) + { + if (this == &fm) + return *this; + + Release(); + + BaseFileMonitor::operator=(fm); + + hdl = -1; + wd = -1; + + Initialize(); + + return *this; + } + + void FileMonitor::Initialize() + { + if (!IsValid() || IsInitialized()) + return; + + hdl = inotify_init(); + if (hdl < 0) + { + EHS_LOG_INT("Error", 0, "Failed to initialize inotify."); + return; + } + + int flags = fcntl(hdl, F_GETFL, 0); + if (flags == -1) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve flags with error #" + Str_8::FromNum(errno) + "."); + return; + } + + flags |= O_NONBLOCK; + + if (fcntl(hdl, F_SETFL, flags) == -1) + { + EHS_LOG_INT("Error", 2, "Failed to set flags with error #" + Str_8::FromNum(errno) + "."); + return; + } + + wd = inotify_add_watch( hdl, filePath, IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_ACCESS); + if (wd < 0) + { + EHS_LOG_INT("Error", 3, "Failed to add watch."); + close(hdl); + hdl = -1; + return; + } + } + + void FileMonitor::Release() + { + if (!IsValid() || !IsInitialized()) + return; + + inotify_rm_watch( hdl, wd); + wd = -1; + + close(hdl); + hdl = -1; + } + + UInt_8 FileMonitor::Poll() + { + UInt_8 mask = EHS_FE_NONE; + + if (!IsValid() || !IsInitialized()) + return mask; + + Byte buffer[BUF_LEN]; + SInt_64 length = read(hdl, buffer, BUF_LEN); + if (length < 0) + { + UInt_8 code = errno; + if (code != EWOULDBLOCK) + EHS_LOG_INT("Error", 0, "Failed to read with error #" + Str_8::FromNum(code) + "."); + + return mask; + } + + UInt_64 i = 0; + while (i < length) + { + inotify_event *event = (inotify_event*)&buffer[i]; + if (event->mask & IN_MODIFY) + mask |= EHS_FE_MODIFIED; + + if (event->mask & IN_DELETE_SELF) + mask |= EHS_FE_DELETED; + + if (event->mask & IN_MOVE_SELF) + mask |= EHS_FE_MOVED; + + if (event->mask & IN_ACCESS) + mask |= EHS_FE_OPENED; + + i += sizeof(inotify_event) + event->len; + } + + if (mask & EHS_FE_DELETED || mask & EHS_FE_MOVED) + Release(); + + return mask; + } + + bool FileMonitor::IsInitialized() const + { + return hdl >= 0 && wd >= 0; + } +} \ No newline at end of file diff --git a/src/io/FileMonitor_W32.cpp b/src/io/FileMonitor_W32.cpp new file mode 100644 index 0000000..c3733a6 --- /dev/null +++ b/src/io/FileMonitor_W32.cpp @@ -0,0 +1,123 @@ +#include "io/FileMonitor_W32.h" +#include "ehs/Log.h" +#include "ehs/UTF.h" + +#include + +namespace ehs +{ + FileMonitor::~FileMonitor() + { + if (!IsInitialized()) + return; + + if (!CloseHandle(hdl)) + EHS_LOG_INT("Error", 0, "Failed to close file at file path, \"" + filePath + "\", with error #" + GetLastError() + "."); + } + + FileMonitor::FileMonitor() + : hdl(nullptr), time{} + { + } + + FileMonitor::FileMonitor(Str_8 filePath) + : BaseFileMonitor((Str_8&&)filePath), hdl(INVALID_HANDLE_VALUE), time{} + { + FileMonitor::Initialize(); + } + + FileMonitor::FileMonitor(FileMonitor&& fm) noexcept + : BaseFileMonitor((BaseFileMonitor&&)fm), hdl(fm.hdl), time(fm.time) + { + fm.hdl = INVALID_HANDLE_VALUE; + fm.time = {}; + } + + FileMonitor::FileMonitor(const FileMonitor& fm) + : BaseFileMonitor(fm), hdl(INVALID_HANDLE_VALUE), time{} + { + FileMonitor::Initialize(); + } + + FileMonitor& FileMonitor::operator=(FileMonitor&& fm) noexcept + { + if (this == &fm) + return *this; + + Release(); + + BaseFileMonitor::operator=((BaseFileMonitor&&)fm); + + hdl = fm.hdl; + time = fm.time; + + fm.hdl = INVALID_HANDLE_VALUE; + fm.time = {}; + + return *this; + } + + FileMonitor& FileMonitor::operator=(const FileMonitor& fm) + { + if (this == &fm) + return *this; + + Release(); + + BaseFileMonitor::operator=(fm); + + hdl = INVALID_HANDLE_VALUE; + time = {}; + + Initialize(); + + return *this; + } + + void FileMonitor::Initialize() + { + if (!IsValid() || IsInitialized()) + return; + + hdl = CreateFileW(UTF::To_16(filePath), GENERIC_READ, FILE_SHARE_READ, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hdl == INVALID_HANDLE_VALUE) + EHS_LOG_INT("Error", 0, "Failed to open file at file path, \"" + filePath + "\", with error #" + GetLastError() + "."); + } + + void FileMonitor::Release() + { + if (!IsValid() || !IsInitialized()) + return; + + if (!CloseHandle(hdl)) + EHS_LOG_INT("Error", 0, "Failed to close file at file path, \"" + filePath + "\", with error #" + GetLastError() + "."); + + hdl = nullptr; + } + + UInt_8 FileMonitor::Poll() + { + UInt_8 mask = EHS_FE_NONE; + + FILETIME lastWriteTime = {}; + if (!GetFileTime(hdl, nullptr, nullptr, &lastWriteTime)) + { + Release(); + return mask; + } + + if (time.dwLowDateTime == lastWriteTime.dwLowDateTime && time.dwHighDateTime == lastWriteTime.dwHighDateTime) + return mask; + + time = lastWriteTime; + mask = EHS_FE_MODIFIED; + + return mask; + } + + bool FileMonitor::IsInitialized() const + { + return hdl != INVALID_HANDLE_VALUE; + } +} \ No newline at end of file diff --git a/src/io/File_UNX.cpp b/src/io/File_UNX.cpp new file mode 100644 index 0000000..0cb1409 --- /dev/null +++ b/src/io/File_UNX.cpp @@ -0,0 +1,307 @@ +#include "ehs/io/File_UNX.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace ehs +{ + File::~File() + { + if (map != MAP_FAILED && munmap(map, mapSize) == -1) + EHS_LOG_INT("Error", 0, "Failed to unmap with error #" + Str_8::FromNum(errno) + "."); + + if (hdl >= 0 && close(hdl) == -1) + EHS_LOG_INT("Error", 0, "Failed to close file handle with error #" + Str_8::FromNum(errno) + "."); + } + + File::File() + : hdl(-1), map(MAP_FAILED), mapSize(0) + { + } + + File::File(const Str_8 &filePath, const Mode mode, const Disposition disposition) + : BaseFile(filePath, mode, disposition), hdl(-1), map(MAP_FAILED), mapSize(0) + { + int linuxMode = 0; + switch (mode) + { + case Mode::READ: + linuxMode = O_RDONLY; + break; + case Mode::WRITE: + linuxMode = O_WRONLY; + break; + case Mode::READ_WRITE: + linuxMode = O_RDWR; + break; + } + + int linuxDisp = 0; + switch (disposition) + { + case Disposition::CREATE_PERSISTENT: + linuxDisp = O_CREAT; + break; + case Disposition::CREATE: + linuxDisp = O_CREAT | O_EXCL; + break; + case Disposition::OPEN_PERSISTENT: + linuxDisp = O_CREAT; + break; + case Disposition::OPEN: + linuxDisp = 0; + break; + case Disposition::TRUNCATE: + linuxDisp = O_TRUNC; + break; + } + + hdl = open64(path, linuxMode | linuxDisp, S_IRUSR | S_IWUSR); + if (hdl == -1) + { + SInt_32 code = errno; + if (code == EEXIST && (disposition == Disposition::CREATE_PERSISTENT || disposition == Disposition::OPEN_PERSISTENT)) + { + hdl = open64(path, linuxMode, S_IRUSR | S_IWUSR); + if (hdl == -1) + EHS_LOG_INT("Error", 0, strerror(errno)); + } + else + { + if (code == ENOENT) + EHS_LOG_INT("Error", 0, "File at filepath, \"" + filePath + "\" not found."); + else + EHS_LOG_INT("Error", 0, strerror(code)); + } + } + } + + File::File(File&& file) noexcept + : BaseFile(std::move(file)), hdl(file.hdl), map(file.map), mapSize(file.mapSize) + { + file.hdl = -1; + file.map = MAP_FAILED; + file.mapSize = 0; + } + + File::File(const File &file) + : BaseFile(file), hdl(-1), map(MAP_FAILED), mapSize(0) + { + } + + File& File::operator=(File&& file) noexcept + { + if (this == &file) + return *this; + + BaseFile::operator=(std::move(file)); + + hdl = file.hdl; + map = file.map; + mapSize = file.mapSize; + + file.hdl = -1; + + return *this; + } + + File& File::operator=(const File &file) + { + if (this == &file) + return *this; + + BaseFile::operator=(file); + + hdl = -1; + map = MAP_FAILED; + mapSize = 0; + + return *this; + } + + File::operator const Byte*() const + { + return (const Byte*)map; + } + + File::operator Byte*() + { + return (Byte*)map; + } + + void File::Release() + { + if (IsMapped() && munmap(map, mapSize) == -1) + EHS_LOG_INT("Error", 0, "Failed to unmap with error #" + Str_8::FromNum(errno) + "."); + map = MAP_FAILED; + mapSize = 0; + + if (IsValid() && close(hdl) == -1) + EHS_LOG_INT("Error", 0, "Failed to close file handle with error #" + Str_8::FromNum(errno) + "."); + hdl = -1; + } + + bool File::IsMapped() const + { + return map != MAP_FAILED; + } + + UInt_64 File::MapSize() const + { + return mapSize; + } + + void File::Map(const UInt_64 offset, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return; + + int linuxMode = 0; + switch (mode) + { + case Mode::READ: + linuxMode = PROT_READ; + break; + case Mode::WRITE: + linuxMode = PROT_WRITE; + break; + case Mode::READ_WRITE: + linuxMode = PROT_READ | PROT_WRITE; + break; + } + + map = mmap64(nullptr, size, linuxMode, MAP_SHARED, hdl, (off64_t)offset); + if (map == MAP_FAILED) + { + EHS_LOG_INT("Error", 0, "Failed to map with error #" + Str_8::FromNum(errno) + "."); + return; + } + + mapSize = size; + } + + void File::Unmap() + { + if (!IsValid() || !IsMapped()) + return; + + if (munmap(map, mapSize) == -1) + EHS_LOG_INT("Error", 0, "Failed to unmap with error #" + Str_8::FromNum(errno) + "."); + + map = MAP_FAILED; + mapSize = 0; + } + + void File::FlushMap() + { + if (!IsValid() || !IsMapped()) + return; + + if (msync((void*)map, mapSize, MS_SYNC) == -1) + EHS_LOG_INT("Error", 0, "Failed to flush view with error #" + Str_8::FromNum(errno) + "."); + } + + UInt_64 File::Write(const Byte *const data, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return 0; + + SInt_64 written = 0; + written = write(hdl, data, size); + if (written == -1) + EHS_LOG_INT("Error", 0, "Failed to write to file, \"" + path + "\", with error #" + Str_8::FromNum(errno) + "."); + + return (UInt_64)written; + } + + UInt_64 File::Read(Byte *const data, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return 0; + + SInt_64 read = 0; + read = ::read(hdl, data, (size_t)size); + if (read == -1) + EHS_LOG_INT("Error", 0, "Failed to read from file, \"" + path + "\", with error #" + Str_8::FromNum(errno) + "."); + + return (UInt_64)read; + } + + void File::Seek(UInt_64 index) + { + if (!IsValid() || IsMapped()) + return; + + if (lseek64(hdl, (off64_t)index, SEEK_SET) == -1) + EHS_LOG_INT("Error", 0, "Failed to seek with error #" + Str_8::FromNum(errno) + "."); + } + + void File::SeekBeginning() + { + if (!IsValid() || IsMapped()) + return; + + if (lseek64(hdl, 0, SEEK_SET) == -1) + EHS_LOG_INT("Error", 0, "Failed to seek with error #" + Str_8::FromNum(errno) + "."); + } + + void File::SeekEnd() + { + if (!IsValid() || IsMapped()) + return; + + if (lseek64(hdl, 0, SEEK_END) == -1) + EHS_LOG_INT("Error", 0, "Failed to seek with error #" + Str_8::FromNum(errno) + "."); + } + + void File::Truncate(const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return; + + if (ftruncate64(hdl, (off64_t)size) == -1) + EHS_LOG_INT("Error", 0, "Failed to truncate with error #" + Str_8::FromNum(errno) + "."); + } + + UInt_64 File::Size() const + { + struct stat64 info = {}; + + if (fstat64(hdl, &info) == -1) + EHS_LOG_INT("Error", 0, "Failed to retrieve file size with error #" + Str_8::FromNum(errno) + "."); + + return info.st_size; + } + + bool File::IsValid() const + { + return hdl >= 0; + } + + void File::Rename_32(const Str_32& filePath, const Str_32& newName) + { + Rename_8(UTF::To_8(filePath), UTF::To_8(newName)); + } + + void File::Rename_16(const Str_16& filePath, const Str_16& newName) + { + Rename_8(UTF::To_8(filePath), UTF::To_8(newName)); + } + + void File::Rename_8(const Str_8& filePath, const Str_8& newName) + { + UInt_64 index = 0; + Str_8 path; + + if (filePath.Find("/", &index, SearchPattern::RIGHT_LEFT) || filePath.Find("\\", &index, SearchPattern::RIGHT_LEFT)) + path = filePath.Sub(0, index); + + if (rename(filePath, path + newName) == -1) + EHS_LOG_INT("Error", 0, "Failed to rename file with error #" + Str_8::FromNum(errno) + "."); + } +} diff --git a/src/io/File_W32.cpp b/src/io/File_W32.cpp new file mode 100644 index 0000000..275293d --- /dev/null +++ b/src/io/File_W32.cpp @@ -0,0 +1,339 @@ +#include "io/File_W32.h" + +namespace ehs +{ + File::~File() + { + if (view && !UnmapViewOfFile(view)) + EHS_LOG_INT("Error", 0, "Failed to unmap view with error #" + Str_8::FromNum(GetLastError()) + "."); + + if (map != INVALID_HANDLE_VALUE && !CloseHandle(map)) + EHS_LOG_INT("Error", 0, "Failed to unmap with error #" + Str_8::FromNum(GetLastError()) + "."); + + if (hdl != INVALID_HANDLE_VALUE && !CloseHandle(hdl)) + EHS_LOG_INT("Error", 0, "Failed to close file handle with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + File::File() + : hdl(INVALID_HANDLE_VALUE), map(INVALID_HANDLE_VALUE), view(nullptr), viewSize(0) + { + } + + File::File(const Str_8 &filePath, const Mode mode, const Disposition disposition) + : BaseFile(filePath, mode, disposition), hdl(INVALID_HANDLE_VALUE), map(INVALID_HANDLE_VALUE), view(nullptr), + viewSize(0) + { + DWORD winMode = 0; + switch (mode) + { + case Mode::READ: + winMode = GENERIC_READ; + break; + case Mode::WRITE: + winMode = GENERIC_WRITE; + break; + case Mode::READ_WRITE: + winMode = GENERIC_READ | GENERIC_WRITE; + break; + } + + DWORD winDisp = 0; + switch (disposition) + { + case Disposition::CREATE_PERSISTENT: + winDisp = CREATE_ALWAYS; + break; + case Disposition::CREATE: + winDisp = CREATE_NEW; + break; + case Disposition::OPEN_PERSISTENT: + winDisp = OPEN_ALWAYS; + break; + case Disposition::OPEN: + winDisp = OPEN_EXISTING; + break; + case Disposition::TRUNCATE: + winDisp = TRUNCATE_EXISTING; + break; + } + + hdl = CreateFileW(UTF::To_16(path), winMode, 0, nullptr, winDisp, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hdl == INVALID_HANDLE_VALUE) + { + DWORD code = GetLastError(); + if (code == ERROR_FILE_NOT_FOUND) + EHS_LOG_INT("Error", 1, "File not found at path, \"" + path + "\"."); + else if (code != ERROR_SUCCESS) + EHS_LOG_INT("Error", 2, "Failed to create handle for file, \"" + path + "\", with error #" + Str_8::FromNum(code) + "."); + + return; + } + } + + File::File(File&& file) noexcept + : BaseFile(std::move(file)), hdl(file.hdl), map(file.map), view(file.view), viewSize(file.viewSize) + { + file.hdl = INVALID_HANDLE_VALUE; + file.map = INVALID_HANDLE_VALUE; + file.view = nullptr; + file.viewSize = 0; + } + + File::File(const File& file) + : BaseFile(file), hdl(INVALID_HANDLE_VALUE), map(INVALID_HANDLE_VALUE), view(nullptr), viewSize(0) + { + } + + File& File::operator=(File&& file) noexcept + { + if (this == &file) + return *this; + + BaseFile::operator=(std::move(file)); + + hdl = file.hdl; + map = file.map; + view = file.view; + viewSize = file.viewSize; + + file.hdl = INVALID_HANDLE_VALUE; + file.map = INVALID_HANDLE_VALUE; + file.view = nullptr; + file.viewSize = 0; + + return *this; + } + + File& File::operator=(const File& file) + { + if (this == &file) + return *this; + + BaseFile::operator=(file); + + hdl = INVALID_HANDLE_VALUE; + map = INVALID_HANDLE_VALUE; + view = nullptr; + viewSize = 0; + + return *this; + } + + File::operator const Byte*() const + { + return view; + } + + File::operator Byte*() + { + return view; + } + + void File::Release() + { + if (view && !UnmapViewOfFile(view)) + EHS_LOG_INT("Error", 0, "Failed to unmap view with error #" + Str_8::FromNum(GetLastError()) + "."); + view = nullptr; + viewSize = 0; + + if (IsMapped() && !CloseHandle(map)) + EHS_LOG_INT("Error", 0, "Failed to unmap with error #" + Str_8::FromNum(GetLastError()) + "."); + map = INVALID_HANDLE_VALUE; + + if (IsValid() && !CloseHandle(hdl)) + EHS_LOG_INT("Error", 0, "Failed to close file handle with error #" + Str_8::FromNum(GetLastError()) + "."); + hdl = INVALID_HANDLE_VALUE; + } + + bool File::IsMapped() const + { + return map != INVALID_HANDLE_VALUE && view; + } + + UInt_64 File::MapSize() const + { + return viewSize; + } + + void File::Map(const UInt_64 offset, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return; + + DWORD winMode = 0; + switch (mode) + { + case Mode::READ: + winMode = PAGE_READONLY; + break; + case Mode::WRITE: + winMode = PAGE_READWRITE; + break; + case Mode::READ_WRITE: + winMode = PAGE_READWRITE; + break; + } + + map = CreateFileMappingW(hdl, nullptr, winMode, 0, 0, nullptr); + if (!map) + { + EHS_LOG_INT("Error", 0, "Failed to create map handle with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + switch (mode) + { + case Mode::READ: + winMode = FILE_MAP_READ; + break; + case Mode::WRITE: + winMode = FILE_MAP_WRITE; + break; + case Mode::READ_WRITE: + winMode = FILE_MAP_ALL_ACCESS; + break; + } + + view = (Byte*)MapViewOfFile(map, winMode, ((DWORD*)&offset)[1], (DWORD)offset, size); + if (!view) + { + EHS_LOG_INT("Error", 0, "Failed to map view with error #" + Str_8::FromNum(GetLastError()) + "."); + + if (!CloseHandle(map)) + EHS_LOG_INT("Error", 0, "Failed to unmap with error #" + Str_8::FromNum(GetLastError()) + "."); + map = INVALID_HANDLE_VALUE; + } + + viewSize = size; + } + + void File::Unmap() + { + if (!IsValid() || !IsMapped()) + return; + + if (!UnmapViewOfFile(view)) + EHS_LOG_INT("Error", 0, "Failed to unmap view with error #" + Str_8::FromNum(GetLastError()) + "."); + view = nullptr; + viewSize = 0; + + if (!CloseHandle(map)) + EHS_LOG_INT("Error", 0, "Failed to unmap with error #" + Str_8::FromNum(GetLastError()) + "."); + map = INVALID_HANDLE_VALUE; + } + + void File::FlushMap() + { + if (!IsValid() || !IsMapped()) + return; + + if (!FlushViewOfFile(view, viewSize)) + EHS_LOG_INT("Error", 0, "Failed to flush view with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + UInt_64 File::Write(const Byte *const data, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return 0; + + SInt_64 written = 0; + + if (!WriteFile(hdl, data, (DWORD)size, (DWORD*)&written, nullptr)) + EHS_LOG_INT("Error", 0, "Failed to write to file, \"" + path + "\", with error #" + Str_8::FromNum(GetLastError()) + "."); + + return (UInt_64)written; + } + + UInt_64 File::Read(Byte *const data, const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return 0; + + SInt_64 read = 0; + + if (!ReadFile(hdl, data, (DWORD)size, (DWORD*)&read, nullptr)) + EHS_LOG_INT("Error", 0, "Failed to read from file, \"" + path + "\", with error #" + Str_8::FromNum(GetLastError()) + "."); + + return (UInt_64)read; + } + + void File::Seek(UInt_64 index) + { + if (!IsValid() || IsMapped()) + return; + + if (SetFilePointer(hdl, (LONG)index, (PLONG)&((UInt_32*)&index)[1], FILE_BEGIN) == INVALID_SET_FILE_POINTER) + EHS_LOG_INT("Error", 0, "Failed to seek with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void File::SeekBeginning() + { + if (!IsValid() || IsMapped()) + return; + + if (SetFilePointer(hdl, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + EHS_LOG_INT("Error", 0, "Failed to seek with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void File::SeekEnd() + { + if (!IsValid() || IsMapped()) + return; + + if (SetFilePointer(hdl, 0, nullptr, FILE_END) == INVALID_SET_FILE_POINTER) + EHS_LOG_INT("Error", 0, "Failed to seek with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void File::Truncate(const UInt_64 size) + { + if (!IsValid() || IsMapped()) + return; + + Seek(size); + + if (!::SetEndOfFile(hdl)) + EHS_LOG_INT("Error", 0, "Failed to set end of file with error #" + Str_8::FromNum(GetLastError()) + "."); + + SeekBeginning(); + } + + UInt_64 File::Size() const + { + if (!IsValid()) + return 0; + + LARGE_INTEGER size = {}; + + if (!GetFileSizeEx(hdl, &size)) + EHS_LOG_INT("Error", 0, "Failed to retrieve file size with error #" + Str_8::FromNum(GetLastError()) + "."); + + return (UInt_64)size.QuadPart; + } + + bool File::IsValid() const + { + return hdl != INVALID_HANDLE_VALUE; + } + + void File::Rename_32(const Str_32& filePath, const Str_32& newName) + { + Rename_16(UTF::To_16(filePath), UTF::To_16(newName)); + } + + void File::Rename_16(const Str_16& filePath, const Str_16& newName) + { + UInt_64 index = 0; + Str_16 path; + + if (filePath.Find(L"/", &index, SearchPattern::RIGHT_LEFT) || filePath.Find(L"\\", &index, SearchPattern::RIGHT_LEFT)) + path = filePath.Sub(0, index); + + if (!MoveFileW(filePath, path + newName)) + EHS_LOG_INT("Error", 0, "Failed to rename file with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void File::Rename_8(const Str_8& filePath, const Str_8& newName) + { + Rename_16(UTF::To_16(filePath), UTF::To_16(newName)); + } +} diff --git a/src/io/FontAtlas.cpp b/src/io/FontAtlas.cpp new file mode 100644 index 0000000..a006829 --- /dev/null +++ b/src/io/FontAtlas.cpp @@ -0,0 +1,304 @@ +#include "ehs/io/FontAtlas.h" +#include "ehs/io/File.h" +#include "ehs/Serializer.h" + +namespace ehs +{ + FontAtlas::~FontAtlas() + { + delete[] atlas; + } + + FontAtlas::FontAtlas() + : hashId(0), glyphScale(0), size(0), atlas(nullptr) + { + } + + FontAtlas::FontAtlas(const Str_8& filePath) + { + File fontFile(filePath, Mode::READ, Disposition::OPEN); + + hashId = fontFile.GetName().Hash_64(); + id = fontFile.GetName(); + + Serializer fData = fontFile.ReadSerializer_64(Endianness::LE, fontFile.Size()); + fontFile.Release(); + + Version ver = fData.ReadVersion(); + if (ver != Version(1, 0, 0)) + { + EHS_LOG_INT("Error", 2, "The Event Horizon Font file, \"" + filePath + "\", must be version 1.0.0, but was version " + + Str_8::FromNum(ver.major) + "." + Str_8::FromNum(ver.minor) + "." + + Str_8::FromNum(ver.patch) + "."); + return; + } + + glyphScale = fData.Read(); + + glyphs.Resize(fData.Read()); + for (UInt_64 i = 0; i < glyphs.Size(); ++i) + glyphs[i] = Glyph(fData); + + resolution = fData.ReadVec2(); + size = resolution.x * resolution.y; + atlas = new Byte[size]; + fData.ReadArray(atlas, &size); + } + + FontAtlas::FontAtlas(FontAtlas&& fa) noexcept + : BaseObj(std::move(fa)), hashId(fa.hashId), id((Str_8&&)fa.id), glyphScale(fa.glyphScale), + glyphs((Array&&)fa.glyphs), resolution(fa.resolution), size(fa.size), atlas(fa.atlas) + { + fa.hashId = 0; + fa.glyphScale = 0; + fa.resolution = {}; + fa.size = 0; + fa.atlas = nullptr; + } + + FontAtlas::FontAtlas(const FontAtlas& fa) + : BaseObj(fa), hashId(fa.hashId), id(fa.id), glyphScale(fa.glyphScale), glyphs(fa.glyphs), + resolution(fa.resolution), size(fa.size), atlas(new Byte[fa.size]) + { + Util::Copy(atlas, fa.atlas, fa.size); + } + + FontAtlas& FontAtlas::operator=(FontAtlas&& fa) noexcept + { + if (this == &fa) + return *this; + + BaseObj::operator=(std::move(fa)); + + hashId = fa.hashId; + id = (Str_8&&)fa.id; + glyphScale = fa.glyphScale; + glyphs = (Array&&)fa.glyphs; + resolution = fa.resolution; + size = fa.size; + delete[] atlas; + atlas = fa.atlas; + + fa.hashId = 0; + fa.glyphScale = 0; + fa.resolution = {}; + fa.size = 0; + fa.atlas = nullptr; + + return *this; + } + + FontAtlas& FontAtlas::operator=(const FontAtlas& fa) + { + if (this == &fa) + return *this; + + BaseObj::operator=(fa); + + hashId = fa.hashId; + id = fa.id; + glyphScale = fa.glyphScale; + glyphs = fa.glyphs; + resolution = {}; + size = fa.size; + delete[] atlas; + atlas = new Byte[fa.size]; + + Util::Copy(atlas, fa.atlas, fa.size); + + return *this; + } + + FontAtlas::operator Byte *() const + { + return atlas; + } + + void FontAtlas::Release() + { + hashId = 0; + id = {}; + glyphScale = 0; + glyphs.Clear(); + resolution = {0, 0}; + size = 0; + delete[] atlas; + atlas = nullptr; + } + + UInt_64 FontAtlas::GetHashId() const + { + return hashId; + } + + Str_8 FontAtlas::GetId() const + { + return id; + } + + UInt_64 FontAtlas::GetGlyphScale() const + { + return glyphScale; + } + + const Array& FontAtlas::GetGlyphs() const + { + return glyphs; + } + + Glyph FontAtlas::GetGlyph(const Char_32 code) const + { + for (UInt_32 i = 0; i < glyphs.Size(); ++i) + if (glyphs[i].GetCode() == code) + return glyphs[i]; + + return glyphs[0]; + } + + Vec2_u64 FontAtlas::GetResolution() const + { + return resolution; + } + + UInt_64 FontAtlas::GetSize() const + { + return size; + } + + bool FontAtlas::IsValid() const + { + return size; + } + + Vec2_f FontAtlas::CalculateSize(const Str_8& text) const + { + Vec2_f result; + + for (UInt_64 i = 0; i < text.Size(); ++i) + { + Glyph glyph = GetGlyph(text[i]); + + result.x += (float)glyph.GetAdvance().x; + + if ((float)glyph.GetScale().y > result.y) + result.y = (float)glyph.GetScale().y; + } + + return result; + } + + float FontAtlas::CalculateWidth(const Str_8 &text) const + { + float result = 0.0f; + + for (UInt_64 i = 0; i < text.Size(); ++i) + { + Glyph glyph = GetGlyph(text[i]); + + result += (float)glyph.GetAdvance().x; + } + + return result; + } + + float FontAtlas::CalculateHeight(const Str_8& text) const + { + float result = 0.0f; + + for (UInt_64 i = 0; i < text.Size(); ++i) + { + Glyph glyph = GetGlyph(text[i]); + + if ((float)glyph.GetScale().y > result) + result = (float)glyph.GetScale().y; + } + + return result; + } + + UInt_64 FontAtlas::CalculateIndexAtPoint(const Str_8& text, const Vec2_f& point) const + { + float result = 0.0f; + + for (UInt_64 i = 0; i < text.Size(); ++i) + { + Glyph glyph = GetGlyph(text[i]); + + float temp = result + (float)glyph.GetAdvance().x; + if (point.x > temp) + result += (float)glyph.GetAdvance().x; + else if (point.x <= temp) + return i; + } + + return text.Size(); + } + + Mesh FontAtlas::Generate(const Anchor anchor, const Str_8& text) const + { + Vec2_f pos; + + switch (anchor) + { + case Anchor::BOTTOM_CENTER: + { + pos.x -= CalculateWidth(text) * 0.5f; + break; + } + case Anchor::TOP_LEFT: + { + pos.x -= CalculateWidth(text); + break; + } + case Anchor::TOP_CENTER: + { + pos.x -= CalculateWidth(text) * 0.5f; + break; + } + case Anchor::CENTER_RIGHT: + { + pos.y -= CalculateHeight(text); + break; + } + case Anchor::CENTER: + { + Vec4_f size = CalculateSize(text); + pos.x -= size.x * 0.5f; + pos.y -= size.y; + break; + } + } + + Array verts(text.Size() * 4); + Array indices(text.Size() * 6); + + for (UInt_64 i = 0; i < text.Size(); ++i) + { + Glyph glyph = GetGlyph(text[i]); + Vec2_f scale = glyph.GetScale(); + + float x = pos.x + (float)glyph.GetBearing().x; + float y = pos.y + (float)(GetGlyphScale() - glyph.GetBearing().y); + + UInt_32 vertsI = i * 4; + + verts[vertsI] = Vertex_f({x, y, 1.0f}, {}, glyph.GetUV().GetPos()); + verts[vertsI + 1] = Vertex_f({x, y + scale.y, 1.0f}, {}, Vec2_f(glyph.GetUV().x, glyph.GetUV().h)); + verts[vertsI + 2] = Vertex_f({x + scale.x, y, 1.0f}, {}, Vec2_f(glyph.GetUV().w, glyph.GetUV().y)); + verts[vertsI + 3] = Vertex_f({x + scale.x, y + scale.y, 1.0f}, {}, glyph.GetUV().GetScale()); + + UInt_32 indicesI = i * 6; + + indices[indicesI] = vertsI; + indices[indicesI + 1] = vertsI + 1; + indices[indicesI + 2] = vertsI + 2; + indices[indicesI + 3] = vertsI + 3; + indices[indicesI + 4] = vertsI + 2; + indices[indicesI + 5] = vertsI + 1; + + pos.x += (float)glyph.GetAdvance().x; + } + + return {id, (Array&&)verts, (Array&&)indices}; + } +} \ No newline at end of file diff --git a/src/io/Glyph.cpp b/src/io/Glyph.cpp new file mode 100644 index 0000000..a5f1fae --- /dev/null +++ b/src/io/Glyph.cpp @@ -0,0 +1,115 @@ +#include "ehs/io/Glyph.h" + +namespace ehs +{ + Glyph::Glyph() + : code(0) + { + } + + Glyph::Glyph(Serializer<>& ser) + : code(ser.Read()), pos(ser.ReadVec2()), scale(ser.ReadVec2()), + uv(ser.ReadRect()), bearing(ser.ReadVec2()), advance(ser.ReadVec2()) + { + } + + Glyph::Glyph(const Char_32 code) + : code(code) + { + } + + Glyph::Glyph(const Glyph& glyph) + : code(glyph.code), pos(glyph.pos), scale(glyph.scale), uv(glyph.uv), bearing(glyph.bearing), advance(glyph.advance) + { + } + + Glyph& Glyph::operator=(const Glyph& glyph) + { + if (this == &glyph) + return *this; + + code = glyph.code; + pos = glyph.pos; + scale = glyph.scale; + uv = glyph.uv; + bearing = glyph.bearing; + advance = glyph.advance; + + return *this; + } + + bool Glyph::operator==(const Glyph& glyph) const + { + return code == glyph.code; + } + + bool Glyph::operator!=(const Glyph& glyph) const + { + return code != glyph.code; + } + + Char_32 Glyph::GetCode() const + { + return code; + } + + void Glyph::SetPos(const Vec2_u64& newPos) + { + pos = newPos; + } + + Vec2_u64 Glyph::GetPos() const + { + return pos; + } + + void Glyph::SetScale(const Vec2_u64& newScale) + { + scale = newScale; + } + + Vec2_u64 Glyph::GetScale() const + { + return scale; + } + + void Glyph::SetUV(const Rect_f& newUV) + { + uv = newUV; + } + + Rect_f Glyph::GetUV() const + { + return uv; + } + + void Glyph::SetBearing(const Vec2_64& newBearing) + { + bearing = newBearing; + } + + Vec2_32 Glyph::GetBearing() const + { + return bearing; + } + + void Glyph::SetAdvance(const Vec2_64& newAdvance) + { + advance = newAdvance; + } + + Vec2_32 Glyph::GetAdvance() const + { + return advance; + } + + void Glyph::Serialize(Serializer<>& ser) const + { + ser.Write(code); + ser.WriteVec2(pos); + ser.WriteVec2(scale); + ser.WriteRect(uv); + ser.WriteVec2(bearing); + ser.WriteVec2(advance); + } +} \ No newline at end of file diff --git a/src/io/RIFF.cpp b/src/io/RIFF.cpp new file mode 100644 index 0000000..fbf85bf --- /dev/null +++ b/src/io/RIFF.cpp @@ -0,0 +1,95 @@ +#include "ehs/io/RIFF.h" +#include "ehs/io/File.h" + +namespace ehs +{ + RIFF::RIFF(const Str_8& filePath) + { + File file(filePath, Mode::READ, Disposition::OPEN); + Serializer data = file.ReadSerializer_64(Endianness::LE, file.Size()); + file.Release(); + + Str_8 riffId = data.ReadStr(4); + if (riffId != "RIFF") + { + EHS_LOG_INT("Error", 0, "File at file path, \"" + filePath + "\", is not a valid RIFF file."); + return; + } + + data.SetOffset(data.GetOffset() + 4); + + type = data.ReadStr(4); + + while (data.GetOffset() < data.Size()) + { + Str_8 id = data.ReadStr(4); + UInt_32 size = data.Read(); + + chunks.Push(RIFF_Chunk(id, Serializer(Endianness::LE, &data[data.GetOffset()], size))); + + data.SetOffset(data.GetOffset() + size); + } + } + + RIFF::RIFF(Serializer<>& data) + { + Str_8 riffId = data.ReadStr(4); + if (riffId != "RIFF") + { + EHS_LOG_INT("Error", 0, "Data is not in RIFF format."); + return; + } + + data.SetOffset(data.GetOffset() + 4); + + type = data.ReadStr(4); + + while (data.GetOffset() < data.Size()) + { + Str_8 id = data.ReadStr(4); + UInt_32 size = data.Read(); + + chunks.Push(RIFF_Chunk(id, Serializer(Endianness::LE, &data[data.GetOffset()], size))); + + data.SetOffset(data.GetOffset() + size); + } + } + + RIFF::operator const RIFF_Chunk*() const + { + return &chunks[0]; + } + + Str_8 RIFF::GetType() const + { + return type; + } + + bool RIFF::HasChunk(const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < chunks.Size(); ++i) + if (chunks[i].GetHashId() == hashId) + return true; + + return false; + } + + bool RIFF::HasChunk(const Str_8& id) const + { + return HasChunk(id.Hash_64()); + } + + RIFF_Chunk RIFF::GetChunk(const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < chunks.Size(); ++i) + if (chunks[i].GetHashId() == hashId) + return chunks[i]; + + return {}; + } + + RIFF_Chunk RIFF::GetChunk(const Str_8& id) const + { + return GetChunk(id.Hash_64()); + } +} \ No newline at end of file diff --git a/src/io/RIFF_Chunk.cpp b/src/io/RIFF_Chunk.cpp new file mode 100644 index 0000000..fdced6e --- /dev/null +++ b/src/io/RIFF_Chunk.cpp @@ -0,0 +1,52 @@ +#include "ehs/io/RIFF_Chunk.h" + +namespace ehs +{ + RIFF_Chunk::RIFF_Chunk() + : hashId(0) + { + + } + + RIFF_Chunk::RIFF_Chunk(const Str_8& id, const Serializer<>& data) + : id(id), hashId(id.Hash_64()), data(data) + { + } + + Str_8 RIFF_Chunk::GetId() const + { + return id; + } + + UInt_64 RIFF_Chunk::GetHashId() const + { + return hashId; + } + + Serializer<> RIFF_Chunk::GetData() const + { + return data; + } + + bool RIFF_Chunk::IsValid() const + { + return hashId; + } + + RIFF_Chunk::RIFF_Chunk(const RIFF_Chunk& chunk) + : id(chunk.id), hashId(chunk.hashId), data(chunk.data) + { + } + + RIFF_Chunk& RIFF_Chunk::operator=(const RIFF_Chunk& chunk) + { + if (this == &chunk) + return *this; + + id = chunk.id; + hashId = chunk.hashId; + data = chunk.data; + + return *this; + } +} \ No newline at end of file diff --git a/src/io/Resource.cpp b/src/io/Resource.cpp new file mode 100644 index 0000000..e4f1545 --- /dev/null +++ b/src/io/Resource.cpp @@ -0,0 +1,95 @@ +#include "ehs/io/Resource.h" + +namespace ehs +{ + Resource::Resource() + : hashId(0) + { + AddType("Resource"); + } + + Resource::Resource(ehs::Str_8 id) + : hashId(id.Hash_64()), id(std::move(id)) + { + AddType("Resource"); + } + + Resource::Resource(Resource&& rsrc) noexcept + : BaseObj((BaseObj&&)rsrc), hashId(rsrc.hashId), id(std::move(rsrc.id)) + { + rsrc.hashId = 0; + } + + Resource::Resource(const Resource& rsrc) + : BaseObj(rsrc), hashId(rsrc.hashId), id(rsrc.id) + { + } + + Resource& Resource::operator=(Resource&& rsrc) noexcept + { + if (this == &rsrc) + return *this; + + BaseObj::operator=((BaseObj&&)rsrc); + + hashId = rsrc.hashId; + id = std::move(rsrc.id); + + rsrc.hashId = 0; + + return *this; + } + + Resource& Resource::operator=(const Resource& rsrc) + { + if (this == &rsrc) + return *this; + + BaseObj::operator=(rsrc); + + hashId = rsrc.hashId; + id = rsrc.id; + + return *this; + } + + bool Resource::operator==(const ehs::UInt_64 otherHashId) const + { + return hashId == otherHashId; + } + + bool Resource::operator!=(const ehs::UInt_64 otherHashId) const + { + return hashId != otherHashId; + } + + void Resource::Release() + { + } + + void Resource::SetId(ehs::Str_8 newId) + { + hashId = newId.Hash_64(); + id = std::move(newId); + } + + ehs::UInt_64 Resource::GetHashId() const + { + return hashId; + } + + ehs::Str_8 Resource::GetId() const + { + return id; + } + + bool Resource::IsValid() const + { + return hashId; + } + + Resource* Resource::Clone() const + { + return new Resource(*this); + } +} \ No newline at end of file diff --git a/src/io/Window_W32.cpp b/src/io/Window_W32.cpp new file mode 100644 index 0000000..e4838fb --- /dev/null +++ b/src/io/Window_W32.cpp @@ -0,0 +1,720 @@ +#include "io/Window_W32.h" +#include "io/hid/Keyboard.h" +#include "io/hid/Mouse.h" +#include "system/Thread.h" + +#include + +namespace ehs +{ + Array Window::windows; + + LRESULT Window::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + Window* win = nullptr; + for (UInt_64 i = 0; i < windows.Size(); ++i) + { + if (hWnd == windows[i]->hdl) + win = windows[i]; + } + + if (uMsg == WM_DESTROY) + { + PostQuitMessage(0); + return 0; + } + + if (!win) + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + + if (uMsg == WM_CLOSE) + { + for (UInt_64 i = 0; i < windows.Size(); ++i) + { + if (windows[i]->hdl != hWnd) + continue; + + if (windows.Size() > 1) + windows.Swap(i, windows.Size() - 1); + + windows.Pop(); + + break; + } + + DestroyWindow(hWnd); + win->hdl = nullptr; + win->Close(); + + return 0; + + //return DefWindowProcW(hWnd, WM_QUIT, 0, lParam); + } + else if (uMsg == WM_SIZE) + { + Vec2 newSize; + newSize[0] = static_cast(lParam); + newSize[1] = static_cast(lParam >> 16); + + win->OnResized(newSize); + } + else if (uMsg == WM_HIDE) + { + ShowWindow(hWnd, SW_HIDE); + } + else if (uMsg == WM_SHOW) + { + ShowWindow(hWnd, SW_SHOW); + } + else if (uMsg == WM_HIDE_CURSOR) + { + while (::ShowCursor(false) >= 0); + } + else if (uMsg == WM_SHOW_CURSOR) + { + while (::ShowCursor(true) < 0); + } + else if (uMsg == WM_PAINT) + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + + FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW)); + + EndPaint(hWnd, &ps); + } + else if (win->HasFocus() && uMsg == WM_INPUT) + { + UINT dwSize = 0; + + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER)); + + if (dwSize) + { + Byte* data = new Byte[dwSize]; + + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) + { + EHS_LOG_INT("Error", 0, "Getting raw input returned incorrect size."); + delete[] data; + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + RAWINPUT* raw = (RAWINPUT*)data; + + if (raw->header.dwType == RIM_TYPEKEYBOARD) + { + Keyboard* keyboard = (Keyboard*)win->ih.GetDevice((UInt_64)raw->header.hDevice); + if (!keyboard) + { + UInt_32 bufferSize; + GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, NULL, &bufferSize); + Char_16* deviceName = new Char_16[bufferSize]; + + if (GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, deviceName, &bufferSize) < 0) + { + EHS_LOG_INT("Error, 0, "Failed to retrieve device name."); + return; + } + + keyboard = new Keyboard(UTF::To_8(deviceName, bufferSize), (UInt_64)raw->header.hDevice); + win->ih.AddDevice(keyboard); + } + + const Button button = Keyboard::TranslateScanCode(raw->data.keyboard.MakeCode); + + if (raw->data.keyboard.Message == WM_KEYDOWN || raw->data.keyboard.Message == WM_SYSKEYDOWN) + keyboard->ButtonDown(button); + else if (raw->data.keyboard.Message == WM_KEYUP || raw->data.keyboard.Message == WM_SYSKEYUP) + keyboard->ButtonUp(button); + } + else if (raw->header.dwType == RIM_TYPEMOUSE) + { + Mouse* mouse = (Mouse*)win->ih.GetDevice((UInt_64)raw->header.hDevice); + if (!mouse) + { + UInt_32 bufferSize; + GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, NULL, &bufferSize); + Char_16* deviceName = new Char_16[bufferSize]; + + if (GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME, deviceName, &bufferSize) < 0) + { + EHS_LOG_INT("Error, 1, "Failed to retrieve device name."); + return; + } + + mouse = new Mouse(UTF::To_8(deviceName, bufferSize), (UInt_64)raw->header.hDevice); + win->ih.AddDevice(mouse); + } + + mouse->SetDelta({raw->data.mouse.lLastX, raw->data.mouse.lLastY}); + + UInt_16 code = raw->data.mouse.usButtonFlags; + + if (code == RI_MOUSE_LEFT_BUTTON_DOWN) + mouse->ButtonDown(Mouse::LMB); + else if (code == RI_MOUSE_LEFT_BUTTON_UP) + mouse->ButtonUp(Mouse::LMB); + else if (code == RI_MOUSE_RIGHT_BUTTON_DOWN) + mouse->ButtonDown(Mouse::RMB); + else if (code == RI_MOUSE_RIGHT_BUTTON_UP) + mouse->ButtonUp(Mouse::RMB); + else if (code == RI_MOUSE_MIDDLE_BUTTON_DOWN) + mouse->ButtonDown(Mouse::MMB); + else if (code == RI_MOUSE_MIDDLE_BUTTON_UP) + mouse->ButtonUp(Mouse::MMB); + else if (code == RI_MOUSE_BUTTON_4_DOWN) + mouse->ButtonDown(Mouse::Four); + else if (code == RI_MOUSE_BUTTON_4_UP) + mouse->ButtonUp(Mouse::Four); + else if (code == RI_MOUSE_BUTTON_5_DOWN) + mouse->ButtonDown(Mouse::Five); + else if (code == RI_MOUSE_BUTTON_5_UP) + mouse->ButtonUp(Mouse::Five); + else if (code == RI_MOUSE_WHEEL) + mouse->SetScroll({mouse->GetScroll().x, (SInt_8)raw->data.mouse.usButtonData}); + else if (code == RI_MOUSE_HWHEEL) + mouse->SetScroll({(SInt_8)raw->data.mouse.usButtonData, mouse->GetScroll().y}); + } + + delete[] data; + } + } + else if (uMsg == WM_KILLFOCUS) + { + win->focused = false; + + if (!win->cursorVisible) + { + win->SendMsg(WM_SHOW_CURSOR, 0, 0); + } + + if (win->cursorConstrained) + { + if (!ClipCursor(nullptr)) + EHS_LOG_INT("Error", 0, "Failed to free cursor after losing focus with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + win->ih.ResetAllStates(); + } + else if (uMsg == WM_SETFOCUS) + { + win->focused = true; + + if (!win->cursorVisible) + win->SendMsg(WM_HIDE_CURSOR, 0, 0); + + if (win->cursorConstrained) + { + RECT client = {}; + + if (!GetClientRect(win->GetHdl(), &client)) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve client scale with error #" + Str_8::FromNum(GetLastError()) + "."); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + POINT pos = {}; + + if (!ClientToScreen(win->GetHdl(), &pos)) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve client's absolute position with error #" + Str_8::FromNum(GetLastError()) + "."); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + client.left = pos.x; + client.top = pos.y; + + POINT scale = {client.right, client.bottom}; + + if (!ClientToScreen(win->GetHdl(), &scale)) + { + EHS_LOG_INT("Error", 2, "Failed to retrieve client's absolute scale with error #" + Str_8::FromNum(GetLastError()) + "."); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + client.right = scale.x; + client.bottom = scale.y; + + if (!ClipCursor(&client)) + { + EHS_LOG_INT("Error", 3, "Failed to confine cursor with error #" + Str_8::FromNum(GetLastError()) + "."); + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + } + } + else if (uMsg == WM_MOUSEMOVE) + { + win->cursorPos.x = ((SInt_16*)&lParam)[0]; + win->cursorPos.y = ((SInt_16*)&lParam)[1]; + } + + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + Window::~Window() + { + if (!created) + return; + + SendMsg(WM_CLOSE, 0, 0); + } + + Window::Window() + : owner(0), instance(nullptr), hdl(nullptr) + { + } + + Window::Window(const Window& win) + : BaseWindow(win), owner(0), instance(nullptr), hdl(nullptr) + { + } + + Window& Window::operator=(const Window& win) + { + if (&win == this) + return *this; + + BaseWindow::operator=(win); + + owner = 0; + created = false; + focused = false; + instance = nullptr; + hdl = nullptr; + + return* this; + } + + bool Window::Poll() + { + ih.Poll(); + + MSG msg = {}; + + while (PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE)) + { + if (GetMessageW(&msg, nullptr, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + else + return false; + } + + BaseWindow::Poll(); + + return true; + } + + void Window::Create_32(const Str_32& title, const Vec2_s32& pos, const Vec2_u32 scale) + { + Create_16(UTF::To_16(title), pos, scale); + } + + void Window::Create_16(const Str_16& title, const Vec2_s32& pos, const Vec2_u32 scale) + { + if (created) + return; + + owner = Thread::GetCurrentId(); + + instance = GetModuleHandleW(nullptr); + + WNDCLASSEXW wcex = {}; + wcex.cbSize = sizeof(wcex); + wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wcex.lpfnWndProc = &Window::WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = instance; + wcex.hIcon = LoadIcon(nullptr, IDI_APPLICATION); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hIconSm = LoadIcon(nullptr, IDI_WINLOGO); + wcex.hbrBackground = nullptr; + wcex.lpszMenuName = nullptr; + wcex.lpszClassName = title; + + if (!RegisterClassExW(&wcex)) + EHS_LOG_INT("Error", 0, "Failed to register window."); + + hdl = CreateWindowExW( + 0, + title, + title, + WS_OVERLAPPEDWINDOW, + pos.x, pos.y, scale.x, scale.y, + nullptr, nullptr, + instance, + nullptr + ); + + if (!hdl) + { + EHS_LOG_INT("Error", 1, "Failed to create window."); + return; + } + + RECT tmp = { + 0, + 0, + static_cast(scale.x), + static_cast(scale.y) + }; + + AdjustWindowRectEx(&tmp, WS_OVERLAPPEDWINDOW, false, 0); + + SetWindowPos(hdl, nullptr, 0, 0, tmp.right - tmp.left, tmp.bottom - tmp.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + + RAWINPUTDEVICE rid[2]; + + rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + rid[0].usUsage = HID_USAGE_GENERIC_MOUSE; + rid[0].dwFlags = RIDEV_INPUTSINK; + rid[0].hwndTarget = hdl; + + rid[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + rid[1].usUsage = HID_USAGE_GENERIC_KEYBOARD; + rid[1].dwFlags = 0; + rid[1].hwndTarget = hdl; + + if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == false) + { + EHS_LOG_INT("Error", 2, "Failed to register raw input devices."); + + return; + } + + windows.Push(this); + + created = true; + + OnCreated(); + } + + void Window::Create_8(const Str_8& title, const Vec2_s32& pos, const Vec2_u32 scale) + { + Create_16(UTF::To_16(title), pos, scale); + } + + void Window::Use(HWND windowHdl) + { + hdl = windowHdl; + } + + void Window::Close() + { + if (hdl) + SendMsg(WM_CLOSE, 0, 0); + + created = false; + } + + void Window::Show() + { + SendMsg(WM_SHOW, 0, 0); + } + + void Window::Hide() + { + SendMsg(WM_HIDE, 0, 0); + } + + void Window::SetTitle_32(const Str_32& title) + { + if (!SetWindowTextW(hdl, UTF::To_16(title))) + EHS_LOG_INT("Error", 0, "Failed to set window title with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + Str_32 Window::GetTitle_32() + { + int size = GetWindowTextLengthW(hdl); + if (!size) + { + DWORD err = GetLastError(); + if (err) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); + + return {}; + } + } + + Char_16* buffer = new Char_16[size]; + + size = GetWindowTextW(hdl, buffer, size); + if (!size) + { + DWORD err = GetLastError(); + if (err) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); + + return {}; + } + } + + return UTF::To_32(buffer, Str_16::Len(buffer)); + } + + void Window::SetTitle_16(const Str_16& title) + { + if (!SetWindowTextW(hdl, title)) + EHS_LOG_INT("Error", 0, "Failed to set window title with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + Str_16 Window::GetTitle_16() + { + int size = GetWindowTextLengthW(hdl); + if (!size) + { + DWORD err = GetLastError(); + if (err) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); + + return {}; + } + } + + Char_16* buffer = new Char_16[size]; + + size = GetWindowTextW(hdl, buffer, size); + if (!size) + { + DWORD err = GetLastError(); + if (err) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); + + return {}; + } + } + + return {buffer}; + } + + void Window::SetTitle_8(const Str_8& title) + { + if (!SetWindowTextW(hdl, UTF::To_16(title))) + EHS_LOG_INT("Error", 0, "Failed to set window title with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + Str_8 Window::GetTitle_8() + { + int size = GetWindowTextLengthW(hdl); + if (!size) + { + DWORD err = GetLastError(); + if (err) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); + + return {}; + } + } + + Char_16* buffer = new Char_16[size]; + + size = GetWindowTextW(hdl, buffer, size); + if (!size) + { + DWORD err = GetLastError(); + if (err) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve the window's title length with error #" + Str_8::FromNum(err) + "."); + + return {}; + } + } + + return UTF::To_8(buffer, Str_16::Len(buffer)); + } + + void Window::SetIcon(const Str_8& filePath) + { + Handle icon = LoadImageW(nullptr, UTF::To_16(filePath), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE); + if (!icon) + { + EHS_LOG_INT("Error", 0, "Failed to load icon at file path, \"" + filePath + "\" with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + SendMsg(WM_SETICON, ICON_SMALL, (LPARAM)icon); + SendMsg(WM_SETICON, ICON_BIG, (LPARAM)icon); + } + + HWND Window::GetHdl() const + { + return hdl; + } + + HWND Window::GetAvailableHdl() + { + for (UInt_64 i = 0; i < windows.Size(); ++i) + if (windows[i]) + return windows[i]->hdl; + + return nullptr; + } + + HINSTANCE Window::GetInst() const + { + return instance; + } + + void Window::ToggleEnabled(bool toggle) + { + if (!created) + return; + + EnableWindow(hdl, toggle); + } + + bool Window::IsEnabled() + { + if (!created) + return false; + + return IsWindowEnabled(hdl); + } + + void Window::SetPos(int x, int y) + { + if (!created) + return; + + SetWindowPos(hdl, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + } + + Vec2 Window::GetPos() + { + if (!created) + return {}; + + RECT tmp = {}; + GetWindowRect(hdl, &tmp); + + return {(Int_32)tmp.left, (Int_32)tmp.top}; + } + + void Window::SetClientSize(const Vec2& size) + { + if (!created) + return; + + RECT rect = { + 0, + 0, + static_cast(size[0]), + static_cast(size[1]) + }; + + DWORD exStyle = (DWORD)GetWindowLongPtr(hdl, GWL_EXSTYLE); + DWORD style = (DWORD)GetWindowLongPtr(hdl, GWL_STYLE); + + AdjustWindowRectEx(&rect, style, false, exStyle); + + SetWindowPos(hdl, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + } + + Vec2 Window::GetClientSize() + { + RECT rect = {}; + + if (!GetClientRect(hdl, &rect)) + EHS_LOG_INT("Error", 0, "Failed to retrieve client size with error #" + Str_8::FromNum(GetLastError()) + "."); + + return {(UInt_32)rect.right, (UInt_32)rect.bottom}; + } + + void Window::OnResized(const Vec2& newSize) + { + } + + void Window::SetSize(int w, int h) + { + if (!created) + return; + + SetWindowPos(hdl, nullptr, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + } + + Vec2 Window::GetSize() + { + if (!created) + return {}; + + RECT tmp = {}; + GetWindowRect(hdl, &tmp); + + return {(Int_32)(tmp.right - tmp.left), (Int_32)(tmp.bottom - tmp.top)}; + } + + void Window::ShowCursor(bool toggle) + { + SendMsg(toggle ? WM_SHOW_CURSOR : WM_HIDE_CURSOR, 0, 0); + + cursorVisible = toggle; + } + + void Window::ConstrainCursor(const bool toggle) + { + if (toggle) + { + RECT client = {}; + + if (!GetClientRect(GetHdl(), &client)) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve client scale with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + POINT pos = {}; + + if (!ClientToScreen(GetHdl(), &pos)) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve client's absolute position with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + client.left = pos.x; + client.top = pos.y; + + POINT scale = {client.right, client.bottom}; + + if (!ClientToScreen(GetHdl(), &scale)) + { + EHS_LOG_INT("Error", 2, "Failed to retrieve client's absolute scale with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + client.right = scale.x; + client.bottom = scale.y; + + if (!ClipCursor(&client)) + { + EHS_LOG_INT("Error", 3, "Failed to confine cursor with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + } + else + { + ClipCursor(nullptr); + } + + cursorConstrained = toggle; + } + + void Window::SendMsg(const UINT msg, const WPARAM wParam, const LPARAM lParam) + { + if (!hdl) + return; + + if (Thread::GetCurrentId() == owner) + PostMessageW(hdl, msg, wParam, lParam); + else + SendMessageW(hdl, msg, wParam, lParam); + } +} \ No newline at end of file diff --git a/src/io/Window_Way.cpp b/src/io/Window_Way.cpp new file mode 100644 index 0000000..b07d3e5 --- /dev/null +++ b/src/io/Window_Way.cpp @@ -0,0 +1,224 @@ +#include "io/Window_Way.h" + +namespace ehs +{ + void Window::SurfaceConfigure(void* data, xdg_surface* xdg_surface, UInt_32 serial) + { + xdg_surface_ack_configure(xdg_surface, serial); + } + + void Window::ShellPing(void* data, xdg_wm_base* shell, UInt_32 serial) + { + xdg_wm_base_pong(shell, serial); + } + + void Window::RegistryHandler(void* data, wl_registry* registry, UInt_32 id, const char* interface, UInt_32 version) + { + Serializer* ser = (Serializer*)data; + + if (Str_8::Cmp(interface, "wl_compositor")) + { + ser->SetOffset(0); + wl_compositor** comp = ser->Read(); + *comp = (wl_compositor*)wl_registry_bind(registry, id, &wl_compositor_interface, 1); + } + + if (Str_8::Cmp(interface, "xdg_wm_base")) + { + ser->SetOffset(sizeof(void*)); + xdg_wm_base** base = ser->Read(); + *base = (xdg_wm_base*)wl_registry_bind(registry, id, &xdg_wm_base_interface, 1); + } + } + + void Window::RegistryRemoved(void* data, wl_registry* registry, UInt_32 id) + { + } + + Window::~Window() + { + xdg_toplevel_destroy(xdgToplevel); + xdg_surface_destroy(xdgSurface); + xdg_wm_base_destroy(xdgShell); + wl_surface_destroy(surface); + wl_compositor_destroy(compositor); + wl_registry_destroy(registry); + wl_display_disconnect(display); + } + + Window::Window() + : display(nullptr), registry(nullptr), compositor(nullptr) , surface(nullptr) + { + } + + void Window::Create_32(const Str_32& title, const Vec2_s32& pos, const Vec2_u32 scale) + { + Create_8(UTF::To_8(title), pos, scale); + } + + void Window::Create_16(const Str_16& title, const Vec2_s32& pos, const Vec2_u32 scale) + { + Create_8(UTF::To_8(title), pos, scale); + } + + void Window::Create_8(const Str_8& title, const Vec2_s32& pos, const Vec2_u32 scale) + { + display = wl_display_connect(nullptr); + if (!display) + { + EHS_LOG_INT("Error", 0, "Failed to connect to display server."); + return; + } + + Serializer data(Endianness::LE); + data.Write(&compositor); + data.Write(&xdgShell); + + static constexpr wl_registry_listener registry_listener = { + RegistryHandler, + RegistryRemoved + }; + + registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, &data); + + wl_display_dispatch(display); + wl_display_roundtrip(display); + + if (!compositor || !xdgShell) + { + EHS_LOG_INT("Error", 1, "Can't find required interfaces."); + return; + } + + static constexpr xdg_wm_base_listener xdg_shell_listener = {ShellPing}; + + xdg_wm_base_add_listener(xdgShell, &xdg_shell_listener, nullptr); + + surface = wl_compositor_create_surface(compositor); + if (!surface) + { + EHS_LOG_INT("Error", 2, "Can't create surface."); + return; + } + + xdgSurface = xdg_wm_base_get_xdg_surface(xdgShell, surface); + xdgToplevel = xdg_surface_get_toplevel(xdgSurface); + + static constexpr xdg_surface_listener surfaceListener = {SurfaceConfigure}; + + xdg_surface_add_listener(xdgSurface, &surfaceListener, nullptr); + + xdg_toplevel_set_title(xdgToplevel, title); + wl_surface_commit(surface); + } + + void Window::OnCreated() + { + } + + void Window::Close() + { + xdg_toplevel_destroy(xdgToplevel); + xdgToplevel = nullptr; + + xdg_surface_destroy(xdgSurface); + xdgSurface = nullptr; + + xdg_wm_base_destroy(xdgShell); + xdgShell = nullptr; + + wl_surface_destroy(surface); + surface = nullptr; + + wl_compositor_destroy(compositor); + compositor = nullptr; + + wl_registry_destroy(registry); + registry = nullptr; + + wl_display_disconnect(display); + display = nullptr; + } + + void Window::Show() + { + } + + void Window::Hide() + { + } + + bool Window::Poll() + { + wl_display_dispatch(display); + + return true; + } + + void Window::ShowCursor(bool toggle) + { + } + + void Window::ConstrainCursor(const bool constrain) + { + } + + void Window::SetTitle_32(const Str_32& newTitle) + { + } + + Str_32 Window::GetTitle_32() const + { + return {}; + } + + void Window::SetTitle_16(const Str_16& newTitle) + { + } + + Str_16 Window::GetTitle_16() const + { + return {}; + } + + void Window::SetTitle_8(const Str_8& newTitle) + { + } + + Str_8 Window::GetTitle_8() const + { + return {}; + } + + void Window::SetPos(const Vec2_s32& newPos) + { + } + + Vec2_s32 Window::GetPos() const + { + return {}; + } + + void Window::SetScale(const Vec2_u32& newScale) + { + } + + Vec2_u32 Window::GetScale() const + { + return {}; + } + + Serializer Window::GetClipboard() + { + return {}; + } + + void Window::SetClipboard(Serializer data) + { + } + + void Window::SetCursorImg(const CursorImg img) + { + } +} diff --git a/src/io/Window_XCB.cpp b/src/io/Window_XCB.cpp new file mode 100644 index 0000000..52e8dff --- /dev/null +++ b/src/io/Window_XCB.cpp @@ -0,0 +1,735 @@ +#include "ehs/io/Window_XCB.h" +#include "ehs/UTF.h" +#include "ehs/Vec2.h" +#include "ehs/io/hid/Keyboard.h" +#include "ehs/io/hid/Mouse.h" +#include "ehs/io/Console.h" + +#include +#include +#include +#include + +namespace ehs +{ + Window::~Window() + { + if (hdl) + xcb_destroy_window(server, hdl); + + if (server) + xcb_disconnect(server); + } + + Window::Window() + : server(nullptr), screen(nullptr), hdl(0), masks{}, extOpCode(0), events{XCB_ATOM_NONE, XCB_ATOM_NONE} + { + } + + Window::Window(Window&& win) noexcept + : BaseWindow((BaseWindow&&)win), server(win.server), screen(win.screen), hdl(win.hdl), + masks{win.masks[0], win.masks[1]}, events((Vector)win.events), extOpCode(win.extOpCode), + clipboard((Serializer&&)win.clipboard) + { + win.server = nullptr; + win.screen = nullptr; + win.hdl = 0; + win.masks[0] = 0; + win.masks[1] = 0; + win.extOpCode = 0; + } + + Window::Window(const Window& win) + : BaseWindow(win), server(nullptr), screen(nullptr), hdl(0), masks{}, extOpCode(0), + events{XCB_ATOM_NONE, XCB_ATOM_NONE} + { + } + + Window& Window::operator=(Window&& win) noexcept + { + if (this == &win) + return *this; + + Close(); + + BaseWindow::operator=((BaseWindow&&)win); + + server = win.server; + screen = win.screen; + hdl = win.hdl; + masks[0] = win.masks[0]; + masks[1] = win.masks[1]; + extOpCode = win.extOpCode; + events = (Vector&&)win.events; + clipboard = (Serializer&&)win.clipboard; + + win.server = nullptr; + win.screen = nullptr; + win.hdl = 0; + win.masks[0] = 0; + win.masks[1] = 0; + win.extOpCode = 0; + + return *this; + } + + Window& Window::operator=(const Window& win) + { + if (this == &win) + return *this; + + Close(); + + BaseWindow::operator=(win); + + server = nullptr; + screen = nullptr; + hdl = 0; + masks[0] = 0; + masks[1] = 0; + extOpCode = 0; + events = {}; + clipboard = {}; + + return *this; + } + + void Window::Create_32(const Str_32 &title, const Vec2_s32 &pos, const Vec2_u32 scale) + { + Create_8(UTF::To_8(title), pos, scale); + } + + void Window::Create_16(const Str_16 &title, const Vec2_s32 &pos, const Vec2_u32 scale) + { + Create_8(UTF::To_8(title), pos, scale); + } + + void Window::Create_8(const Str_8 &title, const Vec2_s32 &pos, const Vec2_u32 scale) + { + if (created) + return; + + server = xcb_connect(nullptr, nullptr); + if (xcb_connection_has_error(server)) + { + EHS_LOG_INT("Error", 0, "Failed to connect to display server."); + return; + } + + screen = xcb_setup_roots_iterator(xcb_get_setup(server)).data; + + hdl = xcb_generate_id(server); + + UInt_32 values[2] = { + screen->white_pixel, + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_FOCUS_CHANGE + }; + + xcb_create_window( + server, + screen->root_depth, + hdl, + screen->root, + (SInt_16)pos.x, (SInt_16)pos.y, + scale.x, scale.y, 1, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, + values + ); + + SetTitle_8(title); + + xcb_atom_t proto = RetrieveAtom(false, "WM_PROTOCOLS"); + masks[0] = RetrieveAtom(true, "WM_DELETE_WINDOW"); + masks[1] = RetrieveAtom(true, "_NET_WM_PING"); + + xcb_change_property(server, XCB_PROP_MODE_REPLACE, hdl, proto, XCB_ATOM_ATOM, 32, 2, masks); + + const xcb_query_extension_reply_t *extension = xcb_get_extension_data(server, &xcb_input_id); + if (!extension) + { + xcb_disconnect(server); + + EHS_LOG_INT("Warning", 1, "Failed to query for XCB XInput extension."); + return; + } + + if (!extension->present) + { + xcb_disconnect(server); + + EHS_LOG_INT("Warning", 2, "XCB XInput extension is not available."); + return; + } + + struct XInputMask + { + xcb_input_event_mask_t iem; + UInt_32 flags; + }; + + XInputMask rootMask = {}; + rootMask.iem.deviceid = XCB_INPUT_DEVICE_ALL; + rootMask.iem.mask_len = 1; + rootMask.flags = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION | XCB_INPUT_XI_EVENT_MASK_RAW_KEY_PRESS | + XCB_INPUT_XI_EVENT_MASK_RAW_KEY_RELEASE | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | + XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE; + + xcb_input_xi_select_events(server, screen->root, 1, &rootMask.iem); + + XInputMask winMask = {}; + winMask.iem.deviceid = XCB_INPUT_DEVICE_ALL; + winMask.iem.mask_len = 1; + winMask.flags = XCB_INPUT_XI_EVENT_MASK_MOTION; + + xcb_input_xi_select_events(server, hdl, 1, &winMask.iem); + + QueryPrimaryDevices(); + + xcb_map_window(server, hdl); + + xcb_flush(server); + + created = true; + + OnCreated(); + } + + void Window::Close() + { + if (hdl) + { + xcb_destroy_window(server, hdl); + hdl = 0; + } + + if (server) + { + xcb_disconnect(server); + server = nullptr; + } + + created = false; + } + + void Window::Show() + { + if (!IsCreated()) + return; + + xcb_map_window(server, hdl); + xcb_flush(server); + } + + void Window::Hide() + { + if (!IsCreated()) + return; + + xcb_unmap_window(server, hdl); + xcb_flush(server); + } + + bool Window::Poll() + { + ih.Poll(); + + xcb_generic_event_t* event = nullptr; + while ((event = RetrieveEvent())) + { + switch (event->response_type & ~0x80) + { + case XCB_CLIENT_MESSAGE: + { + xcb_client_message_event_t* cEvent = (xcb_client_message_event_t*)event; + if (cEvent->data.data32[0] == masks[0]) // WM_DELETE_WINDOW + { + xcb_disconnect(server); + + server = nullptr; + screen = nullptr; + hdl = 0; + + free(event); + + return false; + } + else if (cEvent->data.data32[0] == masks[1]) // _NET_WM_PING + { + cEvent->window = screen->root; + xcb_send_event(server, true, hdl, + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (const char*) &cEvent); + } + + break; + } + case XCB_FOCUS_IN: + { + focused = true; + break; + } + case XCB_FOCUS_OUT: + { + ih.ResetAllStates(); + focused = false; + break; + } + case XCB_SELECTION_REQUEST: + { + const xcb_selection_request_event_t* req_event = (xcb_selection_request_event_t*)event; + + // Change the property of the requestor window with the data + xcb_change_property(server, XCB_PROP_MODE_REPLACE, req_event->requestor, req_event->property, + XCB_ATOM_STRING, 8, clipboard.Size(), clipboard); + + // Send a SelectionNotify event to the requestor + xcb_selection_notify_event_t notify_event = {}; + notify_event.response_type = XCB_SELECTION_NOTIFY; + notify_event.requestor = req_event->requestor; + notify_event.selection = req_event->selection; + notify_event.target = req_event->target; + notify_event.property = req_event->property; + notify_event.time = req_event->time; + + xcb_send_event(server, false, req_event->requestor, XCB_EVENT_MASK_NO_EVENT, (char*)¬ify_event); + + xcb_flush(server); + + break; + } + case XCB_GE_GENERIC: + { + xcb_ge_event_t* ge = (xcb_ge_event_t*)event; + switch (ge->event_type) + { + case XCB_INPUT_MOTION: + { + xcb_input_motion_event_t *motion_event = (xcb_input_motion_event_t *)ge; + cursorPos = {motion_event->event_x >> 16, motion_event->event_y >> 16}; + break; + } + case XCB_INPUT_RAW_MOTION: + { + const xcb_input_raw_motion_event_t* rm = (xcb_input_raw_motion_event_t*)ge; + + int axis_len = xcb_input_raw_button_press_axisvalues_length(rm); + if (axis_len != 2) + break; + + xcb_input_fp3232_t *pos = xcb_input_raw_button_press_axisvalues_raw(rm); + + Mouse* device = (Mouse*)ih.GetDevice(rm->deviceid); + if (!device) + { + device = new Mouse(QueryDeviceName(rm->deviceid), rm->deviceid); + ih.AddDevice(device); + } + + device->SetDelta({pos[0].integral, pos[1].integral}); + + break; + } + case XCB_INPUT_RAW_BUTTON_PRESS: + { + const xcb_input_raw_button_press_event_t* bpEvent = (xcb_input_raw_button_press_event_t*)event; + UInt_32 code = bpEvent->detail; + + Button button = Mouse::TranslateXCB(code); + + Mouse* device = (Mouse*)ih.GetDevice(bpEvent->deviceid); + if (!device) + { + device = new Mouse(QueryDeviceName(bpEvent->deviceid), bpEvent->deviceid); + ih.AddDevice(device); + } + + device->ButtonDown(button); + + break; + } + case XCB_INPUT_RAW_BUTTON_RELEASE: + { + const xcb_input_raw_button_release_event_t* bpEvent = (xcb_input_raw_button_release_event_t*) event; + UInt_32 code = bpEvent->detail; + + Button button = Mouse::TranslateXCB(code); + + Mouse* device = (Mouse*)ih.GetDevice(bpEvent->deviceid); + if (!device) + { + device = new Mouse(QueryDeviceName(bpEvent->deviceid), bpEvent->deviceid); + ih.AddDevice(device); + } + + device->ButtonUp(button); + + break; + } + case XCB_INPUT_RAW_KEY_PRESS: + { + const xcb_input_raw_key_press_event_t* kpEvent = (xcb_input_raw_key_press_event_t*)event; + UInt_32 code = kpEvent->detail - 8; + + Button button = Keyboard::TranslateScanCode(code); + + Keyboard* device = (Keyboard*)ih.GetDevice(kpEvent->deviceid); + if (!device) + { + device = new Keyboard(QueryDeviceName(kpEvent->deviceid), kpEvent->deviceid); + ih.AddDevice(device); + } + + device->ButtonDown(button); + + break; + } + case XCB_INPUT_RAW_KEY_RELEASE: + { + const xcb_input_raw_key_release_event_t* kpEvent = (xcb_input_raw_key_release_event_t*)event; + UInt_32 code = kpEvent->detail - 8; + + Button button = Keyboard::TranslateScanCode(code); + + Keyboard* device = (Keyboard*)ih.GetDevice(kpEvent->deviceid); + if (!device) + { + device = new Keyboard(QueryDeviceName(kpEvent->deviceid), kpEvent->deviceid); + ih.AddDevice(device); + } + + device->ButtonUp(button); + + break; + } + default: + { + break; + } + } + } + default: + { + break; + } + } + } + + free(event); + + BaseWindow::Poll(); + + return true; + } + + void Window::ShowCursor(bool toggle) + { + xcb_xfixes_query_version(server, 4, 0); + + if (toggle) + xcb_xfixes_show_cursor(server, hdl); + else + xcb_xfixes_hide_cursor(server, hdl); + + xcb_flush(server); + + cursorVisible = toggle; + } + + void Window::ConstrainCursor(const bool constrain) + { + if (constrain) + { + xcb_grab_pointer_cookie_t cookie = xcb_grab_pointer( + server, + true, + hdl, + XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_BUTTON_PRESS | + XCB_EVENT_MASK_BUTTON_RELEASE, + XCB_GRAB_MODE_ASYNC, + XCB_GRAB_MODE_ASYNC, + hdl, + XCB_NONE, + XCB_CURRENT_TIME + ); + + xcb_grab_pointer_reply_t* reply = xcb_grab_pointer_reply(server, cookie, nullptr); + if (!reply || reply->status != XCB_GRAB_STATUS_SUCCESS) + { + free(reply); + EHS_LOG_INT("Error", 0, "Failed to constrain cursor."); + return; + } + + free(reply); + } + else + { + xcb_ungrab_pointer(server, XCB_CURRENT_TIME); + xcb_flush(server); + } + + cursorConstrained = constrain; + } + + void Window::SetTitle_32(const Str_32& newTitle) + { + SetTitle_8(UTF::To_8(newTitle)); + } + + Str_32 Window::GetTitle_32() const + { + return UTF::To_32(GetTitle_8()); + } + + void Window::SetTitle_16(const Str_16& newTitle) + { + SetTitle_8(UTF::To_8(newTitle)); + } + + Str_16 Window::GetTitle_16() const + { + return UTF::To_16(GetTitle_8()); + } + + void Window::SetTitle_8(const Str_8 &newTitle) + { + xcb_change_property(server, XCB_PROP_MODE_REPLACE, hdl, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, newTitle.Size(), &newTitle[0]); + } + + Str_8 Window::GetTitle_8() const + { + xcb_get_property_reply_t* reply = RetrieveProp(XCB_ATOM_WM_NAME, XCB_ATOM_STRING); + Str_8 result((char*)xcb_get_property_value(reply), xcb_get_property_value_length(reply)); + free(reply); + return result; + } + + void Window::SetPos(const Vec2_s32& newPos) + { + xcb_configure_window(server, hdl, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &((Byte*)&newPos)[offsetof(Vec2_s32, x)]); + + xcb_flush(server); + } + + Vec2_s32 Window::GetPos() const + { + Vec2_s32 result; + + const xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry(server, hdl); + xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(server, geom_cookie, nullptr); + if (!geom) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve window position."); + return result; + } + + result = {geom->x, geom->y}; + + free(geom); + + return {}; + } + + void Window::SetScale(const Vec2_u32& newScale) + { + xcb_configure_window(server, hdl, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, &((Byte*)&newScale)[offsetof(Vec2_u32, x)]); + + xcb_flush(server); + } + + Vec2_u32 Window::GetScale() const + { + Vec2_s32 result; + + const xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry(server, hdl); + xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(server, geom_cookie, nullptr); + if (!geom) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve window scale."); + return result; + } + + result = {geom->width, geom->height}; + + free(geom); + + return result; + } + + Serializer Window::GetClipboard() + { + Serializer result; + + const xcb_atom_t clipboard_atom = RetrieveAtom(false, "CLIPBOARD"); + const xcb_atom_t utf8_string_atom = RetrieveAtom(false, "STRING"); + const xcb_atom_t property_atom = RetrieveAtom(true, "MATHISART"); + + if (clipboard_atom == XCB_ATOM_NONE || utf8_string_atom == XCB_ATOM_NONE || property_atom == XCB_ATOM_NONE) + { + EHS_LOG_INT("Error", 1, "Failed to retrieve atoms."); + return result; + } + + xcb_convert_selection(server, hdl, clipboard_atom, utf8_string_atom, + property_atom, XCB_CURRENT_TIME); + + xcb_flush(server); + + //events.Insert(0, xcb_wait_for_event(server)); + + const xcb_get_property_cookie_t prop_cookie = xcb_get_property(server, 0, hdl, + property_atom, utf8_string_atom, 0, UINT32_MAX / 4); + + if (xcb_get_property_reply_t* prop_reply = xcb_get_property_reply(server, prop_cookie, nullptr); prop_reply) + { + result = Serializer(Endianness::LE, (Byte*)xcb_get_property_value(prop_reply), + xcb_get_property_value_length(prop_reply), 0); + + free(prop_reply); + } + + return result; + } + + void Window::SetClipboard(Serializer data) + { + if (clipboard == data) + return; + + clipboard = (Serializer&&)data; + + const xcb_atom_t clipboard_atom = RetrieveAtom(false, "CLIPBOARD"); + if (clipboard_atom == XCB_ATOM_NONE) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve atom."); + return; + } + + xcb_set_selection_owner(server, hdl, clipboard_atom, XCB_CURRENT_TIME); + + xcb_flush(server); + } + + void Window::SetCursorImg(const CursorImg img) + { + xcb_cursor_t text_cursor = XCB_NONE; + + if (img == CursorImg::DEFAULT) + { + xcb_change_window_attributes(server, hdl, XCB_CW_CURSOR, &text_cursor); + + xcb_flush(server); + } + if (img == CursorImg::I_BEAM) + { + xcb_cursor_context_t *cursor_context; + + xcb_cursor_context_new(server, xcb_setup_roots_iterator(xcb_get_setup(server)).data, &cursor_context); + text_cursor = xcb_cursor_load_cursor(cursor_context, "xterm"); + + xcb_change_window_attributes(server, hdl, XCB_CW_CURSOR, &text_cursor); + + xcb_cursor_context_free(cursor_context); + + xcb_flush(server); + } + } + + xcb_connection_t *Window::GetServer() + { + return server; + } + + xcb_generic_event_t* Window::RetrieveEvent() + { + if (events.Size()) + { + xcb_generic_event_t* event = events[0]; + events.Remove(0); + return event; + } + else + return xcb_poll_for_event(server); + } + + xcb_atom_t Window::RetrieveAtom(const bool create, const Str_8& name) const + { + xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(server, xcb_intern_atom(server, !create, + name.Size(), name), nullptr); + + if (!reply) + return XCB_ATOM_NONE; + + const xcb_atom_t atom = reply->atom; + + free(reply); + + return atom; + } + + xcb_get_property_reply_t *Window::RetrieveProp(const xcb_atom_t prop, const xcb_atom_t type) const + { + return xcb_get_property_reply(server, xcb_get_property(server, false, hdl, prop, type, 0, 0), nullptr); + } + + void Window::QueryPrimaryDevices() + { + xcb_input_xi_query_device_cookie_t device_cookie = xcb_input_xi_query_device(server, XCB_INPUT_DEVICE_ALL); + + xcb_input_xi_query_device_reply_t *device_reply = xcb_input_xi_query_device_reply(server, device_cookie, nullptr); + + if (!device_reply) + { + EHS_LOG_INT("Error", 0, "Failed to query primary devices."); + return; + } + + xcb_input_xi_device_info_iterator_t device_iter = xcb_input_xi_query_device_infos_iterator(device_reply); + for (; device_iter.rem; xcb_input_xi_device_info_next(&device_iter)) + { + xcb_input_xi_device_info_t *device_info = device_iter.data; + + Str_8 name(xcb_input_xi_device_info_name(device_info), + xcb_input_xi_device_info_name_length(device_info)); + + if (device_info->type == XCB_INPUT_DEVICE_TYPE_MASTER_POINTER) + ih.AddDevice(new Mouse(name, device_info->deviceid)); + + if (device_info->type == XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD) + ih.AddDevice(new Keyboard(name, device_info->deviceid)); + } + + free(device_reply); + } + + Str_8 Window::QueryDeviceName(const UInt_16 id) + { + Str_8 result; + + xcb_input_xi_query_device_cookie_t device_cookie = xcb_input_xi_query_device(server, id); + + xcb_input_xi_query_device_reply_t *device_reply = xcb_input_xi_query_device_reply(server, device_cookie, nullptr); + + if (!device_reply) + { + EHS_LOG_INT("Error", 0, "Failed to query device name from the id, \"" + Str_8::FromNum(id) + "\"."); + return result; + } + + xcb_input_xi_device_info_iterator_t device_iter = xcb_input_xi_query_device_infos_iterator(device_reply); + for (; device_iter.rem; xcb_input_xi_device_info_next(&device_iter)) + { + xcb_input_xi_device_info_t *device_info = device_iter.data; + + result = Str_8(xcb_input_xi_device_info_name(device_info), + xcb_input_xi_device_info_name_length(device_info)); + } + + free(device_reply); + + return result; + } +} \ No newline at end of file diff --git a/src/io/audio/Audio.cpp b/src/io/audio/Audio.cpp new file mode 100644 index 0000000..b743a3c --- /dev/null +++ b/src/io/audio/Audio.cpp @@ -0,0 +1,1951 @@ +#include "ehs/io/audio/Audio.h" +#include "ehs/io/File.h" +#include "ehs/io/RIFF.h" + +namespace ehs +{ + Array Audio::codecs; + + bool Audio::HasCodec(const UInt_64 hashExt) + { + for (UInt_64 i = 0; i < codecs.Size(); ++i) + if (codecs[i].GetHashExt() == hashExt) + return true; + + return false; + } + + bool Audio::HasCodec(const Str_8& ext) + { + return HasCodec(ext.Hash_64()); + } + + bool Audio::AddCodec(AudioCodec codec) + { + if (HasCodec(codec.GetHashExt())) + return false; + + codecs.Push(std::move(codec)); + + return true; + } + + const AudioCodec* Audio::GetCodec(const UInt_64 hashExt) + { + for (UInt_64 i = 0; i < codecs.Size(); ++i) + if (codecs[i].GetHashExt() == hashExt) + return &codecs[i]; + + return nullptr; + } + + const AudioCodec* Audio::GetCodec(const Str_8& ext) + { + return GetCodec(ext.Hash_64()); + } + + Audio::~Audio() + { + delete[] data; + delete[] peak; + } + + Audio::Audio() + : sampleRate(0), dataType(DataType::FLOAT), byteDepth(0), channels(0), frames(0), length(0.0f), data(nullptr), + peak(nullptr) + { + AddType("Audio"); + } + + Audio::Audio(Str_8 id, const UInt_64 sampleRate, const DataType dataType, const UInt_8 channels, const UInt_64 frames, const Byte* const data) + : Resource((Str_8&&)id), dataType(dataType), byteDepth(ToByteDepth(dataType)), sampleRate(sampleRate), + channels(channels), frames(frames), length((float)frames / (float)sampleRate), + data(new Byte[GetSize()]), peak(new Byte[byteDepth]) + { + Util::Copy(this->data, data, GetSize()); + + AddType("Audio"); + } + + Audio::Audio(Str_8 id, const UInt_64 sampleRate, const DataType dataType, const UInt_8 channels, const Serializer& data) + : Resource((Str_8&&)id), sampleRate(sampleRate), dataType(dataType), byteDepth(ToByteDepth(dataType)), + channels(channels), frames(data.Size() / channels / byteDepth), length((float)frames / (float)sampleRate), + data(new Byte[data.Size()]), peak(new Byte[byteDepth]) + { + Util::Copy(this->data, data, data.Size()); + + AddType("Audio"); + } + + Audio::Audio(Str_8 id, const UInt_64 sampleRate, const DataType dataType, const UInt_8 channels, const Vector& data) + : Resource((Str_8&&)id), sampleRate(sampleRate), dataType(dataType), byteDepth(ToByteDepth(dataType)), + channels(channels), frames(data.Size() / channels / byteDepth), length((float)frames / (float)sampleRate), + data(new Byte[data.Size()]), peak(new Byte[byteDepth]) + { + Util::Copy(this->data, data, data.Size()); + + AddType("Audio"); + } + + Audio::Audio(Str_8 id, const UInt_64 sampleRate, const DataType dataType, const UInt_8 channels, const Array& data) + : Resource((Str_8&&)id), sampleRate(sampleRate), dataType(dataType), byteDepth(ToByteDepth(dataType)), + channels(channels), frames(data.Size() / channels / byteDepth), length((float)frames / (float)sampleRate), + data(new Byte[data.Size()]), peak(new Byte[byteDepth]) + { + Util::Copy(this->data, data, data.Size()); + + AddType("Audio"); + } + + Audio::Audio(Str_8 id, const UInt_64 sampleRate, const DataType dataType, const UInt_8 channels, const UInt_64 frames) + : Resource((Str_8&&)id), sampleRate(sampleRate), dataType(dataType), byteDepth(ToByteDepth(dataType)), + channels(channels), frames(frames), length((float)frames / (float)sampleRate), + data(new Byte[GetSize()]), peak(new Byte[byteDepth]) + { + AddType("Audio"); + } + + Audio::Audio(Str_8 id) + : Resource((Str_8&&)id), sampleRate(0), dataType(DataType::FLOAT), byteDepth(0), channels(0), + frames(0), length(0.0f), data(nullptr), peak(nullptr) + { + AddType("Audio"); + } + + Audio::Audio(Audio&& audio) noexcept + : Resource((Resource&&)audio), sampleRate(audio.sampleRate), dataType(audio.dataType), + byteDepth(audio.byteDepth), channels(audio.channels), frames(audio.frames), length(audio.length), + data(audio.data), peak(audio.peak) + { + audio.sampleRate = 0; + audio.dataType = DataType::FLOAT; + audio.byteDepth = ToByteDepth(audio.dataType); + audio.channels = 0; + audio.frames = 0; + audio.length = 0.0f; + audio.data = nullptr; + audio.peak = nullptr; + } + + Audio::Audio(const Audio& audio) + : Resource(audio), sampleRate(audio.sampleRate), dataType(audio.dataType), byteDepth(audio.byteDepth), + channels(audio.channels), frames(audio.frames), length(audio.length), data(new Byte[GetSize()]), + peak(new Byte[audio.byteDepth]) + { + Util::Copy(data, audio.data, GetSize()); + } + + Audio& Audio::operator=(Audio&& audio) noexcept + { + if (this == &audio) + return *this; + + Resource::operator=((Resource&&)audio); + + sampleRate = audio.sampleRate; + dataType = audio.dataType; + byteDepth = audio.byteDepth; + channels = audio.channels; + frames = audio.frames; + length = audio.length; + delete[] data; + data = audio.data; + delete[] peak; + peak = audio.peak; + + audio.sampleRate = 0; + audio.dataType = DataType::FLOAT; + audio.byteDepth = ToByteDepth(audio.dataType); + audio.channels = 0; + audio.frames = 0; + audio.length = 0.0f; + audio.data = nullptr; + audio.peak = nullptr; + + return *this; + } + + Audio& Audio::operator=(const Audio& audio) + { + if (this == &audio) + return *this; + + Resource::operator=(audio); + + sampleRate = audio.sampleRate; + dataType = audio.dataType; + byteDepth = audio.byteDepth; + channels = audio.channels; + length = audio.length; + frames = audio.frames; + + delete[] data; + data = new Byte[audio.GetSize()]; + Util::Copy(data, audio.data, audio.GetSize()); + + delete[] peak; + peak = new Byte[audio.byteDepth]; + Util::Copy(peak, audio.peak, audio.byteDepth); + + return *this; + } + + Audio::operator const Byte*() const + { + return data; + } + + Audio::operator Byte*() + { + return data; + } + + void Audio::Release() + { + sampleRate = 0; + dataType = DataType::FLOAT; + byteDepth = 0; + channels = 0; + frames = 0; + length = 0.0f; + delete[] data; + data = nullptr; + delete[] peak; + peak = nullptr; + } + + UInt_64 Audio::GetSampleRate() const + { + return sampleRate; + } + + DataType Audio::GetDataType() const + { + return dataType; + } + + UInt_8 Audio::GetByteDepth() const + { + return byteDepth; + } + + UInt_8 Audio::GetBitDepth() const + { + return byteDepth * 8; + } + + UInt_8 Audio::GetChannels() const + { + return channels; + } + + UInt_64 Audio::GetFrameCount() const + { + return frames; + } + + UInt_64 Audio::GetSampleCount() const + { + return channels * frames; + } + + UInt_64 Audio::GetSize() const + { + return byteDepth * channels * frames; + } + + float Audio::GetLength() const + { + return length; + } + + Array Audio::FrameAsMono(const UInt_64 frameIndex) const + { + Array result(byteDepth); + ToMono(1, result, frameIndex); + return result; + } + + Array Audio::FrameAsStereo(const UInt_64 frameIndex) const + { + Array result; + + if (channels == 1) + { + result.Resize(byteDepth * channels); + Mono_to_Stereo(1, result, frameIndex); + } + else if (channels == 2) + { + return {&data[byteDepth * channels * frameIndex], (UInt_64)(byteDepth * channels)}; + } + else if (channels == 6) + { + result.Resize(byteDepth * channels); + Five_One_to_Stereo(1, result, frameIndex); + } + else if (channels == 8) + { + result.Resize(byteDepth * channels); + Seven_One_to_Stereo(1, result, frameIndex); + } + else + { + EHS_LOG_INT("Error", 0, "Conversion from " + Str_8::FromNum(channels) + " channels, to 2 channels is unsupported."); + } + + return result; + } + + Array Audio::FrameAsFive_One(const UInt_64 frameIndex) const + { + Array result; + + if (channels == 1) + { + result.Resize(byteDepth * 6 ); + Mono_to_Five_One(1, result, frameIndex); + } + else if (channels == 2) + { + result.Resize(byteDepth * 6); + Stereo_to_Five_One(1, result, frameIndex); + } + else if (channels == 6) + { + return {&data[byteDepth * channels * frameIndex], (UInt_64)(byteDepth * channels)}; + } + else if (channels == 8) + { + result.Resize(byteDepth * 6); + Seven_One_to_Five_One(1, result, frameIndex); + } + else + { + EHS_LOG_INT("Error", 0, "Conversion from " + Str_8::FromNum(channels) + " channels, to 6 channels is unsupported."); + } + + return result; + } + + Array Audio::FrameAsSeven_One(const UInt_64 frameIndex) const + { + Array result; + + if (channels == 1) + { + result.Resize(byteDepth * 8); + Mono_to_Seven_One(1, result, frameIndex); + } + else if (channels == 2) + { + result.Resize(byteDepth * 8); + Stereo_to_Seven_One(1, result, frameIndex); + } + else if (channels == 6) + { + result.Resize(byteDepth * 8); + Five_One_to_Seven_One(1, result, frameIndex); + } + else if (channels == 8) + { + return {&data[byteDepth * channels * frameIndex], (UInt_64)(byteDepth * channels)}; + } + else + { + EHS_LOG_INT("Error", 0, "Conversion from " + Str_8::FromNum(channels) + " channels, to 8 channels is unsupported."); + } + + return result; + } + + SInt_8 Audio::SampleAsSInt_8(const UInt_64 sampleIndex) const + { + switch (dataType) + { + case DataType::SINT_8: + return ((SInt_8*)data)[sampleIndex]; + case DataType::SINT_16: + return (SInt_8)((float)((SInt_16*)data)[sampleIndex] / (float)EHS_SINT_16_MAX * (float)EHS_SINT_8_MAX); + case DataType::FLOAT: + return (SInt_8)(((float*)data)[sampleIndex] * (float)EHS_SINT_8_MAX); + case DataType::SINT_32: + return (SInt_8)((float)((SInt_32*)data)[sampleIndex] / (float)EHS_SINT_32_MAX * (float)EHS_SINT_8_MAX); + case DataType::SINT_64: + return (SInt_8)((float)((SInt_64*)data)[sampleIndex] / (float)EHS_SINT_64_MAX * (float)EHS_SINT_8_MAX); + default: + return 0; + } + } + + SInt_16 Audio::SampleAsSInt_16(const UInt_64 sampleIndex) const + { + switch (dataType) + { + case DataType::SINT_8: + return (SInt_16)((float)((SInt_8*)data)[sampleIndex] / (float)EHS_SINT_8_MAX * (float)EHS_SINT_16_MAX); + case DataType::SINT_16: + return ((SInt_16*)data)[sampleIndex]; + case DataType::FLOAT: + return (SInt_16)(((float*)data)[sampleIndex] * (float)EHS_SINT_16_MAX); + case DataType::SINT_32: + return (SInt_16)((float)((SInt_32*)data)[sampleIndex] / (float)EHS_SINT_32_MAX * (float)EHS_SINT_16_MAX); + case DataType::SINT_64: + return (SInt_16)((float)((SInt_64*)data)[sampleIndex] / (float)EHS_SINT_64_MAX * (float)EHS_SINT_16_MAX); + default: + return 0; + } + } + + float Audio::SampleAsFloat(const UInt_64 sampleIndex) const + { + switch (dataType) + { + case DataType::SINT_8: + return (float)((SInt_8*)data)[sampleIndex] / (float)EHS_SINT_8_MAX; + case DataType::SINT_16: + return (float)((SInt_16*)data)[sampleIndex] / (float)EHS_SINT_16_MAX; + case DataType::FLOAT: + return ((float*)data)[sampleIndex]; + case DataType::SINT_32: + return (float)((SInt_32*)data)[sampleIndex] / (float)EHS_SINT_32_MAX; + case DataType::SINT_64: + return (float)((SInt_64*)data)[sampleIndex] / (float)EHS_SINT_64_MAX; + default: + return 0; + } + } + + SInt_32 Audio::SampleAsSInt_32(const UInt_64 sampleIndex) const + { + switch (dataType) + { + case DataType::SINT_8: + return (SInt_32)((float)((SInt_8*)data)[sampleIndex] / (float)EHS_SINT_8_MAX * (float)EHS_SINT_32_MAX); + case DataType::SINT_16: + return (SInt_32)((float)((SInt_16*)data)[sampleIndex] / (float)EHS_SINT_16_MAX * (float)EHS_SINT_32_MAX); + case DataType::FLOAT: + return (SInt_32)(((float*)data)[sampleIndex] * (float)EHS_SINT_32_MAX); + case DataType::SINT_32: + return ((SInt_32*)data)[sampleIndex]; + case DataType::SINT_64: + return (SInt_32)((float)((SInt_64*)data)[sampleIndex] / (float)EHS_SINT_64_MAX * (float)EHS_SINT_32_MAX); + default: + return 0; + } + } + + SInt_64 Audio::SampleAsSInt_64(const UInt_64 sampleIndex) const + { + switch (dataType) + { + case DataType::SINT_8: + return (SInt_64)((float)((SInt_8*)data)[sampleIndex] / (float)EHS_SINT_8_MAX * (float)EHS_SINT_64_MAX); + case DataType::SINT_16: + return (SInt_64)((float)((SInt_16*)data)[sampleIndex] / (float)EHS_SINT_16_MAX * (float)EHS_SINT_64_MAX); + case DataType::FLOAT: + return (SInt_64)(((float*)data)[sampleIndex] * (float)EHS_SINT_64_MAX); + case DataType::SINT_32: + return (SInt_64)((float)((SInt_32*)data)[sampleIndex] / (float)EHS_SINT_32_MAX * (float)EHS_SINT_64_MAX); + case DataType::SINT_64: + return ((SInt_64*)data)[sampleIndex]; + default: + return 0; + } + } + + SInt_8 Audio::PeakAsSInt_8() const + { + switch (dataType) + { + case DataType::SINT_8: + return *(SInt_8*)peak; + case DataType::SINT_16: + return (SInt_8)((float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX * (float)EHS_SINT_8_MAX); + case DataType::FLOAT: + return (SInt_8)(*(float*)peak * (float)EHS_SINT_8_MAX); + case DataType::SINT_32: + return (SInt_8)((float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX * (float)EHS_SINT_8_MAX); + case DataType::SINT_64: + return (SInt_8)((float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX * (float)EHS_SINT_8_MAX); + default: + return 0; + } + } + + SInt_16 Audio::PeakAsSInt_16() const + { + switch (dataType) + { + case DataType::SINT_8: + return (SInt_16)((float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX * (float)EHS_SINT_16_MAX); + case DataType::SINT_16: + return *(SInt_16*)peak; + case DataType::FLOAT: + return (SInt_16)(*(float*)peak * (float)EHS_SINT_16_MAX); + case DataType::SINT_32: + return (SInt_16)((float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX * (float)EHS_SINT_16_MAX); + case DataType::SINT_64: + return (SInt_16)((float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX * (float)EHS_SINT_16_MAX); + default: + return 0; + } + } + + float Audio::PeakAsFloat() const + { + switch (dataType) + { + case DataType::SINT_8: + return (float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX; + case DataType::SINT_16: + return (float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX; + case DataType::FLOAT: + return *(float*)peak; + case DataType::SINT_32: + return (float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX; + case DataType::SINT_64: + return (float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX; + default: + return 0; + } + } + + SInt_32 Audio::PeakAsSInt_32() const + { + switch (dataType) + { + case DataType::SINT_8: + return (SInt_32)((float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX * (float)EHS_SINT_32_MAX); + case DataType::SINT_16: + return (SInt_32)((float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX * (float)EHS_SINT_32_MAX); + case DataType::FLOAT: + return (SInt_32)(*(float*)peak * (float)EHS_SINT_32_MAX); + case DataType::SINT_32: + return *(SInt_32*)peak; + case DataType::SINT_64: + return (SInt_32)((float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX * (float)EHS_SINT_32_MAX); + default: + return 0; + } + } + + SInt_64 Audio::PeakAsSInt_64() const + { + switch (dataType) + { + case DataType::SINT_8: + return (SInt_64)((float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX * (float)EHS_SINT_64_MAX); + case DataType::SINT_16: + return (SInt_64)((float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX * (float)EHS_SINT_64_MAX); + case DataType::FLOAT: + return (SInt_64)(*(float*)peak * (float)EHS_SINT_64_MAX); + case DataType::SINT_32: + return (SInt_64)((float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX * (float)EHS_SINT_64_MAX); + case DataType::SINT_64: + return *(SInt_64*)peak; + default: + return 0; + } + } + + void Audio::SetPeak(const UInt_64 size, const Byte* newPeak) + { + Util::Copy(peak, newPeak, size); + } + + const Byte* Audio::GetPeak() const + { + return peak; + } + + void Audio::ToDataType(const DataType newDataType) + { + if (dataType == newDataType) + return; + + UInt_8 newByteDepth = ToByteDepth(newDataType); + + Byte* newData = new Byte[GetSampleCount() * newByteDepth]; + Byte* newPeak = new Byte[newByteDepth]; + + if (dataType == DataType::SINT_16 && newDataType == DataType::SINT_8) + SInt_16_to_SInt_8(newData, newPeak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_8) + Float_to_SInt_8(newData, newPeak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::SINT_8) + SInt_32_to_SInt_8(newData, newPeak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::SINT_8) + SInt_64_to_SInt_8(newData, newPeak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::SINT_16) + SInt_8_to_SInt_16(newData, newPeak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_16) + Float_to_SInt_16(newData, newPeak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::SINT_16) + SInt_32_to_SInt_16(newData, newPeak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::SINT_16) + SInt_64_to_SInt_16(newData, newPeak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::FLOAT) + SInt_8_to_Float(newData, newPeak); + else if (dataType == DataType::SINT_16 && newDataType == DataType::FLOAT) + SInt_16_to_Float(newData, newPeak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::FLOAT) + SInt_32_to_Float(newData, newPeak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::FLOAT) + SInt_64_to_Float(newData, newPeak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::SINT_32) + SInt_8_to_SInt_32(newData, newPeak); + else if (dataType == DataType::SINT_16 && newDataType == DataType::SINT_32) + SInt_16_to_SInt_32(newData, newPeak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_32) + Float_to_SInt_32(newData, newPeak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::SINT_32) + SInt_64_to_SInt_32(newData, newPeak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::SINT_64) + SInt_8_to_SInt_64(newData, newPeak); + else if (dataType == DataType::SINT_16 && newDataType == DataType::SINT_64) + SInt_16_to_SInt_64(newData, newPeak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_64) + Float_to_SInt_64(newData, newPeak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::SINT_64) + SInt_32_to_SInt_64(newData, newPeak); + + dataType = newDataType; + byteDepth = ToByteDepth(newDataType); + + delete[] data; + data = newData; + delete[] peak; + peak = newPeak; + } + + Audio Audio::GetAsDataType(const DataType newDataType) const + { + if (dataType == newDataType) + return *this; + + Audio result; + result.sampleRate = sampleRate; + result.dataType = newDataType; + result.byteDepth = ToByteDepth(newDataType); + result.channels = channels; + result.frames = frames; + result.length = length; + + result.data = new Byte[result.GetSize()]; + result.peak = new Byte[result.byteDepth]; + + if (dataType == DataType::SINT_16 && newDataType == DataType::SINT_8) + SInt_16_to_SInt_8(result.data, result.peak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_8) + Float_to_SInt_8(result.data, result.peak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::SINT_8) + SInt_32_to_SInt_8(result.data, result.peak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::SINT_8) + SInt_64_to_SInt_8(result.data, result.peak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::SINT_16) + SInt_8_to_SInt_16(result.data, result.peak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_16) + Float_to_SInt_16(result.data, result.peak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::SINT_16) + SInt_32_to_SInt_16(result.data, result.peak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::SINT_16) + SInt_64_to_SInt_16(result.data, result.peak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::FLOAT) + SInt_8_to_Float(result.data, result.peak); + else if (dataType == DataType::SINT_16 && newDataType == DataType::FLOAT) + SInt_16_to_Float(result.data, result.peak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::FLOAT) + SInt_32_to_Float(result.data, result.peak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::FLOAT) + SInt_64_to_Float(result.data, result.peak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::SINT_32) + SInt_8_to_SInt_32(result.data, result.peak); + else if (dataType == DataType::SINT_16 && newDataType == DataType::SINT_32) + SInt_16_to_SInt_32(result.data, result.peak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_32) + Float_to_SInt_32(result.data, result.peak); + else if (dataType == DataType::SINT_64 && newDataType == DataType::SINT_32) + SInt_64_to_SInt_32(result.data, result.peak); + else if (dataType == DataType::SINT_8 && newDataType == DataType::SINT_64) + SInt_8_to_SInt_64(result.data, result.peak); + else if (dataType == DataType::SINT_16 && newDataType == DataType::SINT_64) + SInt_16_to_SInt_64(result.data, result.peak); + else if (dataType == DataType::FLOAT && newDataType == DataType::SINT_64) + Float_to_SInt_64(result.data, result.peak); + else if (dataType == DataType::SINT_32 && newDataType == DataType::SINT_64) + SInt_32_to_SInt_64(result.data, result.peak); + + return result; + } + + void Audio::ToChannels(const UInt_8 newChannels) + { + if (!data || newChannels == channels) + return; + + Byte* result; + + if (newChannels == 1) + { + result = new Byte[byteDepth * newChannels * frames]; + ToMono(frames, result, 0); + } + else if (channels == 1 && newChannels == 2) + { + result = new Byte[byteDepth * newChannels * frames]; + Mono_to_Stereo(frames, result, 0); + } + else if (channels == 6 && newChannels == 2) + { + result = new Byte[byteDepth * newChannels * frames]; + Five_One_to_Stereo(frames, result, 0); + } + else if (channels == 8 && newChannels == 2) + { + result = new Byte[byteDepth * newChannels * frames]; + Seven_One_to_Stereo(frames, result, 0); + } + else if (channels == 1 && newChannels == 6) + { + result = new Byte[byteDepth * newChannels * frames]; + Mono_to_Five_One(frames, result, 0); + } + else if (channels == 2 && newChannels == 6) + { + result = new Byte[byteDepth * newChannels * frames]; + Stereo_to_Five_One(frames, result, 0); + } + else if (channels == 8 && newChannels == 6) + { + result = new Byte[byteDepth * newChannels * frames]; + Seven_One_to_Five_One(frames, result, 0); + } + else if (channels == 1 && newChannels == 8) + { + result = new Byte[byteDepth * newChannels * frames]; + Mono_to_Seven_One(frames, result, 0); + } + else if (channels == 2 && newChannels == 8) + { + result = new Byte[byteDepth * newChannels * frames]; + Stereo_to_Seven_One(frames, result, 0); + } + else if (channels == 6 && newChannels == 8) + { + result = new Byte[byteDepth * newChannels * frames]; + Five_One_to_Seven_One(frames, result, 0); + } + else + { + EHS_LOG_INT("Error", 0, "Conversion from " + Str_8::FromNum(channels) + " channels, to " + + Str_8::FromNum(newChannels) + " channels is unsupported."); + return; + } + + channels = newChannels; + delete[] data; + data = result; + } + + Audio Audio::GetAsChannels(const UInt_8 newChannels) const + { + Audio result; + + if (!data || newChannels == channels) + return result; + + if (newChannels == 1) + { + result.data = new Byte[byteDepth * newChannels * frames]; + ToMono(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 1 && newChannels == 2) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Mono_to_Stereo(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 6 && newChannels == 2) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Five_One_to_Stereo(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 8 && newChannels == 2) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Seven_One_to_Stereo(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 1 && newChannels == 6) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Mono_to_Five_One(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 2 && newChannels == 6) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Stereo_to_Five_One(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 8 && newChannels == 6) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Seven_One_to_Five_One(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 1 && newChannels == 8) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Mono_to_Seven_One(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 2 && newChannels == 8) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Stereo_to_Seven_One(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else if (channels == 6 && newChannels == 8) + { + result.data = new Byte[byteDepth * newChannels * frames]; + Five_One_to_Seven_One(frames, result.data, 0); + + result.peak = new Byte[byteDepth]; + Util::Copy(result.peak, peak, byteDepth); + } + else + { + EHS_LOG_INT("Error", 0, "Conversion from " + Str_8::FromNum(channels) + " channels, to " + + Str_8::FromNum(newChannels) + " channels is unsupported."); + + return result; + } + + result.sampleRate = sampleRate; + result.dataType = dataType; + result.byteDepth = byteDepth; + result.channels = newChannels; + result.frames = frames; + result.length = (float)frames / (float)sampleRate; + + return result; + } + + bool Audio::ToFile(const Str_8& filePath) const + { + Str_8 ext = File::ParseExt_8(filePath); + + const AudioCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return false; + } + + Serializer result; + if (!codec->Encode(result, this)) + return false; + + File file(filePath, Mode::WRITE, Disposition::CREATE_PERSISTENT); + file.WriteSerializer_64(result); + + return true; + } + + Audio Audio::FromFile(const Str_8& filePath) + { + File file(filePath, Mode::READ, Disposition::OPEN); + Str_8 ext = file.GetExtension(); + + Audio result(file.GetName()); + + const AudioCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return result; + } + + Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); + + file.Release(); + + if (!codec->Decode(data, &result)) + return {}; + + return result; + } + + Audio* Audio::FromFile_Heap(const Str_8& filePath) + { + File file(filePath, Mode::READ, Disposition::OPEN); + Str_8 ext = file.GetExtension(); + + Audio* result = nullptr; + + const AudioCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return result; + } + + result = new Audio(file.GetName()); + + Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); + + file.Release(); + + if (!codec->Decode(data, result)) + { + delete result; + return nullptr; + } + + return result; + } + + Audio Audio::FromFile(const Str_8& filePath, const DataType required) + { + Audio result = std::move(FromFile(filePath)); + if (!result.data) + return result; + + result.ToDataType(required); + return result; + } + + Audio* Audio::FromFile_Heap(const Str_8& filePath, const DataType required) + { + Audio* result = FromFile_Heap(filePath); + if (!result) + return result; + + result->ToDataType(required); + return result; + } + + Audio Audio::FromData(Str_8 id, const Str_8& ext, Serializer& data) + { + Audio result((Str_8&&)id); + + const AudioCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return result; + } + + if (!codec->Decode(data, &result)) + return {}; + + return result; + } + + void Audio::ToMono(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffset = channels * (frameOffset + i); + ((SInt_8*)newData)[i] = (((SInt_8*)data)[sampleOffset] + ((SInt_8*)data)[sampleOffset + 1]) / (PeakAsSInt_8() * 2); + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffset = channels * (frameOffset + i); + ((SInt_16*)newData)[i] = (((SInt_16*)data)[sampleOffset] + ((SInt_16*)data)[sampleOffset + 1]) / (PeakAsSInt_16() * 2); + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffset = channels * (frameOffset + i); + ((float*)newData)[i] = (((float*)data)[sampleOffset] + ((float*)data)[sampleOffset + 1]) / (PeakAsFloat() * 2.0f); + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffset = channels * (frameOffset + i); + ((SInt_32*)newData)[i] = (((SInt_32*)data)[sampleOffset] + ((SInt_32*)data)[sampleOffset + 1]) / (PeakAsSInt_32() * 2); + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffset = channels * (frameOffset + i); + ((SInt_64*)newData)[i] = (((SInt_64*)data)[sampleOffset] + ((SInt_64*)data)[sampleOffset + 1]) / (PeakAsSInt_64() * 2); + } + return; + } + default: + return; + } + } + + void Audio::Mono_to_Stereo(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB]; + } + return; + } + default: + return; + } + } + + void Audio::Five_One_to_Stereo(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB + 1]; + } + return; + } + default: + return; + } + } + + void Audio::Seven_One_to_Stereo(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 2 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB + 1]; + } + return; + } + default: + return; + } + } + + void Audio::Mono_to_Five_One(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 2] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 3] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 4] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 5] = ((SInt_8*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 2] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 3] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 4] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 5] = ((SInt_16*)data)[sampleOffsetB]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 2] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 3] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 4] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 5] = ((float*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 2] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 3] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 4] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 5] = ((SInt_32*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 2] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 3] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 4] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 5] = ((SInt_64*)data)[sampleOffsetB]; + } + return; + } + default: + return; + } + } + + void Audio::Stereo_to_Five_One(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB + 1]; + ((SInt_8*)newData)[sampleOffsetA + 2] = (((SInt_8*)data)[sampleOffsetB] + ((SInt_8*)data)[sampleOffsetB + 1]) / (PeakAsSInt_8() * 2); + ((SInt_8*)newData)[sampleOffsetA + 3] = ((SInt_8*)newData)[sampleOffsetA + 2]; + ((SInt_8*)newData)[sampleOffsetA + 4] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 5] = ((SInt_8*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB + 1]; + ((SInt_16*)newData)[sampleOffsetA + 2] = (((SInt_16*)data)[sampleOffsetB] + ((SInt_16*)data)[sampleOffsetB + 1]) / (PeakAsSInt_16() * 2); + ((SInt_16*)newData)[sampleOffsetA + 3] = ((SInt_16*)newData)[sampleOffsetA + 2]; + ((SInt_16*)newData)[sampleOffsetA + 4] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 5] = ((SInt_16*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB + 1]; + ((float*)newData)[sampleOffsetA + 2] = (((float*)data)[sampleOffsetB] + ((float*)data)[sampleOffsetB + 1]) / (PeakAsFloat() * 2.0f); + ((float*)newData)[sampleOffsetA + 3] = ((float*)newData)[sampleOffsetA + 2]; + ((float*)newData)[sampleOffsetA + 4] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 5] = ((float*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB + 1]; + ((SInt_32*)newData)[sampleOffsetA + 2] = (((SInt_32*)data)[sampleOffsetB] + ((SInt_32*)data)[sampleOffsetB + 1]) / (PeakAsSInt_32() * 2); + ((SInt_32*)newData)[sampleOffsetA + 3] = ((SInt_32*)newData)[sampleOffsetA + 2]; + ((SInt_32*)newData)[sampleOffsetA + 4] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 5] = ((SInt_32*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB + 1]; + ((SInt_64*)newData)[sampleOffsetA + 2] = (((SInt_64*)data)[sampleOffsetB] + ((SInt_64*)data)[sampleOffsetB + 1]) / (PeakAsSInt_64() * 2); + ((SInt_64*)newData)[sampleOffsetA + 3] = ((SInt_64*)newData)[sampleOffsetA + 2]; + ((SInt_64*)newData)[sampleOffsetA + 4] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 5] = ((SInt_64*)data)[sampleOffsetB + 1]; + } + return; + } + default: + return; + } + } + + void Audio::Seven_One_to_Five_One(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB + 1]; + ((SInt_8*)newData)[sampleOffsetA + 2] = ((SInt_8*)data)[sampleOffsetB + 2]; + ((SInt_8*)newData)[sampleOffsetA + 3] = ((SInt_8*)data)[sampleOffsetB + 3]; + ((SInt_8*)newData)[sampleOffsetA + 4] = ((SInt_8*)data)[sampleOffsetB + 4]; + ((SInt_8*)newData)[sampleOffsetA + 5] = ((SInt_8*)data)[sampleOffsetB + 5]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB + 1]; + ((SInt_16*)newData)[sampleOffsetA + 2] = ((SInt_16*)data)[sampleOffsetB + 2]; + ((SInt_16*)newData)[sampleOffsetA + 3] = ((SInt_16*)data)[sampleOffsetB + 3]; + ((SInt_16*)newData)[sampleOffsetA + 4] = ((SInt_16*)data)[sampleOffsetB + 4]; + ((SInt_16*)newData)[sampleOffsetA + 5] = ((SInt_16*)data)[sampleOffsetB + 5]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB + 1]; + ((float*)newData)[sampleOffsetA + 2] = ((float*)data)[sampleOffsetB + 2]; + ((float*)newData)[sampleOffsetA + 3] = ((float*)data)[sampleOffsetB + 3]; + ((float*)newData)[sampleOffsetA + 4] = ((float*)data)[sampleOffsetB + 4]; + ((float*)newData)[sampleOffsetA + 5] = ((float*)data)[sampleOffsetB + 5]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB + 1]; + ((SInt_32*)newData)[sampleOffsetA + 2] = ((SInt_32*)data)[sampleOffsetB + 2]; + ((SInt_32*)newData)[sampleOffsetA + 3] = ((SInt_32*)data)[sampleOffsetB + 3]; + ((SInt_32*)newData)[sampleOffsetA + 4] = ((SInt_32*)data)[sampleOffsetB + 4]; + ((SInt_32*)newData)[sampleOffsetA + 5] = ((SInt_32*)data)[sampleOffsetB + 5]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 6 * i; + UInt_64 sampleOffsetB = 8 * (frameOffset + i); + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB + 1]; + ((SInt_64*)newData)[sampleOffsetA + 2] = ((SInt_64*)data)[sampleOffsetB + 2]; + ((SInt_64*)newData)[sampleOffsetA + 3] = ((SInt_64*)data)[sampleOffsetB + 3]; + ((SInt_64*)newData)[sampleOffsetA + 4] = ((SInt_64*)data)[sampleOffsetB + 4]; + ((SInt_64*)newData)[sampleOffsetA + 5] = ((SInt_64*)data)[sampleOffsetB + 5]; + } + return; + } + default: + return; + } + } + + void Audio::Mono_to_Seven_One(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 2] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 3] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 4] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 5] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 6] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 7] = ((SInt_8*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 2] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 3] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 4] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 5] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 6] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 7] = ((SInt_16*)data)[sampleOffsetB]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 2] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 3] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 4] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 5] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 6] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 7] = ((float*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 2] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 3] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 4] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 5] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 6] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 7] = ((SInt_32*)data)[sampleOffsetB]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = frameOffset + i; + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 2] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 3] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 4] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 5] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 6] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 7] = ((SInt_64*)data)[sampleOffsetB]; + } + return; + } + default: + return; + } + } + + void Audio::Stereo_to_Seven_One(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB + 1]; + ((SInt_8*)newData)[sampleOffsetA + 2] = (((SInt_8*)data)[sampleOffsetB] + ((SInt_8*)data)[sampleOffsetB + 1]) / (PeakAsSInt_8() * 2); + ((SInt_8*)newData)[sampleOffsetA + 3] = ((SInt_8*)newData)[sampleOffsetA + 2]; + ((SInt_8*)newData)[sampleOffsetA + 4] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 5] = ((SInt_8*)data)[sampleOffsetB + 1]; + ((SInt_8*)newData)[sampleOffsetA + 6] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 7] = ((SInt_8*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB + 1]; + ((SInt_16*)newData)[sampleOffsetA + 2] = (((SInt_16*)data)[sampleOffsetB] + ((SInt_16*)data)[sampleOffsetB + 1]) / (PeakAsSInt_16() * 2); + ((SInt_16*)newData)[sampleOffsetA + 3] = ((SInt_16*)newData)[sampleOffsetA + 2]; + ((SInt_16*)newData)[sampleOffsetA + 4] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 5] = ((SInt_16*)data)[sampleOffsetB + 1]; + ((SInt_16*)newData)[sampleOffsetA + 6] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 7] = ((SInt_16*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB + 1]; + ((float*)newData)[sampleOffsetA + 2] = (((float*)data)[sampleOffsetB] + ((float*)data)[sampleOffsetB + 1]) / (PeakAsFloat() * 2.0f); + ((float*)newData)[sampleOffsetA + 3] = ((float*)newData)[sampleOffsetA + 2]; + ((float*)newData)[sampleOffsetA + 4] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 5] = ((float*)data)[sampleOffsetB + 1]; + ((float*)newData)[sampleOffsetA + 6] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 7] = ((float*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB + 1]; + ((SInt_32*)newData)[sampleOffsetA + 2] = (((SInt_32*)data)[sampleOffsetB] + ((SInt_32*)data)[sampleOffsetB + 1]) / (PeakAsSInt_32() * 2); + ((SInt_32*)newData)[sampleOffsetA + 3] = ((SInt_32*)newData)[sampleOffsetA + 2]; + ((SInt_32*)newData)[sampleOffsetA + 4] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 5] = ((SInt_32*)data)[sampleOffsetB + 1]; + ((SInt_32*)newData)[sampleOffsetA + 6] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 7] = ((SInt_32*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 2 * (frameOffset + i); + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB + 1]; + ((SInt_64*)newData)[sampleOffsetA + 2] = (((SInt_64*)data)[sampleOffsetB] + ((SInt_64*)data)[sampleOffsetB + 1]) / (PeakAsSInt_64() * 2); + ((SInt_64*)newData)[sampleOffsetA + 3] = ((SInt_64*)newData)[sampleOffsetA + 2]; + ((SInt_64*)newData)[sampleOffsetA + 4] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 5] = ((SInt_64*)data)[sampleOffsetB + 1]; + ((SInt_64*)newData)[sampleOffsetA + 6] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 7] = ((SInt_64*)data)[sampleOffsetB + 1]; + } + return; + } + default: + return; + } + } + + void Audio::Five_One_to_Seven_One(const UInt_64 newFrameCount, Byte* newData, const UInt_64 frameOffset) const + { + switch (dataType) + { + case DataType::SINT_8: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_8*)newData)[sampleOffsetA] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 1] = ((SInt_8*)data)[sampleOffsetB + 1]; + ((SInt_8*)newData)[sampleOffsetA + 2] = ((SInt_8*)data)[sampleOffsetB + 2]; + ((SInt_8*)newData)[sampleOffsetA + 3] = ((SInt_8*)data)[sampleOffsetB + 3]; + ((SInt_8*)newData)[sampleOffsetA + 4] = ((SInt_8*)data)[sampleOffsetB + 4]; + ((SInt_8*)newData)[sampleOffsetA + 5] = ((SInt_8*)data)[sampleOffsetB + 5]; + ((SInt_8*)newData)[sampleOffsetA + 6] = ((SInt_8*)data)[sampleOffsetB]; + ((SInt_8*)newData)[sampleOffsetA + 7] = ((SInt_8*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_16: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_16*)newData)[sampleOffsetA] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 1] = ((SInt_16*)data)[sampleOffsetB + 1]; + ((SInt_16*)newData)[sampleOffsetA + 2] = ((SInt_16*)data)[sampleOffsetB + 2]; + ((SInt_16*)newData)[sampleOffsetA + 3] = ((SInt_16*)data)[sampleOffsetB + 3]; + ((SInt_16*)newData)[sampleOffsetA + 4] = ((SInt_16*)data)[sampleOffsetB + 4]; + ((SInt_16*)newData)[sampleOffsetA + 5] = ((SInt_16*)data)[sampleOffsetB + 5]; + ((SInt_16*)newData)[sampleOffsetA + 6] = ((SInt_16*)data)[sampleOffsetB]; + ((SInt_16*)newData)[sampleOffsetA + 7] = ((SInt_16*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::FLOAT: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((float*)newData)[sampleOffsetA] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 1] = ((float*)data)[sampleOffsetB + 1]; + ((float*)newData)[sampleOffsetA + 2] = ((float*)data)[sampleOffsetB + 2]; + ((float*)newData)[sampleOffsetA + 3] = ((float*)data)[sampleOffsetB + 3]; + ((float*)newData)[sampleOffsetA + 4] = ((float*)data)[sampleOffsetB + 4]; + ((float*)newData)[sampleOffsetA + 5] = ((float*)data)[sampleOffsetB + 5]; + ((float*)newData)[sampleOffsetA + 6] = ((float*)data)[sampleOffsetB]; + ((float*)newData)[sampleOffsetA + 7] = ((float*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_32: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_32*)newData)[sampleOffsetA] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 1] = ((SInt_32*)data)[sampleOffsetB + 1]; + ((SInt_32*)newData)[sampleOffsetA + 2] = ((SInt_32*)data)[sampleOffsetB + 2]; + ((SInt_32*)newData)[sampleOffsetA + 3] = ((SInt_32*)data)[sampleOffsetB + 3]; + ((SInt_32*)newData)[sampleOffsetA + 4] = ((SInt_32*)data)[sampleOffsetB + 4]; + ((SInt_32*)newData)[sampleOffsetA + 5] = ((SInt_32*)data)[sampleOffsetB + 5]; + ((SInt_32*)newData)[sampleOffsetA + 6] = ((SInt_32*)data)[sampleOffsetB]; + ((SInt_32*)newData)[sampleOffsetA + 7] = ((SInt_32*)data)[sampleOffsetB + 1]; + } + return; + } + case DataType::SINT_64: + { + for (UInt_64 i = 0; i < newFrameCount; ++i) + { + UInt_64 sampleOffsetA = 8 * i; + UInt_64 sampleOffsetB = 6 * (frameOffset + i); + + ((SInt_64*)newData)[sampleOffsetA] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 1] = ((SInt_64*)data)[sampleOffsetB + 1]; + ((SInt_64*)newData)[sampleOffsetA + 2] = ((SInt_64*)data)[sampleOffsetB + 2]; + ((SInt_64*)newData)[sampleOffsetA + 3] = ((SInt_64*)data)[sampleOffsetB + 3]; + ((SInt_64*)newData)[sampleOffsetA + 4] = ((SInt_64*)data)[sampleOffsetB + 4]; + ((SInt_64*)newData)[sampleOffsetA + 5] = ((SInt_64*)data)[sampleOffsetB + 5]; + ((SInt_64*)newData)[sampleOffsetA + 6] = ((SInt_64*)data)[sampleOffsetB]; + ((SInt_64*)newData)[sampleOffsetA + 7] = ((SInt_64*)data)[sampleOffsetB + 1]; + } + return; + } + default: + return; + } + } + + void Audio::SInt_16_to_SInt_8(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + newData[a] = (Byte)((float)((SInt_16*)data)[a] / (float)EHS_SINT_16_MAX * (float)EHS_SINT_8_MAX); + + *(SInt_8*)newPeak = (SInt_8)((float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX * (float)EHS_SINT_8_MAX); + } + + void Audio::Float_to_SInt_8(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_8*)newData)[a] = (SInt_8)(((float*)data)[a] * (float)EHS_SINT_8_MAX); + + *(SInt_8*)newPeak = (SInt_8)(*(float*)peak * (float)EHS_SINT_8_MAX); + } + + void Audio::SInt_32_to_SInt_8(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + newData[a] = (Byte)((float)((SInt_32*)data)[a] / (float)EHS_SINT_32_MAX * (float)EHS_SINT_8_MAX); + + *(SInt_8*)newPeak = (SInt_8)((float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX * (float)EHS_SINT_8_MAX); + } + + void Audio::SInt_64_to_SInt_8(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + newData[a] = (Byte)((float)((SInt_64*)data)[a] / (float)EHS_SINT_64_MAX * (float)EHS_SINT_8_MAX); + + *(SInt_8*)newPeak = (SInt_8)((float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX * (float)EHS_SINT_8_MAX); + } + + void Audio::SInt_8_to_SInt_16(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_16*)newData)[a] = (SInt_16)((float)(SInt_8)data[a] / (float)EHS_SINT_8_MAX * (float)EHS_SINT_16_MAX); + + *(SInt_16*)newPeak = (SInt_16)((float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX * (float)EHS_SINT_16_MAX); + } + + void Audio::Float_to_SInt_16(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_16*)newData)[a] = (SInt_16)(((float*)data)[a] * (float)EHS_SINT_16_MAX); + + *(SInt_16*)newPeak = (SInt_16)(*(float*)peak * (float)EHS_SINT_16_MAX); + } + + void Audio::SInt_32_to_SInt_16(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_16*)newData)[a] = (SInt_16)((float)((SInt_32*)data)[a] / (float)EHS_SINT_32_MAX * (float)EHS_SINT_16_MAX); + + *(SInt_16*)newPeak = (SInt_16)((float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX * (float)EHS_SINT_16_MAX); + } + + void Audio::SInt_64_to_SInt_16(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_16*)newData)[a] = (SInt_16)((float)((SInt_64*)data)[a] / (float)EHS_SINT_64_MAX * (float)EHS_SINT_16_MAX); + + *(SInt_16*)newPeak = (SInt_16)((float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX * (float)EHS_SINT_16_MAX); + } + + void Audio::SInt_8_to_Float(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((float*)newData)[a] = (float)(((SInt_8*)data)[a]) / (float)EHS_SINT_8_MAX; + + *(float*)newPeak = (float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX; + } + + void Audio::SInt_16_to_Float(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((float*)newData)[a] = (float)(((SInt_16*)data)[a]) / (float)EHS_SINT_16_MAX; + + *(float*)newPeak = (float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX; + } + + void Audio::SInt_32_to_Float(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((float*)newData)[a] = (float)(((SInt_32*)data)[a]) / (float)EHS_SINT_32_MAX; + + *(float*)newPeak = (float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX; + } + + void Audio::SInt_64_to_Float(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((float*)newData)[a] = (float)(((SInt_64*)data)[a]) / (float)EHS_SINT_64_MAX; + + *(float*)newPeak = (float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX; + } + + void Audio::SInt_8_to_SInt_32(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_32*)newData)[a] = (SInt_32)((float)(SInt_8)data[a] / (float)EHS_SINT_8_MAX * (float)EHS_SINT_32_MAX); + + *(SInt_32*)newPeak = (SInt_32)((float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX * (float)EHS_SINT_32_MAX); + } + + void Audio::SInt_16_to_SInt_32(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_32*)newData)[a] = (SInt_32)((float)((SInt_16*)data)[a] / (float)EHS_SINT_16_MAX * (float)EHS_SINT_32_MAX); + + *(SInt_32*)newPeak = (SInt_32)((float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX * (float)EHS_SINT_32_MAX); + } + + void Audio::Float_to_SInt_32(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_32*)newData)[a] = (SInt_32)(((float*)data)[a] * (float)EHS_SINT_32_MAX); + + *(SInt_32*)newPeak = (SInt_32)(*(float*)peak * (float)EHS_SINT_32_MAX); + } + + void Audio::SInt_64_to_SInt_32(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_32*)newData)[a] = (SInt_32)((float)((SInt_64*)data)[a] / (float)EHS_SINT_64_MAX * (float)EHS_SINT_32_MAX); + + *(SInt_32*)newPeak = (SInt_32)((float)*(SInt_64*)peak / (float)EHS_SINT_64_MAX * (float)EHS_SINT_32_MAX); + } + + void Audio::SInt_8_to_SInt_64(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_64*)newData)[a] = (SInt_64)((float)(SInt_8)data[a] / (float)EHS_SINT_8_MAX * (float)EHS_SINT_64_MAX); + + *(SInt_64*)newPeak = (SInt_64)((float)*(SInt_8*)peak / (float)EHS_SINT_8_MAX * (float)EHS_SINT_64_MAX); + } + + void Audio::SInt_16_to_SInt_64(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_64*)newData)[a] = (SInt_64)((float)((SInt_16*)data)[a] / (float)EHS_SINT_16_MAX * (float)EHS_SINT_64_MAX); + + *(SInt_64*)newPeak = (SInt_64)((float)*(SInt_16*)peak / (float)EHS_SINT_16_MAX * (float)EHS_SINT_64_MAX); + } + + void Audio::Float_to_SInt_64(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_64*)newData)[a] = (SInt_64)(((float*)data)[a] * (float)EHS_SINT_64_MAX); + + *(SInt_64*)newPeak = (SInt_64)(*(float*)peak * (float)EHS_SINT_64_MAX); + } + + void Audio::SInt_32_to_SInt_64(Byte* newData, Byte* newPeak) const + { + for (UInt_64 a = 0; a < GetSampleCount(); ++a) + ((SInt_64*)newData)[a] = (SInt_64)((float)((SInt_32*)data)[a] / (float)EHS_SINT_32_MAX * (float)EHS_SINT_64_MAX); + + *(SInt_64*)newPeak = (SInt_64)((float)*(SInt_32*)peak / (float)EHS_SINT_32_MAX * (float)EHS_SINT_64_MAX); + } +} \ No newline at end of file diff --git a/src/io/audio/AudioCodec.cpp b/src/io/audio/AudioCodec.cpp new file mode 100644 index 0000000..5d92381 --- /dev/null +++ b/src/io/audio/AudioCodec.cpp @@ -0,0 +1,109 @@ +#include "ehs/io/audio/AudioCodec.h" + +namespace ehs +{ + AudioCodec::AudioCodec() + : hashExt(0), endianness(Endianness::LE), encodeCb(nullptr), decodeCb(nullptr) + { + } + + AudioCodec::AudioCodec(Str_8 id, Str_8 ext, const Endianness end, + bool (* encodeCb)(const AudioCodec* const, Serializer&, const Audio*), + bool (* decodeCb)(const AudioCodec* const, Serializer&, Audio*)) + : id(std::move(id)), hashExt(ext.Hash_64()), ext(std::move(ext)), endianness(end), encodeCb(encodeCb), decodeCb(decodeCb) + { + } + + AudioCodec::AudioCodec(AudioCodec&& codec) noexcept + : id(std::move(codec.id)), hashExt(codec.hashExt), ext(std::move(codec.ext)), endianness(codec.endianness), + encodeCb(codec.encodeCb), decodeCb(codec.decodeCb) + { + codec.hashExt = 0; + codec.endianness = Endianness::LE; + codec.encodeCb = nullptr; + codec.decodeCb = nullptr; + } + + AudioCodec::AudioCodec(const AudioCodec& codec) + : id(codec.id), hashExt(codec.hashExt), ext(codec.ext), endianness(codec.endianness), encodeCb(codec.encodeCb), + decodeCb(codec.decodeCb) + { + } + + AudioCodec& AudioCodec::operator=(AudioCodec&& codec) noexcept + { + if (this == &codec) + return *this; + + id = std::move(codec.id); + hashExt = codec.hashExt; + ext = std::move(codec.ext); + endianness = codec.endianness; + encodeCb = codec.encodeCb; + decodeCb = codec.decodeCb; + + codec.hashExt = 0; + codec.endianness = Endianness::LE; + codec.encodeCb = nullptr; + codec.decodeCb = nullptr; + + return *this; + } + + AudioCodec& AudioCodec::operator=(const AudioCodec& codec) + { + if (this == &codec) + return *this; + + id = codec.id; + hashExt = codec.hashExt; + ext = codec.ext; + endianness = codec.endianness; + encodeCb = codec.encodeCb; + decodeCb = codec.decodeCb; + + return *this; + } + + Str_8 AudioCodec::GetId() const + { + return id; + } + + UInt_64 AudioCodec::GetHashExt() const + { + return hashExt; + } + + Str_8 AudioCodec::GetExt() const + { + return ext; + } + + Endianness AudioCodec::GetEndianness() const + { + return endianness; + } + + bool AudioCodec::Encode(Serializer& out, const Audio* in) const + { + if (!encodeCb) + { + EHS_LOG_INT("Error", 0, "Encoding is not supported for the " + id + " format."); + return false; + } + + return encodeCb(this, out, in); + } + + bool AudioCodec::Decode(Serializer& in, Audio* out) const + { + if (!decodeCb) + { + EHS_LOG_INT("Error", 0, "Decoding is not supported for the " + id + " format."); + return false; + } + + return decodeCb(this, in, out); + } +} \ No newline at end of file diff --git a/src/io/audio/AudioDevice_ALSA.cpp b/src/io/audio/AudioDevice_ALSA.cpp new file mode 100644 index 0000000..764436b --- /dev/null +++ b/src/io/audio/AudioDevice_ALSA.cpp @@ -0,0 +1,383 @@ +#include "ehs/io/audio/AudioDevice_ALSA.h" +#include "ehs/EHS.h" +#include "ehs/Log.h" + +namespace ehs +{ + AudioDevice::~AudioDevice() + { + if (!hdl) + return; + + snd_pcm_drain(hdl); + snd_pcm_close(hdl); + } + + AudioDevice::AudioDevice() + : hdl(nullptr) + { + } + + AudioDevice::AudioDevice(AudioDevice&& device) noexcept + : BaseAudioDevice(device), hdl(device.hdl) + { + device.hdl = nullptr; + } + + AudioDevice::AudioDevice(const AudioDevice& device) + : BaseAudioDevice(device), hdl(nullptr) + { + } + + AudioDevice& AudioDevice::operator=(AudioDevice&& device) noexcept + { + if (this == &device) + return *this; + + BaseAudioDevice::operator=(device); + + hdl = device.hdl; + + device.hdl = nullptr; + + return *this; + } + + AudioDevice& AudioDevice::operator=(const AudioDevice& device) + { + if (this == &device) + return *this; + + BaseAudioDevice::operator=(device); + + hdl = nullptr; + + return *this; + } + + void AudioDevice::Release() + { + if (!IsValid()) + return; + + snd_pcm_drain(hdl); + snd_pcm_close(hdl); + hdl = nullptr; + } + + void AudioDevice::OpenStream() + { + if (streaming) + return; + + snd_pcm_hw_params_t* params; + snd_pcm_hw_params_alloca(¶ms); + + snd_pcm_hw_params_any(hdl, params); + + if (snd_pcm_hw_params_set_access(hdl, params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) + { + EHS_LOG_INT("Error", 0, "Failed to set access."); + return; + } + + if (bitDepth) + { + snd_pcm_format_t format; + switch (dataType) + { + case DataType::FLOAT: + { + format = SND_PCM_FORMAT_FLOAT; + break; + } + case DataType::SINT_32: + { + format = SND_PCM_FORMAT_S32; + break; + } + case DataType::SINT_24: + { + format = SND_PCM_FORMAT_S24; + break; + } + case DataType::SINT_16: + { + format = SND_PCM_FORMAT_S16; + break; + } + case DataType::SINT_8: + { + format = SND_PCM_FORMAT_S8; + break; + } + default: + { + EHS_LOG_INT("Error", 0, "Invalid data type."); + return; + } + } + + snd_pcm_hw_params_set_format(hdl, params, format); + } + + if (channels) + { + if (snd_pcm_hw_params_set_channels_near(hdl, params, &channels) < 0) + { + EHS_LOG_INT("Error", 1, "Failed to set channels."); + return; + } + } + + if (sampleRate) + { + if (snd_pcm_hw_params_set_rate_near(hdl, params, &sampleRate, nullptr) < 0) + { + EHS_LOG_INT("Error", 2, "Failed to set sample rate."); + return; + } + } + + if (snd_pcm_hw_params_set_period_time_near(hdl, params, &period, nullptr) < 0) + { + EHS_LOG_INT("Error", 3, "Failed to set period."); + return; + } + + if (snd_pcm_hw_params_set_buffer_time_near(hdl, params, &latency, nullptr) < 0) + { + EHS_LOG_INT("Error", 4, "Failed to set latency."); + return; + } + + if (snd_pcm_hw_params(hdl, params) < 0) + { + EHS_LOG_INT("Error", 5, "Failed to apply hardware parameters."); + return; + } + + if (!bitDepth) + { + snd_pcm_format_t format; + if (snd_pcm_hw_params_get_format(params, &format) < 0) + { + EHS_LOG_INT("Error", 6, "Failed to retrieve audio device properties."); + return; + } + + switch (format) + { + case SND_PCM_FORMAT_FLOAT: + { + dataType = DataType::FLOAT; + bitDepth = 32; + break; + } + case SND_PCM_FORMAT_S32: + { + dataType = DataType::SINT_32; + bitDepth = 32; + break; + } + case SND_PCM_FORMAT_S24: + { + dataType = DataType::SINT_24; + bitDepth = 24; + break; + } + case SND_PCM_FORMAT_S16: + { + dataType = DataType::SINT_16; + bitDepth = 16; + break; + } + case SND_PCM_FORMAT_S8: + { + dataType = DataType::SINT_8; + bitDepth = 8; + break; + } + default: + { + EHS_LOG_INT("Error", 7, "Format unsupported."); + break; + } + } + } + + if (!channels) + { + if (snd_pcm_hw_params_get_channels(params, &channels) < 0) + { + EHS_LOG_INT("Error", 8, "Failed to retrieve channel count."); + return; + } + } + + if (!sampleRate) + { + int dir; + if (snd_pcm_hw_params_get_rate(params, &sampleRate, &dir) < 0) + { + EHS_LOG_INT("Error", 9, "Failed to retrieve sample rate."); + return; + } + } + + if (snd_pcm_hw_params_get_buffer_size(params, &maxFrames) < 0) + { + EHS_LOG_INT("Error", 10, "Failed to retrieve buffer size."); + } + + snd_pcm_sw_params_t* swParams = nullptr; + snd_pcm_sw_params_alloca(&swParams); + + if (snd_pcm_sw_params_current(hdl, swParams) < 0) + { + EHS_LOG_INT("Error", 11, "Failed to retrieve software parameters."); + return; + } + + if (snd_pcm_sw_params_set_silence_threshold(hdl, swParams, maxFrames) < 0) + { + EHS_LOG_INT("Error", 12, "Failed to set silence threshold."); + return; + } + + if (snd_pcm_sw_params_set_silence_size(hdl, swParams, maxFrames) < 0) + { + EHS_LOG_INT("Error", 12, "Failed to set silence size."); + return; + } + + if (snd_pcm_sw_params(hdl, swParams) < 0) + { + EHS_LOG_INT("Error", 13, "Failed to set software parameters."); + return; + } + + if (snd_pcm_prepare(hdl) < 0) + { + EHS_LOG_INT("Error", 14, "Failed to prepare audio stream."); + return; + } + + streaming = true; + } + + void AudioDevice::CloseStream() + { + if (!streaming) + return; + + snd_pcm_drain(hdl); + + streaming = false; + } + + UInt_64 AudioDevice::GetAvailFrames() const + { + snd_pcm_state_t state = snd_pcm_state(hdl); + if (state == SND_PCM_STATE_XRUN) + { + EHS_LOG_INT("Warning", 0, "Buffer overrun/underrun occurred."); + if (snd_pcm_recover(hdl, -EPIPE, 0) < 0) + { + EHS_LOG_INT("Error", 1, "Failed to recover from buffer overrun/underrun."); + return 0; + } + return GetAvailFrames(); + } + else if (state == SND_PCM_STATE_PREPARED) + { + if (snd_pcm_start(hdl) < 0) + { + EHS_LOG_INT("Error", 2, "Failed to start audio stream."); + return 0; + } + + return GetAvailFrames(); + } + + snd_pcm_sframes_t frames = snd_pcm_avail_update(hdl); + if (frames < 0) + { + if (frames == -EPIPE) + { + EHS_LOG_INT("Warning", 3, "Buffer overrun/underrun occurred."); + if (snd_pcm_recover(hdl, -EPIPE, 1) < 0) + { + EHS_LOG_INT("Error", 4, "Failed to recover from buffer overrun/underrun."); + return 0; + } + + return GetAvailFrames(); + } + else + { + EHS_LOG_INT("Error", 5, "Failed to retrieve available frames with error #" + Str_8::FromNum(frames) + "."); + return 0; + } + } + + return frames; + } + + Byte* AudioDevice::Map(UInt_64* offset, UInt_64* frames) + { + const snd_pcm_channel_area_t* areas; + if (snd_pcm_mmap_begin(hdl, &areas, offset, frames) < 0) + { + EHS_LOG_INT("Error", 0, "Failed to map audio buffer."); + return nullptr; + } + + return (Byte*)areas->addr + areas->first / 8; + } + + void AudioDevice::UnMap(const UInt_64 offset, const UInt_64 frames) + { + snd_pcm_sframes_t committed = snd_pcm_mmap_commit(hdl, offset, frames); + if (committed < 0) + { + EHS_LOG_INT("Error", 0, "Failed to commit mapped audio buffer."); + return; + } + } + + bool AudioDevice::IsValid() const + { + return hdl; + } + + AudioDevice AudioDevice::GetDefault(const AudioDeviceType type) + { + AudioDevice result; + + snd_pcm_stream_t rType; + if (type == AudioDeviceType::INPUT) + { + rType = SND_PCM_STREAM_CAPTURE; + } + else if (type == AudioDeviceType::OUTPUT) + { + rType = SND_PCM_STREAM_PLAYBACK; + } + else + { + EHS_LOG_INT("Error", 0, "Wrong value for the audio device type."); + return {}; + } + + snd_pcm_open(&result.hdl, "default", rType, SND_PCM_NONBLOCK); + result.type = AudioDeviceType::OUTPUT; + + return result; + } + + Array AudioDevice::Get(const AudioDeviceType type, const AudioDeviceState state) + { + return {}; + } +} \ No newline at end of file diff --git a/src/io/audio/AudioDevice_W32.cpp b/src/io/audio/AudioDevice_W32.cpp new file mode 100644 index 0000000..8c71ff2 --- /dev/null +++ b/src/io/audio/AudioDevice_W32.cpp @@ -0,0 +1,581 @@ +#include "io/audio/AudioDevice_W32.h" +#include "ehs/Log.h" + +namespace ehs +{ + AudioDevice::~AudioDevice() + { + if (hdl) + { + hdl->Release(); + hdl = nullptr; + } + } + + AudioDevice::AudioDevice() + : hdl(nullptr), client(nullptr), playbackClient(nullptr), captureClient(nullptr) + { + } + + AudioDevice::AudioDevice(AudioDevice&& device) noexcept + : BaseAudioDevice(device), hdl(device.hdl), client(device.client), playbackClient(device.playbackClient), + captureClient(device.captureClient) + { + device.hdl = nullptr; + device.client = nullptr; + device.playbackClient = nullptr; + device.captureClient = nullptr; + } + + AudioDevice::AudioDevice(const AudioDevice& device) + : BaseAudioDevice(device), hdl(nullptr), client(nullptr), playbackClient(nullptr), + captureClient(nullptr) + { + } + + AudioDevice& AudioDevice::operator=(AudioDevice&& device) noexcept + { + if (this == &device) + return *this; + + BaseAudioDevice::operator=(device); + + hdl = device.hdl; + client = device.client; + playbackClient = device.playbackClient; + captureClient = device.captureClient; + + device.hdl = nullptr; + device.client = nullptr; + device.playbackClient = nullptr; + device.captureClient = nullptr; + + return *this; + } + + AudioDevice& AudioDevice::operator=(const AudioDevice& device) + { + if (this == &device) + return *this; + + BaseAudioDevice::operator=(device); + + hdl = nullptr; + client = nullptr; + playbackClient = nullptr; + captureClient = nullptr; + + return *this; + } + + void AudioDevice::Release() + { + if (captureClient) + { + captureClient->Release(); + captureClient = nullptr; + } + + if (playbackClient) + { + playbackClient->Release(); + playbackClient = nullptr; + } + + if (hdl) + { + HRESULT r = client->Stop(); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 0, "Failed to stop audio with error #" + Str_8::FromNum(r) + "."); + return; + } + + hdl->Release(); + hdl = nullptr; + } + } + + void AudioDevice::OpenStream() + { + if (streaming) + return; + + HRESULT r = hdl->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&client); + if (FAILED(r)) + EHS_LOG_INT("Error", 0, "Failed to create audio client with error #" + Str_8::FromNum(r) + "."); + + WAVEFORMATEXTENSIBLE* format; + client->GetMixFormat((WAVEFORMATEX**)&format); + + if (bitDepth) + { + if (dataType == DataType::FLOAT) + format->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + format->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + format->Format.wBitsPerSample = bitDepth; + } + else + { + if (format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) + dataType = DataType::FLOAT; + else + dataType = FromAudioBitDepth(format->Format.wBitsPerSample); + + bitDepth = format->Format.wBitsPerSample; + } + + if (sampleRate) + format->Format.nSamplesPerSec = sampleRate; + else + sampleRate = format->Format.nSamplesPerSec; + + if (channels) + format->Format.nChannels = channels; + else + channels = format->Format.nChannels; + + format->Format.nBlockAlign = bitDepth / 8 * channels; + format->Format.nAvgBytesPerSec = format->Format.nBlockAlign * sampleRate; + + WAVEFORMATEX* match = {}; + r = client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)format, &match); + if (FAILED(r)) + { + if (r == AUDCLNT_E_UNSUPPORTED_FORMAT) + { + EHS_LOG_INT("Error", 1, + "The audio device, \"" + GetName_8() + "\" doesn't support the format {" + + Str_8::FromNum(format->Format.wBitsPerSample) + "-bit, " + + Str_8::FromNum(format->Format.nSamplesPerSec) + "Hz, " + + Str_8::FromNum(format->Format.nChannels) + " Channels}." + ); + } + else + EHS_LOG_INT("Error", 2, "Failed to retrieve supported audio format with error #" + Str_8::FromNum(r) + "."); + + return; + } + + if (match) + { + EHS_LOG_INT("Error", 3, + "The audio device, \"" + GetName_8() + "\" doesn't support the format {" + + Str_8::FromNum(format->Format.wBitsPerSample) + "-bit, " + + Str_8::FromNum(format->Format.nSamplesPerSec) + "Hz, " + + Str_8::FromNum(format->Format.nChannels) + " Channels}. Closest format, {" + + Str_8::FromNum(match->wBitsPerSample) + "-bit Max, " + + Str_8::FromNum(match->nSamplesPerSec) + "Hz Max, " + + Str_8::FromNum(match->nChannels) + " Channels Min}." + ); + + return; + } + + r = client->Initialize( + AUDCLNT_SHAREMODE_SHARED, + 0, + latency * 10, + period * 10, + (WAVEFORMATEX*)format, + nullptr + ); + + if (r == AUDCLNT_E_ALREADY_INITIALIZED) + { + EHS_LOG_INT("Error", 5, "Audio client is already initialized."); + return; + } + else if (r == AUDCLNT_E_WRONG_ENDPOINT_TYPE) + { + EHS_LOG_INT("Error", 6, "The AUDCLNT_STREAMFLAGS_LOOPBACK flag is set but the endpoint device is a capture device, not a rendering device."); + return; + } + else if (r == AUDCLNT_E_BUFFER_SIZE_ERROR) + { + EHS_LOG_INT("Error", 7, "Indicates that the buffer duration value requested by an exclusive-mode client is out of range. The requested duration value for pull mode must not be greater than 5000 milliseconds; for push mode the duration value must not be greater than 2 seconds."); + return; + } + else if (r == AUDCLNT_E_CPUUSAGE_EXCEEDED) + { + EHS_LOG_INT("Error", 8, "The audio endpoint device has been unplugged, or the audio hardware or associated hardware resources have been reconfigured, disabled, removed, or otherwise made unavailable for use."); + return; + } + else if (r == AUDCLNT_E_DEVICE_IN_USE) + { + EHS_LOG_INT("Error", 9, "The endpoint device is already in use. Either the device is being used in exclusive mode, or the device is being used in shared mode and the caller asked to use the device in exclusive mode."); + return; + } + else if (r == AUDCLNT_E_ENDPOINT_CREATE_FAILED) + { + EHS_LOG_INT("Error", 10, "The method failed to create the audio endpoint for the render or the capture device. This can occur if the audio endpoint device has been unplugged, or the audio hardware or associated hardware resources have been reconfigured, disabled, removed, or otherwise made unavailable for use."); + return; + } + else if (r == AUDCLNT_E_INVALID_DEVICE_PERIOD) + { + EHS_LOG_INT("Error", 11, "Indicates that the device period requested by an exclusive-mode client is greater than 5000 milliseconds."); + return; + } + else if (r == AUDCLNT_E_UNSUPPORTED_FORMAT) + { + EHS_LOG_INT("Error", 12, "The audio engine (shared mode) or audio endpoint device (exclusive mode) does not support the specified format."); + return; + } + else if (r == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED) + { + EHS_LOG_INT("Error", 13, "The caller is requesting exclusive-mode use of the endpoint device, but the user has disabled exclusive-mode use of the device."); + return; + } + else if (r == AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL) + { + EHS_LOG_INT("Error", 14, "The AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag is set but parameters hnsBufferDuration and hnsPeriodicity are not equal."); + return; + } + else if (r == AUDCLNT_E_SERVICE_NOT_RUNNING) + { + EHS_LOG_INT("Error", 15, "The Windows audio service is not running."); + return; + } + else if (r == E_POINTER) + { + EHS_LOG_INT("Error", 16, "Parameter pFormat is NULL."); + return; + } + else if (r == E_INVALIDARG) + { + EHS_LOG_INT("Error", 17, "Parameter pFormat points to an invalid format description; or the AUDCLNT_STREAMFLAGS_LOOPBACK flag is set but ShareMode is not equal to AUDCLNT_SHAREMODE_SHARED; or the AUDCLNT_STREAMFLAGS_CROSSPROCESS flag is set but ShareMode is equal to AUDCLNT_SHAREMODE_EXCLUSIVE.\n" + "A prior call to SetClientProperties was made with an invalid category for audio/render streams."); + return; + } + else if (r == E_OUTOFMEMORY) + { + EHS_LOG_INT("Error", 18, "Out of memory."); + return; + } + else if (FAILED(r)) + { + EHS_LOG_INT("Error", 19, "Failed to initialize audio stream with error #" + Str_8::FromNum(r) + "."); + return; + } + + if (type == AudioDeviceType::OUTPUT) + { + r = client->GetService(__uuidof(IAudioRenderClient), (void**)&playbackClient); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 27, "Failed to retrieve audio render client with error #" + Str_8::FromNum(r) + "."); + return; + } + } + else if (type == AudioDeviceType::INPUT) + { + r = client->GetService(__uuidof(IAudioCaptureClient), (void**)&captureClient); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 28, "Failed to retrieve audio capture client with error #" + Str_8::FromNum(r) + "."); + return; + } + } + + r = client->GetBufferSize((UINT32*)&maxFrames); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 29, "Failed to retrieve buffer size with error #" + Str_8::FromNum(r) + "."); + return; + } + + r = client->Start(); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 30, "Failed to start audio with error #" + Str_8::FromNum(r) + "."); + return; + } + + streaming = true; + } + + void AudioDevice::CloseStream() + { + if (!streaming) + return; + + if (playbackClient) + { + playbackClient->Release(); + playbackClient = nullptr; + } + + if (captureClient) + { + captureClient->Release(); + captureClient = nullptr; + } + + if (hdl) + { + HRESULT r = client->Stop(); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 0, "Failed to stop audio with error #" + Str_8::FromNum(r) + "."); + return; + } + + client->Release(); + client = nullptr; + } + + streaming = false; + } + + UInt_64 AudioDevice::GetAvailFrames() const + { + if (!IsValid() || !streaming) + return 0; + + UInt_32 sampleSize = 0; + + if (playbackClient) + { + HRESULT r = client->GetCurrentPadding(&sampleSize); + if (r == AUDCLNT_E_DEVICE_INVALIDATED) + EHS_LOG_INT("Error", 0, "The audio device has been invalidated."); + else if (FAILED(r)) + EHS_LOG_INT("Error", 1, "Failed to retrieve samples padding with error #" + Str_8::FromNum(r) + "."); + + sampleSize = maxFrames - sampleSize; + } + else if (captureClient) + { + HRESULT r = captureClient->GetNextPacketSize(&sampleSize); + if (r == AUDCLNT_E_DEVICE_INVALIDATED) + EHS_LOG_INT("Error", 0, "The audio device has been invalidated."); + else if (FAILED(r)) + EHS_LOG_INT("Error", 1, "Failed to retrieve samples size with error #" + Str_8::FromNum(r) + "."); + } + + return sampleSize; + } + + Byte* AudioDevice::Map(UInt_64* offset, UInt_64* frames) + { + Byte* buffer = nullptr; + + if (!IsValid()) + return buffer; + + HRESULT r = client->GetCurrentPadding((UINT32*)&offset); + if (r == AUDCLNT_E_DEVICE_INVALIDATED) + EHS_LOG_INT("Error", 0, "The audio device has been invalidated."); + else if (FAILED(r)) + EHS_LOG_INT("Error", 1, "Failed to retrieve samples padding with error #" + Str_8::FromNum(r) + "."); + + if (playbackClient) + { + r = playbackClient->GetBuffer(*frames, &buffer); + if (r == AUDCLNT_E_BUFFER_TOO_LARGE) + EHS_LOG_INT("Error", 0, "The given frame size, " + Str_8::FromNum(*frames) + ", must be less than or equal to, " + Str_8::FromNum(maxFrames - *offset) + "."); + else if (r == AUDCLNT_E_DEVICE_INVALIDATED) + EHS_LOG_INT("Error", 1, "The audio device has been invalidated."); + else if (FAILED(r)) + EHS_LOG_INT("Error", 2, "Failed to retrieve buffer with error #" + Str_8::FromNum(r) + "."); + } + else if (captureClient) + { + UInt_32 flags = 0; + + r = captureClient->GetBuffer(&buffer, (UINT32*)frames, (DWORD*)&flags, nullptr, nullptr); + if (r == AUDCLNT_E_DEVICE_INVALIDATED) + EHS_LOG_INT("Error", 1, "The audio device has been invalidated."); + else if (FAILED(r)) + EHS_LOG_INT("Error", 2, "Failed to retrieve buffer with error #" + Str_8::FromNum(r) + "."); + } + + return buffer; + } + + void AudioDevice::UnMap(const UInt_64 offset, const UInt_64 frames) + { + if (!IsValid() || !streaming) + return; + + if (playbackClient) + { + HRESULT r = playbackClient->ReleaseBuffer(frames, 0); + if (r == AUDCLNT_E_DEVICE_INVALIDATED) + EHS_LOG_INT("Error", 0, "The audio device has been invalidated."); + else if (FAILED(r)) + EHS_LOG_INT("Error", 1, "Failed to release buffer with error #" + Str_8::FromNum(r) + "."); + } + else if (captureClient) + { + HRESULT r = captureClient->ReleaseBuffer(frames); + if (r == AUDCLNT_E_DEVICE_INVALIDATED) + EHS_LOG_INT("Error", 0, "The audio device has been invalidated."); + else if (FAILED(r)) + EHS_LOG_INT("Error", 1, "Failed to release buffer with error #" + Str_8::FromNum(r) + "."); + } + } + + Str_32 AudioDevice::GetInterfaceName_32() const + { + return UTF::To_32(GetInterfaceName_16()); + } + + Str_16 AudioDevice::GetInterfaceName_16() const + { + IPropertyStore* pProps = nullptr; + HRESULT r = hdl->OpenPropertyStore(STGM_READ, &pProps); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve interface name with error #" + Str_8::FromNum(r) + "."); + return nullptr; + } + + PROPVARIANT varName = {}; + PropVariantInit(&varName); + + pProps->GetValue(PKEY_DeviceInterface_FriendlyName, &varName); + + Str_16 name((wchar_t*)varName.pwszVal); + + PropVariantClear(&varName); + pProps->Release(); + + return name; + } + + Str_8 AudioDevice::GetInterfaceName_8() const + { + return UTF::To_8(GetInterfaceName_16()); + } + + Str_32 AudioDevice::GetName_32() const + { + return UTF::To_32(GetName_16()); + } + + Str_16 AudioDevice::GetName_16() const + { + IPropertyStore* pProps = nullptr; + HRESULT r = hdl->OpenPropertyStore(STGM_READ, &pProps); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve name with error #" + Str_8::FromNum(r) + "."); + return nullptr; + } + + PROPVARIANT varName = {}; + PropVariantInit(&varName); + + pProps->GetValue(PKEY_Device_FriendlyName, &varName); + + Str_16 name((wchar_t*)varName.pwszVal); + + PropVariantClear(&varName); + pProps->Release(); + + return name; + } + + Str_8 AudioDevice::GetName_8() const + { + return UTF::To_8(GetName_16()); + } + + bool AudioDevice::IsValid() const + { + return hdl; + } + + AudioDevice AudioDevice::GetDefault(const AudioDeviceType type) + { + AudioDevice result; + + HRESULT r = CoInitialize(nullptr); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 0, "Failed to initialize COM with error #" + Str_8::FromNum(r) + "."); + return result; + } + + IMMDeviceEnumerator* enumerator = nullptr; + r = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&enumerator); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 1, "Failed to initialize WASAPI with error #" + Str_8::FromNum(r) + "."); + return result; + } + + IMMDevice* deviceHdl = nullptr; + r = enumerator->GetDefaultAudioEndpoint((EDataFlow)type, eConsole, &deviceHdl); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 2, "Failed to retrieve default audio output device with error #" + Str_8::FromNum(r) + "."); + return result; + } + + result.hdl = deviceHdl; + result.type = type; + + enumerator->Release(); + + return result; + } + + Array AudioDevice::Get(const AudioDeviceType type, const AudioDeviceState state) + { + Array devices; + + HRESULT r = CoInitialize(nullptr); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 0, "Failed to initialize COM with error #" + Str_8::FromNum(r) + "."); + return devices; + } + + IMMDeviceEnumerator* enumerator = nullptr; + r = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&enumerator); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 1, "Failed to initialize WASAPI with error #" + Str_8::FromNum(r) + "."); + return devices; + } + + IMMDeviceCollection* collection = nullptr; + r = enumerator->EnumAudioEndpoints((EDataFlow)type, (DWORD)state, &collection); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 2, "Failed to retrieve audio device collection with error #" + Str_8::FromNum(r) + "."); + return devices; + } + + UInt_32 count = 0; + r = collection->GetCount((UINT*)&count); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 3, "Failed to retrieve audio device count with error #" + Str_8::FromNum(r) + "."); + return devices; + } + + devices.Resize(count); + for (UInt_32 i = 0; i < count; ++i) + { + IMMDevice* deviceHdl = nullptr; + r = collection->Item(i, &deviceHdl); + if (FAILED(r)) + { + EHS_LOG_INT("Error", 4, "Failed to retrieve audio device with error #" + Str_8::FromNum(r) + "."); + return devices; + } + + devices[i].hdl = deviceHdl; + devices[i].type = type; + } + + enumerator->Release(); + + return devices; + } +} \ No newline at end of file diff --git a/src/io/audio/BaseAudioDevice.cpp b/src/io/audio/BaseAudioDevice.cpp new file mode 100644 index 0000000..adc36ff --- /dev/null +++ b/src/io/audio/BaseAudioDevice.cpp @@ -0,0 +1,171 @@ +#include "ehs/io/audio/BaseAudioDevice.h" + +namespace ehs +{ + BaseAudioDevice::BaseAudioDevice() + : type(AudioDeviceType::ALL), dataType(DataType::SINT_8), bitDepth(0), sampleRate(0), channels(0), + period(20000), latency(150000), maxFrames(0), streaming(false) + { + } + + BaseAudioDevice::BaseAudioDevice(const BaseAudioDevice& device) + : type(device.type), dataType(device.dataType), bitDepth(device.bitDepth), sampleRate(device.sampleRate), + channels(device.channels), period(device.period), latency(device.latency), maxFrames(0), streaming(false) + { + } + + BaseAudioDevice& BaseAudioDevice::operator=(const BaseAudioDevice& device) + { + if (this == &device) + return *this; + + type = device.type; + dataType = device.dataType; + bitDepth = device.bitDepth; + sampleRate = device.sampleRate; + channels = device.channels; + period = device.period; + latency = device.latency; + maxFrames = device.maxFrames; + streaming = false; + + return *this; + } + + void BaseAudioDevice::Release() + { + } + + void BaseAudioDevice::OpenStream() + { + } + + void BaseAudioDevice::CloseStream() + { + } + + UInt_64 BaseAudioDevice::GetAvailFrames() const + { + return 0; + } + + Byte* BaseAudioDevice::Map(UInt_64* offset, UInt_64* frames) + { + return nullptr; + } + + void BaseAudioDevice::UnMap(const UInt_64 offset, const UInt_64 frames) + { + } + + AudioDeviceType BaseAudioDevice::GetType() const + { + return type; + } + + void BaseAudioDevice::SetDataType(const DataType newDataType) + { + if (streaming) + return; + + dataType = newDataType; + bitDepth = ToBitDepth(newDataType); + } + + DataType BaseAudioDevice::GetDataType() const + { + return dataType; + } + + UInt_8 BaseAudioDevice::GetByteDepth() const + { + return bitDepth / 8; + } + + UInt_16 BaseAudioDevice::GetBitDepth() const + { + return bitDepth; + } + + void BaseAudioDevice::SetSampleRate(const UInt_32 newSampleRate) + { + if (streaming) + return; + + sampleRate = newSampleRate; + } + + UInt_32 BaseAudioDevice::GetSampleRate() const + { + return sampleRate; + } + + void BaseAudioDevice::SetChannels(const UInt_32 newChannels) + { + if (streaming) + return; + + channels = newChannels; + } + + UInt_16 BaseAudioDevice::GetChannels() const + { + return channels; + } + + UInt_32 BaseAudioDevice::GetFrameSize() const + { + return bitDepth / 8 * channels; + } + + void BaseAudioDevice::SetPeriod(const UInt_32 newPeriod) + { + if (streaming) + return; + + period = newPeriod; + } + + UInt_32 BaseAudioDevice::GetPeriod() const + { + return period; + } + + void BaseAudioDevice::SetLatency(const UInt_32 newLatency) + { + if (streaming) + return; + + latency = newLatency; + } + + UInt_32 BaseAudioDevice::GetLatency() const + { + return latency; + } + + UInt_64 BaseAudioDevice::GetMaxFrames() const + { + return maxFrames; + } + + bool BaseAudioDevice::IsStreaming() const + { + return streaming; + } + + bool BaseAudioDevice::IsValid() const + { + return false; + } + + BaseAudioDevice BaseAudioDevice::GetDefault(const AudioDeviceType type) + { + return {}; + } + + Array BaseAudioDevice::Get(const AudioDeviceType type, const AudioDeviceState state) + { + return {}; + } +} \ No newline at end of file diff --git a/src/io/hid/Button.cpp b/src/io/hid/Button.cpp new file mode 100644 index 0000000..bf47426 --- /dev/null +++ b/src/io/hid/Button.cpp @@ -0,0 +1,50 @@ +#include "ehs/io/hid/Button.h" + +namespace ehs +{ + Button::Button() + : hash(0) + { + } + + Button::Button(const Str_8& name) + : name(name), hash(name.Hash_32()) + { + } + + Button::Button(const Button& key) + : name(key.name), hash(key.hash) + { + } + + Button& Button::operator=(const Button& key) + { + if (this == &key) + return *this; + + name = key.name; + hash = key.hash; + + return *this; + } + + bool Button::operator==(const Button& key) const + { + return key.hash == hash; + } + + bool Button::operator!=(const Button& key) const + { + return key.hash != hash; + } + + Str_8 Button::GetName() const + { + return name; + } + + UInt_32 Button::GetHash() const + { + return hash; + } +} \ No newline at end of file diff --git a/src/io/hid/ButtonState.cpp b/src/io/hid/ButtonState.cpp new file mode 100644 index 0000000..881afae --- /dev/null +++ b/src/io/hid/ButtonState.cpp @@ -0,0 +1,87 @@ +#include "ehs/io/hid/ButtonState.h" + +namespace ehs +{ + ButtonState::ButtonState() + : state(State::RELEASED), pressed(false), threshold(1.0f) + { + } + + ButtonState::ButtonState(const Button& button, const State state) + : button(button), state(state), pressed(true), threshold(1.0f) + { + } + + ButtonState::ButtonState(const ButtonState& bs) + : button(bs.button), state(bs.state), pressed(bs.pressed), threshold(bs.threshold) + { + } + + ButtonState& ButtonState::operator=(const ButtonState& bs) + { + if (this == &bs) + return *this; + + button = bs.button; + state = bs.state; + pressed = bs.pressed; + threshold = bs.threshold; + + return *this; + } + + bool ButtonState::operator==(const Button& other) const + { + return button == other; + } + + bool ButtonState::operator!=(const Button& other) const + { + return button != other; + } + + bool ButtonState::operator==(const State otherState) const + { + return state == otherState; + } + + bool ButtonState::operator!=(const State otherState) const + { + return state != otherState; + } + + Button ButtonState::GetButton() const + { + return button; + } + + void ButtonState::SetState(State newState) + { + state = newState; + } + + State ButtonState::GetState() const + { + return state; + } + + void ButtonState::SetPressed(bool value) + { + pressed = value; + } + + bool ButtonState::IsPressed() const + { + return pressed; + } + + void ButtonState::SetThreshold(const float newThreshold) + { + threshold = newThreshold; + } + + float ButtonState::GetThreshold() const + { + return 1.0f; + } +} \ No newline at end of file diff --git a/src/io/hid/HID.cpp b/src/io/hid/HID.cpp new file mode 100644 index 0000000..756f093 --- /dev/null +++ b/src/io/hid/HID.cpp @@ -0,0 +1,296 @@ +#include "ehs/io/hid/HID.h" + +namespace ehs +{ + HID::HID() + : type(EHS_HID_UNKNOWN), id(0) + { + } + + HID::HID(const UInt_8 type, Str_8 name, const UInt_64 id) + : type(type), name((Str_8&&)name), id(id) + { + } + + HID::HID(HID&& hid) noexcept + : type(hid.type), name((Str_8&&)hid.name), id(hid.id), states((Array&&)hid.states) + { + hid.type = EHS_HID_UNKNOWN; + hid.id = 0; + } + + HID::HID(const HID& hid) + : type(hid.type), name(hid.name), id(hid.id), states(hid.states) + { + } + + HID& HID::operator=(HID&& hid) noexcept + { + if (this == &hid) + return *this; + + type = hid.type; + name = (Str_8&&)hid.name; + id = hid.id; + states = (Array&&)hid.states; + + hid.type = EHS_HID_UNKNOWN; + hid.id = 0; + + return *this; + } + + HID& HID::operator=(const HID& hid) + { + if (this == &hid) + return *this; + + type = hid.type; + name = hid.name; + id = hid.id; + states = hid.states; + + return *this; + } + + bool HID::operator==(const HID& other) const + { + return type == other.type && hashName == other.hashName; + } + + bool HID::operator!=(const HID& other) const + { + return type != other.type || hashName != other.hashName; + } + + bool HID::operator==(const UInt_64 otherId) const + { + return id == otherId; + } + + bool HID::operator!=(const UInt_64 otherId) const + { + return id != otherId; + } + + void HID::Poll() + { + for (UInt_64 i = 0; i < states.Size(); ++i) + { + if (states[i].IsPressed()) + { + if (states[i].GetState() == State::RELEASED) + states[i].SetState(State::TOUCHED); + else + states[i].SetState(State::PRESSED); + } + else + { + if (states[i].GetState() == State::PRESSED || states[i].GetState() == State::TOUCHED) + states[i].SetState(State::JUST_RELEASED); + else + states[i].SetState(State::RELEASED); + } + } + } + + UInt_8 HID::GetType() const + { + return type; + } + + Str_8 HID::GetName() const + { + return name; + } + + UInt_64 HID::GetId() const + { + return id; + } + + void HID::ReleaseAll() + { + for (UInt_64 i = 0; i < states.Size(); ++i) + states[i].SetPressed(false); + } + + Vector HID::GetAllTouched() const + { + Vector result(0, states.Size() + 1); + + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::TOUCHED) + result.Push(&states[i]); + + return result; + } + + const ButtonState* HID::IsTouched(const Button& button) const + { + const ButtonState* state = GetState(button); + if (!state || *state != State::TOUCHED) + return nullptr; + + return state; + } + + const ButtonState* HID::IsTouched() const + { + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::TOUCHED) + return &states[i]; + + return nullptr; + } + + Vector HID::GetAllDown() const + { + Vector result(0, states.Size() + 1); + + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::PRESSED || states[i] == State::TOUCHED) + result.Push(&states[i]); + + return result; + } + + const ButtonState* HID::IsDown(const Button& button) const + { + const ButtonState* state = GetState(button); + if (!state || (*state != State::PRESSED && *state != State::TOUCHED)) + return nullptr; + + return state; + } + + const ButtonState* HID::IsDown() const + { + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::PRESSED || states[i] == State::TOUCHED) + return &states[i]; + + return nullptr; + } + + Vector HID::GetAllJustReleased() const + { + Vector result(0, states.Size() + 1); + + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::JUST_RELEASED) + result.Push(&states[i]); + + return result; + } + + const ButtonState* HID::IsJustReleased(const Button& button) const + { + const ButtonState* state = GetState(button); + if (!state || *state != State::JUST_RELEASED) + return nullptr; + + return state; + } + + const ButtonState* HID::IsJustReleased() const + { + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::JUST_RELEASED) + return &states[i]; + + return nullptr; + } + + Vector HID::GetAllUp() const + { + Vector result(0, states.Size() + 1); + + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::RELEASED || states[i] == State::JUST_RELEASED) + result.Push(&states[i]); + + return result; + } + + const ButtonState* HID::IsUp(const Button& button) const + { + const ButtonState* state = GetState(button); + if (!state || (*state != State::RELEASED && *state != State::JUST_RELEASED)) + return nullptr; + + return state; + } + + const ButtonState* HID::IsUp() const + { + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i] == State::RELEASED || states[i] == State::JUST_RELEASED) + return &states[i]; + + return nullptr; + } + + void HID::ButtonDown(const Button& button) + { + if (ButtonState* state = GetState(button); state) + state->SetPressed(true); + else + states.Push(ButtonState(button, State::RELEASED)); + } + + void HID::ButtonUp(const Button& button) + { + if (ButtonState* state = GetState(button); state) + state->SetPressed(false); + else + states.Push(ButtonState(button, State::JUST_RELEASED)); + } + + const ButtonState* HID::GetState(const Button& button) const + { + for (UInt_64 i = 0; i < states.Size(); ++i) + if (states[i] == button) + return &states[i]; + + return nullptr; + } + + bool HID::IsValid() const + { + return id; + } + + HID* HID::Clone() const + { + return new HID(*this); + } + + bool HID::HasState(const Button& button) const + { + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i].GetButton() == button) + return true; + + return false; + } + + bool HID::AddState(const ButtonState& state) + { + if (HasState(state.GetButton())) + return false; + + states.Push(state); + + return true; + } + + ButtonState* HID::GetState(const Button& button) + { + for (UInt_64 i = 0; i < states.Size(); i++) + if (states[i].GetButton() == button) + return &states[i]; + + return nullptr; + } +} \ No newline at end of file diff --git a/src/io/hid/Input.cpp b/src/io/hid/Input.cpp new file mode 100644 index 0000000..d55106c --- /dev/null +++ b/src/io/hid/Input.cpp @@ -0,0 +1,160 @@ +#include "ehs/io/hid/Input.h" + +namespace ehs +{ + Input::~Input() + { + for (UInt_64 i = 0; i < handlers.Size(); i++) + delete handlers[i]; + } + + Input::Input() + : initalized(false) + { + } + + Input::Input(Input&& input) noexcept + : handlers((Array&&)input.handlers), initalized(input.initalized) + { + input.initalized = false; + } + + Input::Input(const Input& input) + : initalized(false) + { + } + + Input& Input::operator=(Input&& input) noexcept + { + if (this == &input) + return *this; + + handlers = (Array&&)input.handlers; + initalized = input.initalized; + + input.initalized = false; + + return *this; + } + + Input& Input::operator=(const Input& input) + { + if (this == &input) + return *this; + + for (UInt_64 i = 0; i < handlers.Size(); i++) + delete handlers; + + handlers = Array(); + initalized = false; + + return *this; + } + + void Input::Initialize() + { + if (initalized) + return; + + UInt_64 i = 0; + while (i < handlers.Size()) + { + if (!handlers[i]->Initialize()) + { + if (i != handlers.Size() - 1) + handlers.Swap(i, handlers.End()); + + delete handlers.Pop(); + + continue; + } + + i++; + } + + initalized = true; + } + + void Input::Release() + { + if (!initalized) + return; + + UInt_64 i = 0; + while (i < handlers.Size()) + { + if (!handlers[i]->Release()) + { + if (i != handlers.Size() - 1) + handlers.Swap(i, handlers.End()); + + delete handlers.Pop(); + + continue; + } + + i++; + } + + initalized = false; + } + + void Input::Poll() + { + for (UInt_64 i = 0; i < handlers.Size(); i++) + handlers[i]->Poll(); + } + + bool Input::HasHandler(const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < handlers.Size(); i++) + if (*handlers[i] == hashId) + return true; + + return false; + } + + bool Input::HasHandler(const Str_8& id) const + { + return HasHandler(id.Hash_64()); + } + + bool Input::AddHandler(InputHandler* handler) + { + if (HasHandler(handler->GetHashId())) + return false; + + if (initalized) + { + bool hInitialized = handler->Initialize(); + if (!hInitialized) + { + delete handler; + return false; + } + } + + handlers.Push(handler); + + return true; + } + + const InputHandler* Input::GetHandler(const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < handlers.Size(); i++) + if (*handlers[i] == hashId) + return handlers[i]; + + return nullptr; + } + + const InputHandler* Input::GetHandler(const Str_8& id) const + { + return GetHandler(id.Hash_64()); + } + + bool Input::IsInitialized() const + { + return initalized; + } +} \ No newline at end of file diff --git a/src/io/hid/InputHandler.cpp b/src/io/hid/InputHandler.cpp new file mode 100644 index 0000000..4b52eda --- /dev/null +++ b/src/io/hid/InputHandler.cpp @@ -0,0 +1,160 @@ +#include "ehs/io/hid/InputHandler.h" + +namespace ehs +{ + InputHandler::~InputHandler() + { + for (UInt_64 i = 0; i < devices.Size(); i++) + delete devices[i]; + } + + InputHandler::InputHandler() + : hashId(0) + { + } + + InputHandler::InputHandler(Str_8 id) + : hashId(id.Hash_64()), id((Str_8&&)id) + { + } + + InputHandler::InputHandler(InputHandler&& ih) noexcept + : hashId(ih.hashId), id((Str_8&&)ih.id), devices((Array&&)ih.devices) + { + ih.hashId = 0; + } + + InputHandler::InputHandler(const InputHandler& ih) + : hashId(ih.hashId), id(ih.id), devices(ih.devices.Size()) + { + for (UInt_64 i = 0; i < devices.Size(); i++) + devices[i] = ih.devices[i]->Clone(); + } + + InputHandler& InputHandler::operator=(InputHandler&& ih) noexcept + { + if (this == &ih) + return *this; + + hashId = ih.hashId; + id = (Str_8&&)ih.id; + devices = (Array&&)ih.devices; + + ih.hashId = 0; + + return *this; + } + + InputHandler& InputHandler::operator=(const InputHandler& ih) + { + if (this == &ih) + return *this; + + for (UInt_64 i = 0; i < devices.Size(); i++) + delete devices[i]; + + hashId = ih.hashId; + id = ih.id; + devices = Array(ih.devices.Size()); + + for (UInt_64 i = 0; i < devices.Size(); i++) + devices[i] = ih.devices[i]->Clone(); + + return *this; + } + + bool InputHandler::operator==(const UInt_64 otherHashId) + { + return hashId == otherHashId; + } + + bool InputHandler::operator!=(const UInt_64 otherHashId) + { + return hashId != otherHashId; + } + + bool InputHandler::Initialize() + { + if (IsInitialized()) + return false; + + return true; + } + + bool InputHandler::Release() + { + if (!IsInitialized()) + return false; + + for (UInt_64 i = 0; i < devices.Size(); i++) + delete devices[i]; + + devices.Clear(); + + return true; + } + + void InputHandler::Poll() + { + for (UInt_64 i = 0; i < devices.Size(); i++) + devices[i]->Poll(); + } + + UInt_64 InputHandler::GetHashId() const + { + return hashId; + } + + Str_8 InputHandler::GetId() const + { + return id; + } + + void InputHandler::ResetAllStates() + { + for (UInt_64 i = 0; i < devices.Size(); i++) + devices[i]->ReleaseAll(); + } + + bool InputHandler::HasDevice(const UInt_64 id) const + { + for (UInt_64 i = 0; i < devices.Size(); i++) + if (*devices[i] == id) + return true; + + return false; + } + + bool InputHandler::AddDevice(HID* device) + { + if (HasDevice(device->GetId())) + return false; + + devices.Push(device); + + return true; + } + + HID* InputHandler::GetDevice(const UInt_64 id) const + { + for (UInt_64 i = 0; i < devices.Size(); i++) + if (*devices[i] == id) + return devices[i]; + + return nullptr; + } + + HID* InputHandler::GetDeviceByType(const UInt_8 type) const + { + for (UInt_64 i = 0; i < devices.Size(); i++) + if (devices[i]->GetType() == type) + return devices[i]; + + return nullptr; + } + + bool InputHandler::IsInitialized() const + { + return false; + } +} \ No newline at end of file diff --git a/src/io/hid/Keyboard.cpp b/src/io/hid/Keyboard.cpp new file mode 100644 index 0000000..125400c --- /dev/null +++ b/src/io/hid/Keyboard.cpp @@ -0,0 +1,507 @@ +#include "ehs/io/hid/Keyboard.h" + +namespace ehs +{ + Keyboard::Keyboard() + { + } + + Keyboard::Keyboard(Str_8 name, const UInt_64 id) + : HID(EHS_HID_KEYBOARD, (Str_8&&)name, id) + { + } + + Keyboard::Keyboard(const Keyboard& hid) + : HID(hid) + { + } + + Keyboard& Keyboard::operator=(const Keyboard& hid) + { + if (this == &hid) + return *this; + + HID::operator=(hid); + + return *this; + } + + void Keyboard::Poll() + { + HID::Poll(); + } + + Keyboard* Keyboard::Clone() const + { + return new Keyboard(*this); + } + + const Button Keyboard::Unknown("Unknown"); + const Button Keyboard::Escape("Escape Button"); + const Button Keyboard::Backspace("Backspace Button"); + const Button Keyboard::Enter("Enter Button"); + const Button Keyboard::LShift("Left Shift Button"); + const Button Keyboard::RShift("Right Shift Button"); + const Button Keyboard::LAlt("Left Alt Button"); + const Button Keyboard::RAlt("Right Alt Button"); + const Button Keyboard::LCtrl("Left Control Button"); + const Button Keyboard::RCtrl("Right Control Button"); + const Button Keyboard::Space("Space Button"); + const Button Keyboard::A("A Button"); + const Button Keyboard::B("B Button"); + const Button Keyboard::C("C Button"); + const Button Keyboard::D("D Button"); + const Button Keyboard::E("E Button"); + const Button Keyboard::F("F Button"); + const Button Keyboard::G("G Button"); + const Button Keyboard::H("H Button"); + const Button Keyboard::I("I Button"); + const Button Keyboard::J("J Button"); + const Button Keyboard::K("K Button"); + const Button Keyboard::L("L Button"); + const Button Keyboard::M("M Button"); + const Button Keyboard::N("N Button"); + const Button Keyboard::O("O Button"); + const Button Keyboard::P("P Button"); + const Button Keyboard::Q("Q Button"); + const Button Keyboard::R("R Button"); + const Button Keyboard::S("S Button"); + const Button Keyboard::T("T Button"); + const Button Keyboard::U("U Button"); + const Button Keyboard::V("V Button"); + const Button Keyboard::W("W Button"); + const Button Keyboard::X("X Button"); + const Button Keyboard::Y("Y Button"); + const Button Keyboard::Z("Z Button"); + const Button Keyboard::One("One Button"); + const Button Keyboard::Two("Two Button"); + const Button Keyboard::Three("Three Button"); + const Button Keyboard::Four("Four Button"); + const Button Keyboard::Five("Five Button"); + const Button Keyboard::Six("Six Button"); + const Button Keyboard::Seven("Seven Button"); + const Button Keyboard::Eight("Eight Button"); + const Button Keyboard::Nine("Nine Button"); + const Button Keyboard::Zero("Zero Button"); + const Button Keyboard::Equals("Equals Button"); + const Button Keyboard::Minus("Minus Button"); + const Button Keyboard::Tilde("Tilde Button"); + const Button Keyboard::BackSlash("Back Slash Button"); + const Button Keyboard::LeftSquareBracket("Left Square Bracket Button"); + const Button Keyboard::RightSquareBracket("Right Square Bracket Button"); + const Button Keyboard::SemiColon("Semi-Colon Button"); + const Button Keyboard::Apostrophe("Apostrophe Button"); + const Button Keyboard::Comma("Comma Button"); + const Button Keyboard::Period("Period Button"); + const Button Keyboard::ForwardSlash("Forward Slash Button"); + const Button Keyboard::F1("Function 1 Button"); + const Button Keyboard::F2("Function 2 Button"); + const Button Keyboard::F3("Function 3 Button"); + const Button Keyboard::F4("Function 4 Button"); + const Button Keyboard::F5("Function 5 Button"); + const Button Keyboard::F6("Function 6 Button"); + const Button Keyboard::F7("Function 7 Button"); + const Button Keyboard::F8("Function 8 Button"); + const Button Keyboard::F9("Function 9 Button"); + const Button Keyboard::F10("Function 10 Button"); + const Button Keyboard::F11("Function 11 Button"); + const Button Keyboard::F12("Function 12 Button"); + const Button Keyboard::F13("Function 13 Button"); + const Button Keyboard::F14("Function 14 Button"); + const Button Keyboard::F15("Function 15 Button"); + const Button Keyboard::F16("Function 16 Button"); + const Button Keyboard::F17("Function 17 Button"); + const Button Keyboard::F18("Function 18 Button"); + const Button Keyboard::F19("Function 19 Button"); + const Button Keyboard::F20("Function 20 Button"); + const Button Keyboard::F21("Function 21 Button"); + const Button Keyboard::F22("Function 22 Button"); + const Button Keyboard::F23("Function 23 Button"); + const Button Keyboard::F24("Function 24 Button"); + const Button Keyboard::Left("Left Arrow Button"); + const Button Keyboard::Right("Right Arrow Button"); + const Button Keyboard::Up("Up Arrow Button"); + const Button Keyboard::Down("Down Arrow Button"); + + Button Keyboard::TranslateScanCode(const UInt_32 code) + { + switch (code) + { + case 1: + return Keyboard::Escape; + case 14: + return Keyboard::Backspace; + case 28: + return Keyboard::Enter; + case 0x2A: + return Keyboard::LShift; + case 0x36: + return Keyboard::RShift; + case 56: + return Keyboard::LAlt; + case 100: + return Keyboard::RAlt; + case 29: + return Keyboard::LCtrl; + case 97: + return Keyboard::RCtrl; + case 57: + return Keyboard::Space; + case 30: + return Keyboard::A; + case 48: + return Keyboard::B; + case 46: + return Keyboard::C; + case 32: + return Keyboard::D; + case 18: + return Keyboard::E; + case 33: + return Keyboard::F; + case 34: + return Keyboard::G; + case 35: + return Keyboard::H; + case 23: + return Keyboard::I; + case 36: + return Keyboard::J; + case 37: + return Keyboard::K; + case 38: + return Keyboard::L; + case 50: + return Keyboard::M; + case 49: + return Keyboard::N; + case 24: + return Keyboard::O; + case 25: + return Keyboard::P; + case 16: + return Keyboard::Q; + case 19: + return Keyboard::R; + case 31: + return Keyboard::S; + case 20: + return Keyboard::T; + case 22: + return Keyboard::U; + case 47: + return Keyboard::V; + case 17: + return Keyboard::W; + case 45: + return Keyboard::X; + case 21: + return Keyboard::Y; + case 44: + return Keyboard::Z; + case 2: + return Keyboard::One; + case 3: + return Keyboard::Two; + case 4: + return Keyboard::Three; + case 5: + return Keyboard::Four; + case 6: + return Keyboard::Five; + case 7: + return Keyboard::Six; + case 8: + return Keyboard::Seven; + case 9: + return Keyboard::Eight; + case 10: + return Keyboard::Nine; + case 11: + return Keyboard::Zero; + case 12: + return Keyboard::Minus; + case 13: + return Keyboard::Equals; + case 41: + return Keyboard::Tilde; + case 43: + return Keyboard::BackSlash; + case 26: + return Keyboard::LeftSquareBracket; + case 27: + return Keyboard::RightSquareBracket; + case 39: + return Keyboard::SemiColon; + case 40: + return Keyboard::Apostrophe; + case 51: + return Keyboard::Comma; + case 52: + return Keyboard::Period; + case 53: + return Keyboard::ForwardSlash; + case 59: + return Keyboard::F1; + case 60: + return Keyboard::F2; + case 61: + return Keyboard::F3; + case 62: + return Keyboard::F4; + case 63: + return Keyboard::F5; + case 64: + return Keyboard::F6; + case 65: + return Keyboard::F7; + case 66: + return Keyboard::F8; + case 67: + return Keyboard::F9; + case 68: + return Keyboard::F10; + case 69: + return Keyboard::F11; + case 70: + return Keyboard::F12; + case 71: + return Keyboard::F13; + case 72: + return Keyboard::F14; + case 73: + return Keyboard::F15; + case 74: + return Keyboard::F16; + case 75: + return Keyboard::F17; + case 76: + return Keyboard::F18; + case 77: + return Keyboard::F19; + case 78: + return Keyboard::F20; + case 79: + return Keyboard::F21; + case 80: + return Keyboard::F22; + case 81: + return Keyboard::F23; + case 82: + return Keyboard::F24; + case 105: + return Keyboard::Left; + case 106: + return Keyboard::Right; + case 103: + return Keyboard::Up; + case 108: + return Keyboard::Down; + default: + return Keyboard::Unknown; + } + } + + Char_8 Keyboard::TranslateToEnglish_8(const bool shifted, const Button& button) + { + if (shifted) + { + if (button == A) + return 'A'; + else if (button == B) + return 'B'; + else if (button == C) + return 'C'; + else if (button == D) + return 'D'; + else if (button == E) + return 'E'; + else if (button == F) + return 'F'; + else if (button == G) + return 'G'; + else if (button == H) + return 'H'; + else if (button == I) + return 'I'; + else if (button == J) + return 'J'; + else if (button == K) + return 'K'; + else if (button == L) + return 'L'; + else if (button == M) + return 'M'; + else if (button == N) + return 'N'; + else if (button == O) + return 'O'; + else if (button == P) + return 'P'; + else if (button == Q) + return 'Q'; + else if (button == Q) + return 'R'; + else if (button == S) + return 'S'; + else if (button == T) + return 'T'; + else if (button == U) + return 'U'; + else if (button == V) + return 'V'; + else if (button == W) + return 'W'; + else if (button == X) + return 'X'; + else if (button == Y) + return 'Y'; + else if (button == Z) + return 'Z'; + else if (button == Two) + return '@'; + else if (button == Three) + return '#'; + else if (button == Four) + return '$'; + else if (button == Five) + return '%'; + else if (button == Six) + return '^'; + else if (button == Seven) + return '&'; + else if (button == Eight) + return '*'; + else if (button == Nine) + return '('; + else if (button == Zero) + return ')'; + else if (button == One) + return '!'; + else if (button == Minus) + return '_'; + else if (button == Equals) + return '+'; + else if (button == Tilde) + return '~'; + else if (button == BackSlash) + return '|'; + else if (button == LeftSquareBracket) + return '{'; + else if (button == RightSquareBracket) + return '}'; + else if (button == SemiColon) + return ':'; + else if (button == Apostrophe) + return '"'; + else if (button == Comma) + return '<'; + else if (button == Period) + return '>'; + else if (button == ForwardSlash) + return '?'; + } + else + { + if (button == A) + return 'a'; + else if (button == B) + return 'b'; + else if (button == C) + return 'c'; + else if (button == D) + return 'd'; + else if (button == E) + return 'e'; + else if (button == F) + return 'f'; + else if (button == G) + return 'g'; + else if (button == H) + return 'h'; + else if (button == I) + return 'i'; + else if (button == J) + return 'j'; + else if (button == K) + return 'k'; + else if (button == L) + return 'l'; + else if (button == M) + return 'm'; + else if (button == N) + return 'n'; + else if (button == O) + return 'o'; + else if (button == P) + return 'p'; + else if (button == Q) + return 'q'; + else if (button == R) + return 'r'; + else if (button == S) + return 's'; + else if (button == T) + return 't'; + else if (button == U) + return 'u'; + else if (button == V) + return 'v'; + else if (button == W) + return 'w'; + else if (button == X) + return 'x'; + else if (button == Y) + return 'y'; + else if (button == Z) + return 'z'; + else if (button == One) + return '1'; + else if (button == Two) + return '2'; + else if (button == Three) + return '3'; + else if (button == Four) + return '4'; + else if (button == Five) + return '5'; + else if (button == Six) + return '6'; + else if (button == Seven) + return '7'; + else if (button == Eight) + return '8'; + else if (button == Nine) + return '9'; + else if (button == Zero) + return '0'; + else if (button == Minus) + return '-'; + else if (button == Equals) + return '='; + else if (button == Tilde) + return '`'; + else if (button == BackSlash) + return '\\'; + else if (button == LeftSquareBracket) + return '['; + else if (button == RightSquareBracket) + return ']'; + else if (button == SemiColon) + return ';'; + else if (button == Apostrophe) + return '\''; + else if (button == Comma) + return ','; + else if (button == Period) + return '.'; + else if (button == ForwardSlash) + return '/'; + } + + if (button == Space) + return ' '; + else + return 0x00; + } +} diff --git a/src/io/hid/Mouse.cpp b/src/io/hid/Mouse.cpp new file mode 100644 index 0000000..d983c55 --- /dev/null +++ b/src/io/hid/Mouse.cpp @@ -0,0 +1,88 @@ +#include "ehs/io/hid/Mouse.h" + +namespace ehs +{ + Mouse::Mouse() + { + } + + Mouse::Mouse(Str_8 name, const UInt_64 id) + : HID(EHS_HID_MOUSE, (Str_8&&)name, id) + { + } + + Mouse::Mouse(const Mouse& hid) + : HID(hid), delta(hid.delta) + { + } + + Mouse& Mouse::operator=(const Mouse& hid) + { + if (this == &hid) + return *this; + + HID::operator=(hid); + + delta = hid.delta; + + return *this; + } + + void Mouse::Poll() + { + delta = {}; + + HID::Poll(); + } + + void Mouse::SetDelta(const Vec2_s32& newDelta) + { + delta = newDelta; + } + + Vec2_s32 Mouse::GetDelta() const + { + return delta; + } + + Mouse* Mouse::Clone() const + { + return new Mouse(*this); + } + + const Button Mouse::Unknown("Unknown"); + const Button Mouse::LMB("Left Mouse Button"); + const Button Mouse::MMB("Middle Mouse Button"); + const Button Mouse::RMB("Right Mouse Button"); + const Button Mouse::Four("Mouse Button Four"); + const Button Mouse::Five("Mouse Button Five"); + const Button Mouse::ScrollUp("Scroll Up"); + const Button Mouse::ScrollDown("Scroll Down"); + const Button Mouse::ScrollLeft("Scroll Left"); + const Button Mouse::ScrollRight("Scroll Right"); + const Button Mouse::Back("Back Mouse Button"); + const Button Mouse::Forward("Forward Mouse Button"); + + Button Mouse::TranslateXCB(const UInt_32 code) + { + switch (code) + { + case 1: + return Mouse::LMB; + case 2: + return Mouse::MMB; + case 3: + return Mouse::RMB; + case 4: + return Mouse::ScrollUp; + case 5: + return Mouse::ScrollDown; + case 8: + return Mouse::Back; + case 9: + return Mouse::Forward; + default: + return Mouse::Unknown; + } + } +} \ No newline at end of file diff --git a/src/io/img/Img.cpp b/src/io/img/Img.cpp new file mode 100644 index 0000000..df30c9d --- /dev/null +++ b/src/io/img/Img.cpp @@ -0,0 +1,1488 @@ +#include "ehs/io/img/Img.h" + +namespace ehs +{ + Array Img::codecs; + + bool Img::HasCodec(const UInt_64 hashExt) + { + for (UInt_64 i = 0; i < codecs.Size(); ++i) + if (codecs[i].GetHashExt() == hashExt) + return true; + + return false; + } + + bool Img::HasCodec(const Str_8& ext) + { + return HasCodec(ext.Hash_64()); + } + + bool Img::AddCodec(ImgCodec codec) + { + if (HasCodec(codec.GetHashExt())) + return false; + + codecs.Push(std::move(codec)); + + return true; + } + + const ImgCodec* Img::GetCodec(const UInt_64 hashExt) + { + for (UInt_64 i = 0; i < codecs.Size(); ++i) + if (codecs[i].GetHashExt() == hashExt) + return &codecs[i]; + + return nullptr; + } + + const ImgCodec* Img::GetCodec(const Str_8& ext) + { + return GetCodec(ext.Hash_64()); + } + + Img::~Img() + { + delete[] data; + } + + Img::Img() + : hashId(0), data(nullptr), byteDepth(0), channels(0), size(0) + { + AddType("Img"); + } + + Img::Img(Str_8 id, const UInt_8 byteDepth, const UInt_8 channels, const Vec2_u64& resolution, const Byte* const data) + : hashId(id.Hash_64()), id((Str_8&&)id), byteDepth(byteDepth), channels(channels), resolution(resolution), + size(resolution.x * byteDepth * channels * resolution.y), data(new Byte[size]) + { + Util::Copy(this->data, data, size); + + AddType("Img"); + } + + Img::Img(Str_8 id, const UInt_8 byteDepth, const UInt_8 channels, const Vec2_u64& resolution) + : hashId(id.Hash_64()), id((Str_8&&)id), byteDepth(byteDepth), channels(channels), resolution(resolution), + size(resolution.x * byteDepth * channels * resolution.y), data(new Byte[size]) + { + AddType("Img"); + } + + Img::Img(Str_8 id) + : hashId(id.Hash_64()), id((Str_8&&)id), byteDepth(0), channels(0), size(0), data(nullptr) + { + AddType("Img"); + } + + Img::Img(Img&& img) noexcept + : BaseObj((BaseObj&&)img), hashId(img.hashId), id((Str_8&&)img.id), byteDepth(img.byteDepth), + channels(img.channels), resolution(img.resolution), size(img.size), data(img.data) + { + img.byteDepth = 0; + img.channels = 0; + img.resolution = {}; + img.size = 0; + img.data = nullptr; + } + + Img::Img(const Img& img) + : BaseObj(img), hashId(img.hashId), id(img.id), byteDepth(img.byteDepth), channels(img.channels), + resolution(img.resolution), size(img.size), data(new Byte[img.size]) + { + Util::Copy(data, img.data, img.size); + } + + Img& Img::operator=(Img&& img) noexcept + { + if (this == &img) + return *this; + + BaseObj::operator=((BaseObj&&)img); + + byteDepth = img.byteDepth; + channels = img.channels; + resolution = img.resolution; + size = img.size; + delete[] data; + data = img.data; + + img.byteDepth = 0; + img.channels = 0; + img.resolution = {}; + img.size = 0; + img.data = nullptr; + + return *this; + } + + Img& Img::operator=(const Img& img) + { + if (this == &img) + return *this; + + BaseObj::operator=(img); + + byteDepth = img.byteDepth; + channels = img.channels; + resolution = img.resolution; + size = img.size; + delete[] data; + data = new Byte[img.size]; + Util::Copy(data, img.data, img.size); + + return *this; + } + + Img::operator const Byte* () const + { + return data; + } + + Img::operator Byte* () + { + return data; + } + + void Img::Release() + { + byteDepth = 0; + channels = 0; + resolution = {}; + size = 0; + delete[] data; + data = nullptr; + } + + UInt_64 Img::GetHashId() const + { + return hashId; + } + + void Img::SetId(Str_8 newId) + { + hashId = newId.Hash_64(); + id = (Str_8&&)newId; + } + + Str_8 Img::GetId() const + { + return id; + } + + UInt_8 Img::GetByteDepth() const + { + return byteDepth; + } + + UInt_8 Img::GetBitDepth() const + { + return byteDepth * 8; + } + + UInt_8 Img::GetChannels() const + { + return channels; + } + + Vec2_u64 Img::GetResolution() const + { + return resolution; + } + + UInt_64 Img::GetSize() const + { + return size; + } + + void Img::SetPixel(const UInt_64 index, const Byte* const pixel) + { + UInt_64 rIndex = index * byteDepth * channels; + + for (UInt_64 i = 0; i < byteDepth * channels; ++i) + data[rIndex + i] = pixel[i]; + } + + void Img::GetPixel(const UInt_64 index, Byte* const pixel) const + { + UInt_64 rIndex = index * byteDepth * channels; + + for (UInt_64 i = 0; i < byteDepth * channels; ++i) + pixel[i] = data[rIndex + i]; + } + + void Img::SetPixel(const UInt_64 x, const UInt_64 y, const Byte* const pixel) + { + UInt_64 rIndex = (y * resolution.x * byteDepth * channels) + (x * byteDepth * channels); + + for (UInt_64 i = 0; i < byteDepth * channels; ++i) + data[rIndex + i] = pixel[i]; + } + + void Img::GetPixel(const UInt_64 x, const UInt_64 y, Byte* const pixel) const + { + UInt_64 rIndex = (y * resolution.x * byteDepth * channels) + (x * byteDepth * channels); + + for (UInt_64 i = 0; i < byteDepth * channels; ++i) + pixel[i] = data[rIndex + i]; + } + + void Img::Resize(const Resampling method, const Vec2_u64& newResolution) + { + switch (method) + { + case Resampling::NEAREST_NEIGHBOR: + { + NearestNeighbor(newResolution); + return; + } + default: + return; + } + } + + Img Img::GetResized(const Resampling method, const Vec2_u64& newResolution) const + { + switch (method) + { + case Resampling::NEAREST_NEIGHBOR: + return GetNearestNeighbor(newResolution); + default: + return {}; + } + } + + void Img::ToRGBA() + { + switch (channels) + { + case 3: + { + size = resolution.x * resolution.y * byteDepth * 4; + Byte* result = new Byte[size]; + RGB_To_RGBA(size, result); + channels = 4; + delete[] data; + data = result; + break; + } + case 2: + { + size = resolution.x * resolution.y * byteDepth * 4; + Byte* result = new Byte[size]; + MonoA_To_RGBA(size, result); + channels = 4; + delete[] data; + data = result; + break; + } + case 1: + { + size = resolution.x * resolution.y * byteDepth * 4; + Byte* result = new Byte[size]; + Mono_To_RGBA(size, result); + channels = 4; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAsRGBA() const + { + switch (channels) + { + case 4: + { + return {*this}; + } + case 3: + { + Img result(id, byteDepth, 4, resolution); + RGB_To_RGBA(result.GetSize(), result); + return result; + } + case 2: + { + Img result(id, byteDepth, 4, resolution); + MonoA_To_RGBA(result.GetSize(), result); + return result; + } + case 1: + { + Img result(id, byteDepth, 4, resolution); + Mono_To_RGBA(result.GetSize(), result); + return result; + } + default: + { + return {}; + } + } + } + + void Img::ToRGB() + { + switch (channels) + { + case 4: + { + size = resolution.x * resolution.y * byteDepth * 3; + Byte* result = new Byte[size]; + RGBA_To_RGB(size, result); + channels = 3; + delete[] data; + data = result; + break; + } + case 2: + { + size = resolution.x * resolution.y * byteDepth * 3; + Byte* result = new Byte[size]; + MonoA_To_RGB(size, result); + channels = 3; + delete[] data; + data = result; + break; + } + case 1: + { + size = resolution.x * resolution.y * byteDepth * 3; + Byte* result = new Byte[size]; + Mono_To_RGB(size, result); + channels = 3; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAsRGB() const + { + switch (channels) + { + case 4: + { + Img result(id, byteDepth, 3, resolution); + RGBA_To_RGB(result.GetSize(), result); + return result; + } + case 3: + { + return {*this}; + } + case 2: + { + Img result(id, byteDepth, 3, resolution); + MonoA_To_RGB(result.GetSize(), result); + return result; + } + case 1: + { + Img result(id, byteDepth, 3, resolution); + Mono_To_RGB(result.GetSize(), result); + return result; + } + default: + { + return {}; + } + } + } + + void Img::ToMonoA() + { + switch (channels) + { + case 4: + { + size = resolution.x * resolution.y * byteDepth * 2; + Byte* result = new Byte[size]; + RGBA_To_MonoA(size, result); + channels = 2; + delete[] data; + data = result; + break; + } + case 3: + { + size = resolution.x * resolution.y * byteDepth * 2; + Byte* result = new Byte[size]; + RGB_To_MonoA(size, result); + channels = 2; + delete[] data; + data = result; + break; + } + case 1: + { + size = resolution.x * resolution.y * byteDepth * 2; + Byte* result = new Byte[size]; + Mono_To_MonoA(size, result); + channels = 2; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAsMonoA() const + { + switch (channels) + { + case 4: + { + Img result(id, byteDepth, 2, resolution); + RGBA_To_MonoA(result.GetSize(), result); + return result; + } + case 3: + { + Img result(id, byteDepth, 2, resolution); + RGB_To_MonoA(result.GetSize(), result); + return result; + } + case 2: + { + return {*this}; + } + case 1: + { + Img result(id, byteDepth, 2, resolution); + Mono_To_MonoA(result.GetSize(), result); + return result; + } + default: + { + return {}; + } + } + } + + void Img::ToMono() + { + switch (channels) + { + case 4: + { + size = resolution.x * resolution.y * byteDepth; + Byte* result = new Byte[size]; + RGBA_To_Mono(size, result); + channels = 1; + delete[] data; + data = result; + break; + } + case 3: + { + size = resolution.x * resolution.y * byteDepth; + Byte* result = new Byte[size]; + RGB_To_Mono(size, result); + channels = 1; + delete[] data; + data = result; + break; + } + case 2: + { + size = resolution.x * resolution.y * byteDepth; + Byte* result = new Byte[size]; + MonoA_To_Mono(size, result); + channels = 1; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAsMono() const + { + switch (channels) + { + case 4: + { + Img result(id, byteDepth, 1, resolution); + RGBA_To_Mono(result.GetSize(), result); + return result; + } + case 3: + { + Img result(id, byteDepth, 1, resolution); + RGB_To_Mono(result.GetSize(), result); + return result; + } + case 2: + { + Img result(id, byteDepth, 1, resolution); + MonoA_To_Mono(result.GetSize(), result); + return result; + } + case 1: + { + return {*this}; + } + default: + { + return {}; + } + } + } + + void Img::To32() + { + switch (byteDepth) + { + case 3: + { + size = resolution.x * resolution.y * 4 * channels; + Byte* result = new Byte[size]; + BD24_to_BD32(size, result); + byteDepth = 4; + delete[] data; + data = result; + break; + } + case 2: + { + size = resolution.x * resolution.y * 4 * channels; + Byte* result = new Byte[size]; + BD16_to_BD32(size, result); + byteDepth = 4; + delete[] data; + data = result; + break; + } + case 1: + { + size = resolution.x * resolution.y * 4 * channels; + Byte* result = new Byte[size]; + BD8_to_BD32(size, result); + byteDepth = 4; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAs32() const + { + switch (byteDepth) + { + case 4: + { + return {*this}; + } + case 3: + { + Img result(id, 4, channels, resolution); + BD24_to_BD32(result.GetSize(), result); + return result; + } + case 2: + { + Img result(id, 4, channels, resolution); + BD16_to_BD32(result.GetSize(), result); + return result; + } + case 1: + { + Img result(id, 4, channels, resolution); + BD8_to_BD32(result.GetSize(), result); + return result; + } + default: + { + return {}; + } + } + } + + void Img::To24() + { + switch (byteDepth) + { + case 4: + { + size = resolution.x * resolution.y * 3 * channels; + Byte* result = new Byte[size]; + BD32_to_BD24(size, result); + byteDepth = 3; + delete[] data; + data = result; + break; + } + case 2: + { + size = resolution.x * resolution.y * 3 * channels; + Byte* result = new Byte[size]; + BD16_to_BD24(size, result); + byteDepth = 3; + delete[] data; + data = result; + break; + } + case 1: + { + size = resolution.x * resolution.y * 3 * channels; + Byte* result = new Byte[size]; + BD8_to_BD24(size, result); + byteDepth = 3; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAs24() const + { + switch (byteDepth) + { + case 4: + { + Img result(id, 3, channels, resolution); + BD32_to_BD24(result.GetSize(), result); + return result; + } + case 3: + { + return {*this}; + } + case 2: + { + Img result(id, 3, channels, resolution); + BD16_to_BD24(result.GetSize(), result); + return result; + } + case 1: + { + Img result(id, 3, channels, resolution); + BD8_to_BD24(result.GetSize(), result); + return result; + } + default: + { + return {}; + } + } + } + + void Img::To16() + { + switch (byteDepth) + { + case 4: + { + size = resolution.x * resolution.y * 2 * channels; + Byte* result = new Byte[size]; + BD32_to_BD16(size, result); + byteDepth = 2; + delete[] data; + data = result; + break; + } + case 3: + { + size = resolution.x * resolution.y * 2 * channels; + Byte* result = new Byte[size]; + BD24_to_BD16(size, result); + byteDepth = 2; + delete[] data; + data = result; + break; + } + case 1: + { + size = resolution.x * resolution.y * 2 * channels; + Byte* result = new Byte[size]; + BD8_to_BD16(size, result); + byteDepth = 2; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAs16() const + { + switch (byteDepth) + { + case 4: + { + Img result(id, 2, channels, resolution); + BD32_to_BD16(result.GetSize(), result); + return result; + } + case 3: + { + Img result(id, 2, channels, resolution); + BD24_to_BD16(result.GetSize(), result); + return result; + } + case 2: + { + return {*this}; + } + case 1: + { + Img result(id, 2, channels, resolution); + BD8_to_BD16(result.GetSize(), result); + return result; + } + default: + { + return {}; + } + } + } + + void Img::To8() + { + switch (byteDepth) + { + case 4: + { + size = resolution.x * resolution.y * channels; + Byte* result = new Byte[size]; + BD32_to_BD8(size, result); + byteDepth = 1; + delete[] data; + data = result; + break; + } + case 3: + { + size = resolution.x * resolution.y * channels; + Byte* result = new Byte[size]; + BD24_to_BD8(size, result); + byteDepth = 1; + delete[] data; + data = result; + break; + } + case 2: + { + size = resolution.x * resolution.y * channels; + Byte* result = new Byte[size]; + BD16_to_BD8(size, result); + byteDepth = 1; + delete[] data; + data = result; + break; + } + default: + { + return; + } + } + } + + Img Img::GetAs8() const + { + switch (byteDepth) + { + case 4: + { + Img result(id, 1, channels, resolution); + BD32_to_BD8(result.GetSize(), result); + return result; + } + case 3: + { + Img result(id, 1, channels, resolution); + BD24_to_BD8(result.GetSize(), result); + return result; + } + case 2: + { + Img result(id, 1, channels, resolution); + BD16_to_BD8(result.GetSize(), result); + return result; + } + case 1: + { + return {*this}; + } + default: + { + return {}; + } + } + } + + bool Img::IsValid() const + { + return size; + } + + bool Img::ToFile(const Str_8& filePath) const + { + Str_8 ext = File::ParseExt_8(filePath); + + const ImgCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return false; + } + + Serializer result; + if (!codec->Encode(result, this)) + return false; + + File file(filePath, Mode::WRITE, Disposition::CREATE_PERSISTENT); + file.WriteSerializer_64(result); + + return true; + } + + Img Img::FromFile(const Str_8& filePath) + { + File file(filePath, Mode::READ, Disposition::OPEN); + Str_8 ext = file.GetExtension(); + + Img result(file.GetName()); + + const ImgCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return result; + } + + Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); + + file.Release(); + + if (!codec->Decode(data, &result)) + return {}; + + return result; + } + + Img* Img::FromFile_Heap(const Str_8& filePath) + { + File file(filePath, Mode::READ, Disposition::OPEN); + Str_8 ext = file.GetExtension(); + + Img* result = nullptr; + + const ImgCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return result; + } + + result = new Img(file.GetName()); + + Serializer data = file.ReadSerializer_64(codec->GetEndianness(), file.Size()); + + file.Release(); + + if (!codec->Decode(data, result)) + { + delete result; + return nullptr; + } + + return result; + } + + Img Img::FromData(Str_8 id, const Str_8& ext, Serializer& data) + { + Img result((Str_8&&)id); + + const ImgCodec* codec = GetCodec(ext); + if (!codec) + { + EHS_LOG_INT("Error", 0, "Codec not found for file extension, \"" + ext + "\"."); + return result; + } + + if (!codec->Decode(data, &result)) + return {}; + + return result; + } + + Img Img::GetNearestNeighbor(const Vec2_u64& newResolution) const + { + Img result(id, byteDepth, channels, newResolution); + + Vec2_d ratio = { + (double)resolution.x / (double)newResolution.x, + (double)resolution.y / (double)newResolution.x + }; + + Vec2_d pixel; + + for(UInt_64 y = 0; y < newResolution.y; ++y) + { + for (UInt_64 x = 0; x < newResolution.x; ++x) + { + pixel = { + Math::Floor((double)x * ratio.x), + Math::Floor((double)y * ratio.y) + }; + + for (UInt_64 b = 0; b < byteDepth * channels; ++b) + result[y * newResolution.x * byteDepth * channels + x * byteDepth * channels + b] = data[(UInt_64)pixel.y * resolution.x * byteDepth * channels + (UInt_64)pixel.x * byteDepth * channels + b]; + } + } + + return result; + } + + void Img::NearestNeighbor(const Vec2_u64& newResolution) + { + Byte* result = new Byte[newResolution.x * byteDepth * channels * newResolution.y]; + + Vec2_d ratio = { + (double)resolution.x / (double)newResolution.x, + (double)resolution.y / (double)newResolution.x + }; + + Vec2_d pixel; + + for(UInt_64 y = 0; y < newResolution.y; ++y) + { + for (UInt_64 x = 0; x < newResolution.x; ++x) + { + pixel = { + Math::Floor((double)x * ratio.x), + Math::Floor((double)y * ratio.y) + }; + + for (UInt_64 b = 0; b < byteDepth * channels; ++b) + result[y * newResolution.x * byteDepth * channels + x * byteDepth * channels + b] = data[(UInt_64)pixel.y * resolution.x * byteDepth * channels + (UInt_64)pixel.x * byteDepth * channels + b]; + } + } + + delete[] data; + data = result; + resolution = newResolution; + } + + void Img::RGB_To_RGBA(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 4, n += byteDepth * channels) + { + for (UInt_64 b = 0; b < byteDepth * 3; ++b) + buffer[i + b] = data[n + b]; + + if (byteDepth == 1) + buffer[i + byteDepth * 3] = EHS_UINT_8_MAX; + else if (byteDepth == 2) + *(UInt_16*)&buffer[i + byteDepth * 3] = EHS_UINT_16_MAX; + else if (byteDepth == 3) + { + UInt_32 value = EHS_UINT_24_MAX; + for (UInt_64 b = 0; b < byteDepth; ++b) + buffer[i + byteDepth * 3 + b] = ((Byte*)&value)[b]; + } + else if (byteDepth == 4) + *(UInt_32*)&buffer[i + byteDepth * 3] = EHS_UINT_32_MAX; + } + } + + void Img::MonoA_To_RGBA(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 4, n += byteDepth * channels) + { + for (UInt_64 b = 0; b < byteDepth * 4; ++b) + buffer[i + b] = data[n + b % byteDepth]; + + if (byteDepth == 1) + buffer[i + byteDepth * 3] = data[n + byteDepth]; + else if (byteDepth == 2) + *(UInt_16*) &buffer[i + byteDepth * 3] = *(UInt_16*) &data[n + byteDepth]; + else if (byteDepth == 3) + { + for (UInt_64 b = 0; b < byteDepth; ++b) + buffer[i + byteDepth * 3 + b] = data[n + byteDepth + b]; + } + else if (byteDepth == 4) + *(UInt_32*) &buffer[i + byteDepth * 3] = *(UInt_32*) &data[n + byteDepth]; + } + } + + void Img::Mono_To_RGBA(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 4, n += byteDepth * channels) + { + for (UInt_64 b = 0; b < byteDepth * 4; ++b) + buffer[i + b] = data[n + b % byteDepth]; + + if (byteDepth == 1) + buffer[i + byteDepth * 3] = EHS_UINT_8_MAX; + else if (byteDepth == 2) + *(UInt_16*)&buffer[i + byteDepth * 3] = EHS_UINT_16_MAX; + else if (byteDepth == 3) + { + UInt_32 value = EHS_UINT_24_MAX; + for (UInt_64 b = 0; b < byteDepth; ++b) + buffer[i + byteDepth * 3 + b] = ((Byte*)&value)[b]; + } + else if (byteDepth == 4) + *(UInt_32*) &buffer[i + byteDepth * 3] = EHS_UINT_32_MAX; + } + } + + void Img::RGBA_To_RGB(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 3, n += byteDepth * channels) + for (UInt_64 b = 0; b < byteDepth * 3; ++b) + buffer[i + b] = data[n + b]; + } + + void Img::MonoA_To_RGB(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 3, n += byteDepth * channels) + for (UInt_64 b = 0; b < byteDepth * 3; ++b) + buffer[i + b] = data[n + b % byteDepth]; + } + + void Img::Mono_To_RGB(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 3, n += byteDepth * channels) + for (UInt_64 b = 0; b < byteDepth * 3; ++b) + buffer[i + b] = data[n + b % byteDepth]; + } + + void Img::RGBA_To_MonoA(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 2, n += byteDepth * channels) + { + if (byteDepth == 1) + { + UInt_16 average = 0; + + for (UInt_64 b = 0; b < 3; ++b) + average += data[n + b]; + + buffer[i] = average / 4; + buffer[i + byteDepth] = data[n + 3]; + } + else if (byteDepth == 2) + { + UInt_32 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_16*)&data[n + b]; + + *(UInt_16*)&buffer[i] = average / 4; + *(UInt_16*)&buffer[i + byteDepth] = *(UInt_16*)&data[n + byteDepth * 3]; + } + else if (byteDepth == 3) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + { + UInt_32 num = 0; + num |= data[n + b]; + num |= data[n + b + 1] << 8; + num |= data[n + b + 2] << 16; + + average += num; + } + + average /= 4; + + buffer[i] = ((Byte*)&average)[0]; + buffer[i + 1] = ((Byte*)&average)[1]; + buffer[i + 2] = ((Byte*)&average)[2]; + + buffer[i + byteDepth] = data[n + byteDepth * 3]; + buffer[i + byteDepth + 1] = data[n + byteDepth * 3 + 1]; + buffer[i + byteDepth + 2] = data[n + byteDepth * 3 + 2]; + } + else if (byteDepth == 4) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_32*)&data[n + b]; + + *(UInt_32*)&buffer[i] = average / 4; + *(UInt_32*)&buffer[i + byteDepth] = *(UInt_32*)&data[n + byteDepth * 3]; + } + } + } + + void Img::RGB_To_MonoA(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 2, n += byteDepth * channels) + { + if (byteDepth == 1) + { + UInt_16 average = 0; + + for (UInt_64 b = 0; b < 3; ++b) + average += data[n + b]; + + buffer[i] = average / 4; + buffer[i + byteDepth] = EHS_UINT_8_MAX; + } + else if (byteDepth == 2) + { + UInt_32 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_16*)&data[n + b]; + + *(UInt_16*)&buffer[i] = average / 4; + *(UInt_16*)&buffer[i + byteDepth] = EHS_UINT_16_MAX; + } + else if (byteDepth == 3) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + { + UInt_32 num = 0; + num |= data[n + b]; + num |= data[n + b + 1] << 8; + num |= data[n + b + 2] << 16; + + average += num; + } + + average /= 4; + + buffer[i] = ((Byte*)&average)[0]; + buffer[i + 1] = ((Byte*)&average)[1]; + buffer[i + 2] = ((Byte*)&average)[2]; + + UInt_32 tmp = EHS_UINT_24_MAX; + + buffer[i + byteDepth] = ((Byte*)&tmp)[0]; + buffer[i + byteDepth + 1] = ((Byte*)&tmp)[1]; + buffer[i + byteDepth + 2] = ((Byte*)&tmp)[2]; + } + else if (byteDepth == 4) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_32*)&data[n + b]; + + *(UInt_32*)&buffer[i] = average / 4; + *(UInt_32*)&buffer[i + byteDepth] = EHS_UINT_32_MAX; + } + } + } + + void Img::Mono_To_MonoA(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth * 2, n += byteDepth * channels) + { + if (byteDepth == 1) + { + buffer[i] = data[n]; + buffer[i + byteDepth] = EHS_UINT_8_MAX; + } + else if (byteDepth == 2) + { + *(UInt_16*)&buffer[i] = *(UInt_16*)&data[n]; + *(UInt_16*)&buffer[i + byteDepth] = EHS_UINT_16_MAX; + } + else if (byteDepth == 3) + { + buffer[i] = data[n]; + buffer[i + 1] = data[n + 1]; + buffer[i + 2] = data[n + 2]; + + UInt_32 tmp = EHS_UINT_24_MAX; + + buffer[i + byteDepth] = ((Byte*)&tmp)[0]; + buffer[i + byteDepth + 1] = ((Byte*)&tmp)[1]; + buffer[i + byteDepth + 2] = ((Byte*)&tmp)[2]; + } + else if (byteDepth == 4) + { + *(UInt_32*)&buffer[i] = *(UInt_32*)&data[n]; + *(UInt_32*)&buffer[i + byteDepth] = EHS_UINT_32_MAX; + } + } + } + + void Img::RGBA_To_Mono(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth, n += byteDepth * channels) + { + if (byteDepth == 1) + { + UInt_16 average = 0; + + for (UInt_64 b = 0; b < 3; ++b) + average += data[n + b]; + + buffer[i] = average / 4; + } + else if (byteDepth == 2) + { + UInt_32 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_16*)&data[n + b]; + + *(UInt_16*)&buffer[i] = average / 4; + } + else if (byteDepth == 3) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + { + UInt_32 num = 0; + num |= data[n + b]; + num |= data[n + b + 1] << 8; + num |= data[n + b + 2] << 16; + + average += num; + } + + average /= 4; + + buffer[i] = ((Byte*)&average)[0]; + buffer[i + 1] = ((Byte*)&average)[1]; + buffer[i + 2] = ((Byte*)&average)[2]; + } + else if (byteDepth == 4) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_32*)&data[n + b]; + + *(UInt_32*)&buffer[i] = average / 4; + } + } + } + + void Img::RGB_To_Mono(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth, n += byteDepth * channels) + { + if (byteDepth == 1) + { + UInt_16 average = 0; + + for (UInt_64 b = 0; b < 3; ++b) + average += data[n + b]; + + buffer[i] = average / 4; + } + else if (byteDepth == 2) + { + UInt_32 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_16*)&data[n + b]; + + *(UInt_16*)&buffer[i] = average / 4; + } + else if (byteDepth == 3) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + { + UInt_32 num = 0; + num |= data[n + b]; + num |= data[n + b + 1] << 8; + num |= data[n + b + 2] << 16; + + average += num; + } + + average /= 4; + + buffer[i] = ((Byte*)&average)[0]; + buffer[i + 1] = ((Byte*)&average)[1]; + buffer[i + 2] = ((Byte*)&average)[2]; + } + else if (byteDepth == 4) + { + UInt_64 average = 0; + + for (UInt_64 b = 0; b < byteDepth * 3; b += byteDepth) + average += *(UInt_32*)&data[n + b]; + + *(UInt_32*)&buffer[i] = average / 4; + } + } + } + + void Img::MonoA_To_Mono(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += byteDepth, n += byteDepth * channels) + { + if (byteDepth == 1) + { + buffer[i] = data[n]; + } + else if (byteDepth == 2) + { + *(UInt_16*)&buffer[i] = *(UInt_16*)&data[n]; + } + else if (byteDepth == 3) + { + buffer[i] = data[n]; + buffer[i + 1] = data[n + 1]; + buffer[i + 2] = data[n + 2]; + } + else if (byteDepth == 4) + { + *(UInt_32*)&buffer[i] = *(UInt_32*)&data[n]; + } + } + } + + void Img::BD24_to_BD32(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 4, n += 3) + { + UInt_32 inValue = 0; + ((Byte*)&inValue)[0] = data[n]; + ((Byte*)&inValue)[1] = data[n + 1]; + ((Byte*)&inValue)[2] = data[n + 2]; + + *(UInt_32*)&buffer[i] = (UInt_32)((float)inValue / (float)EHS_UINT_24_MAX * (float)EHS_UINT_32_MAX); + } + } + + void Img::BD16_to_BD32(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 4, n += 2) + *(UInt_32*)&buffer[i] = (UInt_32)((float)*(UInt_16*)&data[n] / (float)EHS_UINT_16_MAX * (float)EHS_UINT_32_MAX); + } + + void Img::BD8_to_BD32(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 4, ++n) + *(UInt_32*)&buffer[i] = (UInt_32)((float)data[n] / (float)EHS_UINT_8_MAX * (float)EHS_UINT_32_MAX); + } + + void Img::BD32_to_BD24(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 3, n += 4) + { + UInt_32 rValue = (UInt_32)((float)*(UInt_32*)&data[n] / (float)EHS_UINT_32_MAX * (float)EHS_UINT_24_MAX); + + buffer[i] = ((Byte*)&rValue)[0]; + buffer[i + 1] = ((Byte*)&rValue)[1]; + buffer[i + 2] = ((Byte*)&rValue)[2]; + } + } + + void Img::BD16_to_BD24(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 3, n += 2) + { + UInt_32 rValue = (UInt_32)((float)*(UInt_16*)&data[n] / (float)EHS_UINT_16_MAX * (float)EHS_UINT_24_MAX); + + buffer[i] = ((Byte*)&rValue)[0]; + buffer[i + 1] = ((Byte*)&rValue)[1]; + buffer[i + 2] = ((Byte*)&rValue)[2]; + } + } + + void Img::BD8_to_BD24(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 3, ++n) + { + UInt_32 rValue = (UInt_32)((float)data[n] / (float)EHS_UINT_8_MAX * (float)EHS_UINT_24_MAX); + + buffer[i] = ((Byte*)&rValue)[0]; + buffer[i + 1] = ((Byte*)&rValue)[1]; + buffer[i + 2] = ((Byte*)&rValue)[2]; + } + } + + void Img::BD32_to_BD16(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 2, n += 4) + *(UInt_16*)&buffer[i] = (UInt_16)((float)*(UInt_32*)&data[n] / (float)EHS_UINT_32_MAX * (float)EHS_UINT_16_MAX); + } + + void Img::BD24_to_BD16(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 2, n += 3) + { + UInt_32 inValue = 0; + ((Byte*)&inValue)[0] = data[n]; + ((Byte*)&inValue)[1] = data[n + 1]; + ((Byte*)&inValue)[2] = data[n + 2]; + + *(UInt_16*)&buffer[i] = (UInt_16)((float)inValue / (float)EHS_UINT_24_MAX * (float)EHS_UINT_16_MAX); + } + } + + void Img::BD8_to_BD16(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; i += 2, ++n) + *(UInt_16*)&buffer[i] = (UInt_16)((float)data[n] / (float)EHS_UINT_8_MAX * (float)EHS_UINT_16_MAX); + } + + void Img::BD32_to_BD8(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; ++i, n += 4) + buffer[i] = (Byte)((float)*(UInt_32*)&data[n] / (float)EHS_UINT_32_MAX * (float)EHS_UINT_8_MAX); + } + + void Img::BD24_to_BD8(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; ++i, n += 3) + { + UInt_32 inValue = 0; + ((Byte*)&inValue)[0] = data[n]; + ((Byte*)&inValue)[1] = data[n + 1]; + ((Byte*)&inValue)[2] = data[n + 2]; + + buffer[i] = (Byte)((float)inValue / (float)EHS_UINT_24_MAX * (float)EHS_UINT_8_MAX); + } + } + + void Img::BD16_to_BD8(const UInt_64 newSize, Byte* buffer) const + { + for (UInt_64 i = 0, n = 0; i < newSize; ++i, n += 2) + buffer[i] = (Byte)((float)*(UInt_16*)&data[n] / (float)EHS_UINT_16_MAX * (float)EHS_UINT_8_MAX); + } +} \ No newline at end of file diff --git a/src/io/img/ImgCodec.cpp b/src/io/img/ImgCodec.cpp new file mode 100644 index 0000000..0281a26 --- /dev/null +++ b/src/io/img/ImgCodec.cpp @@ -0,0 +1,110 @@ +#include "ehs/io/img/ImgCodec.h" +#include "ehs/io/img/Img.h" + +namespace ehs +{ + ImgCodec::ImgCodec() + : hashExt(0), endianness(Endianness::LE), encodeCb(nullptr), decodeCb(nullptr) + { + } + + ImgCodec::ImgCodec(Str_8 id, Str_8 ext, const Endianness end, + bool (* encodeCb)(const ImgCodec* const, Serializer&, const Img*), + bool (* decodeCb)(const ImgCodec* const, Serializer&, Img*)) + : id(std::move(id)), hashExt(ext.Hash_64()), ext(std::move(ext)), endianness(end), encodeCb(encodeCb), decodeCb(decodeCb) + { + } + + ImgCodec::ImgCodec(ImgCodec&& codec) noexcept + : id(std::move(codec.id)), hashExt(codec.hashExt), ext(std::move(codec.ext)), endianness(codec.endianness), + encodeCb(codec.encodeCb), decodeCb(codec.decodeCb) + { + codec.hashExt = 0; + codec.endianness = Endianness::LE; + codec.encodeCb = nullptr; + codec.decodeCb = nullptr; + } + + ImgCodec::ImgCodec(const ImgCodec& codec) + : id(codec.id), hashExt(codec.hashExt), ext(codec.ext), endianness(codec.endianness), encodeCb(codec.encodeCb), + decodeCb(codec.decodeCb) + { + } + + ImgCodec& ImgCodec::operator=(ImgCodec&& codec) noexcept + { + if (this == &codec) + return *this; + + id = std::move(codec.id); + hashExt = codec.hashExt; + ext = std::move(codec.ext); + endianness = codec.endianness; + encodeCb = codec.encodeCb; + decodeCb = codec.decodeCb; + + codec.hashExt = 0; + codec.endianness = Endianness::LE; + codec.encodeCb = nullptr; + codec.decodeCb = nullptr; + + return *this; + } + + ImgCodec& ImgCodec::operator=(const ImgCodec& codec) + { + if (this == &codec) + return *this; + + id = codec.id; + hashExt = codec.hashExt; + ext = codec.ext; + endianness = codec.endianness; + encodeCb = codec.encodeCb; + decodeCb = codec.decodeCb; + + return *this; + } + + Str_8 ImgCodec::GetId() const + { + return id; + } + + UInt_64 ImgCodec::GetHashExt() const + { + return hashExt; + } + + Str_8 ImgCodec::GetExt() const + { + return ext; + } + + Endianness ImgCodec::GetEndianness() const + { + return endianness; + } + + bool ImgCodec::Encode(Serializer& out, const Img* in) const + { + if (!encodeCb) + { + EHS_LOG_INT("Error", 0, "Encoding is not supported for the " + id + " format."); + return false; + } + + return encodeCb(this, out, in); + } + + bool ImgCodec::Decode(Serializer& in, Img* out) const + { + if (!decodeCb) + { + EHS_LOG_INT("Error", 0, "Decoding is not supported for the " + id + " format."); + return false; + } + + return decodeCb(this, in, out); + } +} \ No newline at end of file diff --git a/src/io/img/PNG.cpp b/src/io/img/PNG.cpp new file mode 100644 index 0000000..f01b1b9 --- /dev/null +++ b/src/io/img/PNG.cpp @@ -0,0 +1,232 @@ +#include "ehs/io/img/PNG.h" + +namespace ehs +{ + Array pngSeq = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; + + PNG::PNG() + : hashId(0) + { + } + + PNG::PNG(const Str_8& filePath) + { + id = File::ParseName_8(filePath); + hashId = id.Hash_64(); + + File file(filePath, Mode::READ, Disposition::OPEN); + + Serializer data = file.ReadSerializer_64(Endianness::BE, file.Size()); + + file.Release(); + + Array seq = data.ReadArray(8); + if (seq != pngSeq) + { + EHS_LOG_INT("Error", 0, "File at file path, \"" + filePath + "\", is not a valid PNG file."); + return; + } + + while (data.GetOffset() < data.Size()) + { + UInt_32 length = data.Read(); + + Str_8 id = data.ReadStr(4); + + Serializer chunkData(Endianness::BE, &data[data.GetOffset()], length); + data.SetOffset(data.GetOffset() + length); + + Byte crc[4]; + UInt_64 crcSize = 4; + + data.ReadArray(crc, &crcSize); + + PNG_Chunk* chunk = GetChunk(id); + if (chunk) + { + chunk->GetData()->SetOffset(chunk->GetData()->Size()); + chunk->GetData()->WriteSer(chunkData); + chunk->GetData()->SetOffset(0); + } + else + chunks.Push(PNG_Chunk(id, chunkData, crc)); + } + } + + PNG::PNG(const Str_8& id, Serializer& data) + : id(id), hashId(id.Hash_64()) + { + Array seq = data.ReadArray(8); + if (seq != pngSeq) + return; + + while (data.GetOffset() < data.Size()) + { + UInt_32 length = data.Read(); + + Str_8 id = data.ReadStr(4); + + Serializer chunkData(Endianness::BE, &data[data.GetOffset()], length); + data.SetOffset(data.GetOffset() + length); + + Byte crc[4]; + UInt_64 crcSize = 4; + + data.ReadArray(crc, &crcSize); + + PNG_Chunk* chunk = GetChunk(id); + if (chunk) + { + chunk->GetData()->SetOffset(chunk->GetData()->Size()); + chunk->GetData()->WriteSer(chunkData); + chunk->GetData()->SetOffset(0); + } + else + chunks.Push(PNG_Chunk(id, chunkData, crc)); + } + } + + PNG::PNG(const PNG& png) + : id(png.id), hashId(png.hashId), chunks(png.chunks) + { + } + + PNG& PNG::operator=(const PNG& png) + { + if (this == &png) + return *this; + + id = png.id; + hashId = png.hashId; + chunks = png.chunks; + + return *this; + } + + bool PNG::HasChunk(const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < chunks.Size(); ++i) + if (chunks[i].GetHashId() == hashId) + return true; + + return false; + } + + bool PNG::HasChunk(const Str_8& id) const + { + return HasChunk(id.Hash_64()); + } + + PNG_Chunk* PNG::GetChunk(const UInt_64 hashId) + { + for (UInt_64 i = 0; i < chunks.Size(); ++i) + if (chunks[i].GetHashId() == hashId) + return &chunks[i]; + + return nullptr; + } + + PNG_Chunk* PNG::GetChunk(const Str_8& id) + { + return GetChunk(id.Hash_64()); + } + + bool PNG::IsPNG(Serializer& data) + { + UInt_32 oldOffset = data.GetOffset(); + data.SetOffset(0); + + Array seq = data.ReadArray(8); + if (seq != pngSeq) + return false; + + data.SetOffset(oldOffset); + + return true; + } + + void PNG::FilterNone(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline) + { + UInt_8 bytes = bitDepth / 8; + + for (UInt_32 i = 0; i < scanline; i += bytes) + for (UInt_8 b = 0; b < bytes; ++b) + out[i + b] = in[i + bytes - 1 - b]; + } + + void PNG::FilterSub(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline) + { + UInt_8 bytes = bitDepth / 8; + UInt_8 pxSize = bytes * channels; + + for (UInt_32 i = 0; i < scanline; i += bytes) + { + for (UInt_8 b = 0; b < bytes; ++b) + { + if (i >= pxSize) + out[i + b] = in[i + bytes - 1 - b] + out[i - pxSize + b]; + else + out[i + b] = in[i + bytes - 1 - b]; + } + } + } + + void PNG::FilterUp(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline) + { + UInt_8 bytes = bitDepth / 8; + + for (UInt_32 i = 0; i < scanline; i += bytes) + for (UInt_8 b = 0; b < bytes; ++b) + out[scanline + i + b] = in[i + bytes - 1 - b] + out[i + b]; + } + + void PNG::FilterAverage(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline) + { + UInt_8 bytes = bitDepth / 8; + UInt_8 pxSize = bytes * channels; + + for (UInt_32 i = 0; i < scanline; i += bytes) + { + for (UInt_8 b = 0; b < bytes; ++b) + { + Byte prevPx = 0; + if (i >= pxSize) + prevPx = out[scanline + i - pxSize + b]; + + out[scanline + i + b] = in[i + bytes - 1 - b] + (Byte)Math::Floor(((float)prevPx + (float)out[i + b]) / 2.0f); + } + } + } + + void PNG::FilterPaeth(const Byte* const in, Byte* const out, const UInt_8 bitDepth, const UInt_8 channels, const UInt_32 scanline) + { + UInt_8 bytes = bitDepth / 8; + UInt_8 pxSize = bytes * channels; + + for (UInt_32 i = 0; i < scanline; i += bytes) + { + for (UInt_8 b = 0; b < bytes; ++b) + { + if (i >= pxSize) + out[scanline + i + b] = in[i + bytes - 1 - b] + PaethPredictor(out[scanline + i - pxSize + b], out[i + b], out[i - pxSize + b]); + else + out[scanline + i + b] = in[i + bytes - 1 - b] + out[i + b]; + } + } + } + + Byte PNG::PaethPredictor(const Byte a, const Byte b, const Byte c) + { + SInt_16 p = a + b - c; + SInt_16 pa = Math::Abs(p - a); + SInt_16 pb = Math::Abs(p - b); + SInt_16 pc = Math::Abs(p - c); + + if (pa <= pb && pa <= pc) + return a; + else if (pb <= pc) + return b; + else + return c; + } +} \ No newline at end of file diff --git a/src/io/img/PNG_Chunk.cpp b/src/io/img/PNG_Chunk.cpp new file mode 100644 index 0000000..fff8096 --- /dev/null +++ b/src/io/img/PNG_Chunk.cpp @@ -0,0 +1,55 @@ +#include "ehs/io/img/PNG_Chunk.h" + +namespace ehs +{ + PNG_Chunk::PNG_Chunk() + : hashId(0), crc{0x0, 0x0, 0x0, 0x0} + { + } + + PNG_Chunk::PNG_Chunk(const Str_8& id, const Serializer& data, const Byte crc[4]) + : id(id), hashId(id.Hash_64()), data(data), crc{crc[0], crc[1], crc[2], crc[3]} + { + } + + PNG_Chunk::PNG_Chunk(const PNG_Chunk& chunk) + : id(chunk.id), hashId(chunk.hashId), data(chunk.data), crc{chunk.crc[0], chunk.crc[1], chunk.crc[2], chunk.crc[3]} + { + } + + PNG_Chunk& PNG_Chunk::operator=(const PNG_Chunk& chunk) + { + if (this == &chunk) + return *this; + + id = chunk.id; + hashId = chunk.hashId; + data = chunk.data; + crc[0] = chunk.crc[0]; + crc[1] = chunk.crc[1]; + crc[2] = chunk.crc[2]; + crc[3] = chunk.crc[3]; + + return *this; + } + + Str_8 PNG_Chunk::GetId() const + { + return id; + } + + UInt_64 PNG_Chunk::GetHashId() const + { + return hashId; + } + + Serializer* PNG_Chunk::GetData() + { + return &data; + } + + const unsigned char* PNG_Chunk::GetCRC() const + { + return crc; + } +} \ No newline at end of file diff --git a/src/io/model/AnimBone.cpp b/src/io/model/AnimBone.cpp new file mode 100644 index 0000000..0e973c7 --- /dev/null +++ b/src/io/model/AnimBone.cpp @@ -0,0 +1,91 @@ +#include "ehs/io/model/AnimBone.h" + +#include + +namespace ehs +{ + AnimBone::AnimBone() + : boneId(0xFF) + { + } + + AnimBone::AnimBone(const UInt_8 boneId) + : boneId(boneId) + { + } + + AnimBone::AnimBone(const UInt_8 boneId, const UInt_64 size) + : boneId(boneId), keyFrames(size) + { + } + + AnimBone::AnimBone(const UInt_8 boneId, Array keyFrames) + : boneId(boneId), keyFrames(std::move(keyFrames)) + { + } + + AnimBone::AnimBone(const AnimBone& anim) + : boneId(anim.boneId), keyFrames(anim.keyFrames) + { + } + + AnimBone::AnimBone(AnimBone&& anim) noexcept + : boneId(anim.boneId), keyFrames(std::move(anim.keyFrames)) + { + anim.boneId = 0xFF; + } + + AnimBone& AnimBone::operator=(AnimBone&& anim) noexcept + { + if (this == &anim) + return *this; + + boneId = anim.boneId; + keyFrames = std::move(anim.keyFrames); + + anim.boneId = 0xFF; + + return *this; + } + + AnimBone& AnimBone::operator=(const AnimBone& anim) + { + if (this == &anim) + return *this; + + boneId = anim.boneId; + keyFrames = anim.keyFrames; + + return *this; + } + + UInt_8 AnimBone::GetBoneId() const + { + return boneId; + } + + Array AnimBone::GetKeyFrames() const + { + return keyFrames; + } + + Array& AnimBone::GetKeyFrames() + { + return keyFrames; + } + + float AnimBone::GetPrevAndNext(KeyFrame& prev, KeyFrame& next, const float elapsed) const + { + prev = keyFrames[0]; + next = keyFrames[0]; + for (UInt_64 i = 1; i < keyFrames.Size(); ++i) + { + next = keyFrames[i]; + if (keyFrames[i].GetTimeStamp() > elapsed) + break; + prev = keyFrames[i]; + } + + return (elapsed - prev.GetTimeStamp()) / (next.GetTimeStamp() - prev.GetTimeStamp()); + } +} \ No newline at end of file diff --git a/src/io/model/Animation.cpp b/src/io/model/Animation.cpp new file mode 100644 index 0000000..21c1de8 --- /dev/null +++ b/src/io/model/Animation.cpp @@ -0,0 +1,130 @@ +#include "ehs/io/model/Animation.h" + +namespace ehs +{ + Animation::Animation() + : hashId(0), duration(0.0f) + { + } + + Animation::Animation(Str_8 id, const float duration) + : hashId(id.Hash_64()), id(std::move(id)), duration(duration) + { + } + + Animation::Animation(Str_8 id, const float duration, UInt_64 size) + : hashId(id.Hash_64()), id(std::move(id)), duration(duration), animated(size) + { + } + + Animation::Animation(Str_8 id, const float duration, Array animated) + : hashId(id.Hash_64()), id(std::move(id)), duration(duration), animated(std::move(animated)) + { + } + + Animation::Animation(Animation&& anim) noexcept + : hashId(anim.hashId), id(std::move(anim.id)), duration(anim.duration), animated(std::move(anim.animated)) + { + anim.hashId = 0; + anim.duration = 0.0f; + } + + Animation::Animation(const Animation& anim) + : hashId(anim.hashId), id(anim.id), duration(anim.duration), animated(anim.animated) + { + } + + Animation& Animation::operator=(Animation&& anim) noexcept + { + if (this == &anim) + return *this; + + hashId = anim.hashId; + id = std::move(anim.id); + duration = anim.duration; + animated = std::move(anim.animated); + + anim.hashId = 0; + anim.duration = 0.0f; + + return *this; + } + + Animation& Animation::operator=(const Animation& anim) + { + if (this == &anim) + return *this; + + hashId = anim.hashId; + id = anim.id; + duration = anim.duration; + animated = anim.animated; + + return *this; + } + + UInt_64 Animation::GetHashId() const + { + return hashId; + } + + void Animation::SetId(Str_8 newId) + { + hashId = newId.Hash_64(); + id = std::move(newId); + } + + Str_8 Animation::GetId() const + { + return id; + } + + float Animation::GetDuration() const + { + return duration; + } + + Array Animation::GetAnimated() const + { + return animated; + } + + Array& Animation::GetAnimated() + { + return animated; + } + + Array Animation::Interpolate(const UInt_64 boneCount, const float elapsed) const + { + Array result(boneCount); + for (UInt_64 i = 0; i < result.Size(); ++i) + result[i] = Mat4_f::Identity(); + + if (elapsed == 0.0f) + { + for (UInt_64 i = 0; i < animated.Size(); ++i) + { + Array keyFrames = animated[i].GetKeyFrames(); + if (!keyFrames.Size()) + continue; + + result[animated[i].GetBoneId()] = keyFrames[0].GetTrans(); + } + } + else + { + for (UInt_64 i = 0; i < animated.Size(); ++i) + { + KeyFrame prev; + KeyFrame next; + float perc = animated[i].GetPrevAndNext(prev, next, elapsed); + if (prev.GetNum() == next.GetNum() || perc <= 0.0f) + result[animated[i].GetBoneId()] = prev.GetTrans(); + else + result[animated[i].GetBoneId()] = KeyFrame::Interpolate(prev, next, perc); + } + } + + return result; + } +} \ No newline at end of file diff --git a/src/io/model/Bone.cpp b/src/io/model/Bone.cpp new file mode 100644 index 0000000..1cae5c2 --- /dev/null +++ b/src/io/model/Bone.cpp @@ -0,0 +1,240 @@ +#include "ehs/io/model/Bone.h" + +namespace ehs +{ + Bone::Bone() + : hashName(0), id(0), animTrans(Mat4_f::Identity()) + { + } + + Bone::Bone(Str_8 name, const UInt_8 id, const Mat4_f& localBindTrans, const Mat4_f& invBindTrans) + : hashName(name.Hash_64()), name(std::move(name)), id(id), animTrans(Mat4_f::Identity()), + localBindTrans(localBindTrans), invBindTrans(invBindTrans) + { + } + + Bone::Bone(Bone&& bone) noexcept + : hashName(bone.hashName), name(std::move(bone.name)), id(bone.id), animTrans(bone.animTrans), + localBindTrans(bone.localBindTrans), invBindTrans(bone.invBindTrans), children(std::move(bone.children)) + { + bone.hashName = 0; + bone.id = 0xFF; + bone.animTrans = Mat4_f::Identity(); + bone.localBindTrans = {}; + bone.invBindTrans = {}; + } + + Bone::Bone(const Bone& bone) + : hashName(bone.hashName), name(bone.name), id(bone.id), animTrans(bone.animTrans), localBindTrans(bone.localBindTrans), + invBindTrans(bone.invBindTrans), children(bone.children) + { + } + + Bone& Bone::operator=(Bone&& bone) noexcept + { + if (this == &bone) + return *this; + + hashName = bone.hashName; + name = std::move(bone.name); + id = bone.id; + animTrans = bone.animTrans; + localBindTrans = bone.localBindTrans; + invBindTrans = bone.invBindTrans; + children = std::move(bone.children); + + bone.hashName = 0; + bone.id = 0; + bone.animTrans = Mat4_f::Identity(); + bone.localBindTrans = {}; + bone.invBindTrans = {}; + + return *this; + } + + Bone& Bone::operator=(const Bone& bone) + { + if (this == &bone) + return *this; + + hashName = bone.hashName; + name = bone.name; + id = bone.id; + animTrans = bone.animTrans; + localBindTrans = bone.localBindTrans; + invBindTrans = bone.invBindTrans; + children = bone.children; + + return *this; + } + + UInt_64 Bone::GetHashName() const + { + return hashName; + } + + void Bone::SetName(Str_8 newId) + { + hashName = newId.Hash_64(); + name = std::move(newId); + } + + Str_8 Bone::GetName() const + { + return id; + } + + UInt_8 Bone::GetId() const + { + return id; + } + + void Bone::SetAnimTrans(const Mat4_f& newTrans) + { + animTrans = newTrans; + } + + Mat4_f Bone::GetAnimTrans() const + { + return animTrans; + } + + void Bone::GetAnimTransRec(Array& output) const + { + output[id] = animTrans; + + for (UInt_64 i = 0; i < children.Size(); ++i) + children[i].GetAnimTransRec(output); + } + + Mat4_f Bone::GetLocalBindTrans() const + { + return localBindTrans; + } + + Mat4_f Bone::GetInvBindTrans() const + { + return invBindTrans; + } + + UInt_8 Bone::GetBoneCount() const + { + UInt_8 count = 1; + + for (UInt_64 i = 0; i < children.Size(); ++i) + count += children[i].GetBoneCount(); + + return count; + } + + bool Bone::HasBone(const UInt_64 hashName, const UInt_8 id) const + { + if (this->hashName == hashName && this->id == id) + return true; + + bool result = false; + + for (UInt_64 i = 0; i < children.Size(); ++i) + result = children[i].HasBone(hashName, id); + + return result; + } + + bool Bone::HasBone(const UInt_64 hashName) const + { + if (this->hashName == hashName) + return true; + + bool result = false; + + for (UInt_64 i = 0; i < children.Size(); ++i) + result = children[i].HasBone(hashName); + + return result; + } + + bool Bone::HasBone(const UInt_8 id) const + { + if (this->id == id) + return true; + + bool result = false; + + for (UInt_64 i = 0; i < children.Size(); ++i) + result = children[i].HasBone(id); + + return result; + } + + bool Bone::AddBone(Bone child) + { + if (HasBone(child.hashName, child.id)) + return false; + + children.Push(std::move(child)); + + return true; + } + + const Bone* Bone::GetBone(const UInt_64 hashName) const + { + if (this->hashName == hashName) + return this; + + const Bone* result = nullptr; + + for (UInt_64 i = 0; i < children.Size(); ++i) + result = children[i].GetBone(hashName); + + return result; + } + + Bone* Bone::GetBone(const UInt_64 hashName) + { + if (this->hashName == hashName) + return this; + + Bone* result = nullptr; + + for (UInt_64 i = 0; i < children.Size(); ++i) + result = children[i].GetBone(hashName); + + return result; + } + + const Bone* Bone::GetBone(const UInt_8 id) const + { + if (this->id == id) + return this; + + const Bone* result = nullptr; + + for (UInt_64 i = 0; i < children.Size(); ++i) + result = children[i].GetBone(id); + + return result; + } + + Bone* Bone::GetBone(const UInt_8 id) + { + if (this->id == id) + return this; + + Bone* result = nullptr; + + for (UInt_64 i = 0; i < children.Size(); ++i) + result = children[i].GetBone(id); + + return result; + } + + const Array& Bone::GetChildren() const + { + return children; + } + + Array& Bone::GetChildren() + { + return children; + } +} \ No newline at end of file diff --git a/src/io/model/KeyFrame.cpp b/src/io/model/KeyFrame.cpp new file mode 100644 index 0000000..525b940 --- /dev/null +++ b/src/io/model/KeyFrame.cpp @@ -0,0 +1,102 @@ +#include "ehs/io/model/KeyFrame.h" + +#include + +namespace ehs +{ + KeyFrame::KeyFrame() + : num(0.0f), timeStamp(0.0f), scale(1.0f), trans(Mat4_f::Identity()) + { + } + + KeyFrame::KeyFrame(const float num, const float timeStamp, const Vec3_f& pos, const Quat_f& rot, const Vec3_f& scale) + : num(num), timeStamp(timeStamp), pos(pos), rot(rot), scale(scale), + trans(Mat4_f::Translate(pos) * rot.ToMatrix() * Mat4_f::Scale(scale)) + { + } + + KeyFrame::KeyFrame(const float num, const float timeStamp) + : num(num), timeStamp(timeStamp), scale(1.0f), trans(Mat4_f::Identity()) + { + } + + KeyFrame::KeyFrame(const KeyFrame& kf) + : num(kf.num), timeStamp(kf.timeStamp), pos(kf.pos), rot(kf.rot), scale(kf.scale), trans(kf.trans) + { + } + + KeyFrame& KeyFrame::operator=(const KeyFrame& kf) + { + if (this == &kf) + return *this; + + num = kf.num; + timeStamp = kf.timeStamp; + pos = kf.pos; + rot = kf.rot; + scale = kf.scale; + trans = kf.trans; + + return *this; + } + + float KeyFrame::GetNum() const + { + return num; + } + + float KeyFrame::GetTimeStamp() const + { + return timeStamp; + } + + void KeyFrame::SetPos(const Vec3_f& newPos) + { + pos = newPos; + } + + Vec3_f KeyFrame::GetPos() const + { + return pos; + } + + void KeyFrame::SetRot(const Quat_f& newRot) + { + rot = newRot; + } + + Quat_f KeyFrame::GetRot() const + { + return rot; + } + + void KeyFrame::SetScale(const Vec3_f& newScale) + { + scale = newScale; + } + + Vec3_f KeyFrame::GetScale() const + { + return scale; + } + + void KeyFrame::CalculateTransform() + { + trans = Mat4_f::Translate(pos) * rot.ToMatrix() * Mat4_f::Scale(scale); + } + + Mat4_f KeyFrame::GetTrans() const + { + return trans; + } + + Mat4_f KeyFrame::Interpolate(const KeyFrame& prev, const KeyFrame& next, const float percentage) + { + Vec3_f newPos = Vec3_f::Lerp(prev.pos, next.pos, percentage); + Quat_f newRot = Quat_f::Slerp(prev.rot, next.rot, percentage).GetNormalized(); + Vec3_f newScale = Vec3_f::Lerp(prev.scale, next.scale, percentage); + Mat4_f newTrans = Mat4_f::Translate(newPos) * newRot.ToMatrix() * Mat4_f::Scale(newScale); + + return newTrans; + } +} \ No newline at end of file diff --git a/src/io/model/Mesh.cpp b/src/io/model/Mesh.cpp new file mode 100644 index 0000000..d22eb7d --- /dev/null +++ b/src/io/model/Mesh.cpp @@ -0,0 +1,164 @@ +#include "ehs/io/model/Mesh.h" + +namespace ehs +{ + Mesh::Mesh() + : hashId(0) + { + AddType("Mesh"); + } + + Mesh::Mesh(Str_8 id, Array vertices, Array indices) + : hashId(id.Hash_64()), id((Str_8&&)id), vertices((Array&&)vertices), + indices((Array&&)indices) + { + AddType("Mesh"); + } + + Mesh::Mesh(Str_8 id, Array vertices) + : hashId(id.Hash_64()), id((Str_8&&)id), vertices((Array&&)vertices) + { + AddType("Mesh"); + } + + Mesh::Mesh(Mesh&& mesh) noexcept + : BaseObj((BaseObj&&)mesh), hashId(mesh.hashId), id((Str_8&&)mesh.id), vertices((Array&&)mesh.vertices), + indices((Array&&)mesh.indices) + { + mesh.hashId = 0; + } + + Mesh::Mesh(const Mesh& mesh) + : BaseObj((BaseObj&&)mesh), hashId(mesh.hashId), id(mesh.id), vertices(mesh.vertices), indices(mesh.indices) + { + } + + Mesh& Mesh::operator=(Mesh&& mesh) noexcept + { + if (this == &mesh) + return *this; + + BaseObj::operator=((BaseObj&&)mesh); + + hashId = mesh.hashId; + id = (Str_8&&)mesh.id; + vertices = (Array&&)mesh.vertices; + indices = (Array&&)mesh.indices; + + mesh.hashId = 0; + + return *this; + } + + Mesh& Mesh::operator=(const Mesh& mesh) + { + if (this == &mesh) + return *this; + + BaseObj::operator=(mesh); + + hashId = mesh.hashId; + id = mesh.id; + vertices = mesh.vertices; + indices = mesh.indices; + + return *this; + } + + void Mesh::Release() + { + vertices.Clear(); + indices.Clear(); + } + + UInt_64 Mesh::GetHashId() const + { + return hashId; + } + + void Mesh::SetId(Str_8 newId) + { + hashId = newId.Hash_64(); + id = (Str_8&&)newId; + } + + Str_8 Mesh::GetId() const + { + return id; + } + + void Mesh::SetVertices(const Array& newVertices) + { + vertices = newVertices; + } + + const Array& Mesh::GetVertices() const + { + return vertices; + } + + Array& Mesh::GetVertices() + { + return vertices; + } + + void Mesh::SetIndices(const Array& newIndices) + { + indices = newIndices; + } + + bool Mesh::HasIndices() const + { + return indices.Size(); + } + + const Array& Mesh::GetIndices() const + { + return indices; + } + + Array& Mesh::GetIndices() + { + return indices; + } + + void Mesh::Calculate() + { + if (indices.Size()) + for (UInt_64 i = 0; i < indices.Size(); i += 3) + Calculate(vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]); + else + for (UInt_64 i = 0; i < vertices.Size(); i += 3) + Calculate(vertices[i], vertices[i + 1], vertices[i + 2]); + } + + void Mesh::Calculate(Vertex_f& vert1, Vertex_f& vert2, Vertex_f& vert3) + { + Vec3_f edge1 = vert2.pos - vert1.pos; + Vec3_f edge2 = vert3.pos - vert1.pos; + Vec2_f deltaUV1 = vert2.uv - vert1.uv; + Vec2_f deltaUV2 = vert3.uv - vert1.uv; + + float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y); + + Vec3_f tan( + f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x), + f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y), + f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z) + ); + + vert1.tan = tan; + vert2.tan = tan; + vert3.tan = tan; + + Vec3_f bTan( + f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x), + f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y), + f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z) + ); + + vert1.bTan = bTan; + vert2.bTan = bTan; + vert3.bTan = bTan; + } +} \ No newline at end of file diff --git a/src/io/model/Model.cpp b/src/io/model/Model.cpp new file mode 100644 index 0000000..e90f621 --- /dev/null +++ b/src/io/model/Model.cpp @@ -0,0 +1,275 @@ +#include "ehs/io/model/Model.h" + +namespace ehs +{ + Model::Model() + : hashId(0) + { + AddType("Model"); + } + + Model::Model(const Str_8& filePath) + { + AddType("Model"); + + File file(filePath, Mode::READ, Disposition::OPEN); + + hashId = file.GetName().Hash_64(); + id = file.GetName(); + + if (file.GetExtension() == "ehm") + { + FromEHM(file); + } + } + + Model::Model(Str_8 id, Array meshes, Bone skeleton, Array animations) + : hashId(id.Hash_64()), id((Str_8&&)id), meshes((Array&&)meshes), skeleton((Bone&&)skeleton), + animations((Array&&)animations) + { + AddType("Model"); + } + + Model::Model(Str_8 id, Array meshes, Bone skeleton) + : hashId(id.Hash_64()), id((Str_8&&)id), meshes((Array&&)meshes), skeleton((Bone&&)skeleton) + { + AddType("Model"); + } + + Model::Model(Str_8 id, Array meshes) + : hashId(id.Hash_64()), id((Str_8&&)id), meshes((Array&&)meshes) + { + AddType("Model"); + } + + Model::Model(Model&& model) noexcept + : BaseObj((BaseObj&&)model), hashId(model.hashId), id((Str_8&&)model.id), meshes((Array&&)model.meshes), + skeleton((Bone&&)model.skeleton), animations((Array&&)model.animations) + { + } + + Model& Model::operator=(Model&& model) noexcept + { + if (this == &model) + return *this; + + BaseObj::operator=((BaseObj&&)model); + + hashId = model.hashId; + id = (Str_8&&)model.id; + meshes = (Array&&)model.meshes; + skeleton = (Bone&&)model.skeleton; + animations = (Array&&)model.animations; + + model.hashId = 0; + + return *this; + } + + void Model::Release() + { + meshes.Clear(); + skeleton = {}; + animations.Clear(); + } + + UInt_64 Model::GetHashId() const + { + return hashId; + } + + void Model::SetId(Str_8 newId) + { + hashId = newId.Hash_64(); + id = (Str_8&&)newId; + } + + Str_8 Model::GetId() const + { + return id; + } + + const Array& Model::GetMeshes() const + { + return meshes; + } + + Array& Model::GetMeshes() + { + return meshes; + } + + Mesh* Model::GetMesh(const UInt_64 inHashId) + { + for (UInt_64 i = 0; i < meshes.Size(); ++i) + if (meshes[i].GetHashId() == inHashId) + return &meshes[i]; + + return nullptr; + } + + Mesh* Model::GetMesh(const Str_8& inId) + { + return GetMesh(inId.Hash_64()); + } + + const Bone& Model::GetSkeleton() const + { + return skeleton; + } + + Bone& Model::GetSkeleton() + { + return skeleton; + } + + Animation* Model::GetAnimation(const UInt_64 inHashId) + { + for (UInt_64 i = 0; i < animations.Size(); ++i) + if (animations[i].GetHashId() == inHashId) + return &animations[i]; + + return nullptr; + } + + const Array& Model::GetAnimations() const + { + return animations; + } + + Array& Model::GetAnimations() + { + return animations; + } + + void Model::Calculate() + { + for (UInt_64 i = 0; i < meshes.Size(); ++i) + meshes[i].Calculate(); + } + + void Model::Export(const Str_8& filePath, const ModelEncoding encoding) + { + File file(filePath, Mode::WRITE, Disposition::OPEN_PERSISTENT); + + switch (encoding) + { + case ModelEncoding::EHM: + { + ToEHM(file); + break; + } + } + } + + void Model::ToEHM(File& file) + { + Serializer data(Endianness::LE); + data.WriteVersion({1, 0, 0}); + data.Write(meshes.Size()); + + for (UInt_64 i = 0; i < meshes.Size(); ++i) + { + data.WriteStr(meshes[i].GetId()); + + Array vertices = meshes[i].GetVertices(); + + data.Write(vertices.Size()); + + for (UInt_64 v = 0; v < vertices.Size(); ++v) + { + data.WriteVec3(vertices[v].pos); + data.WriteVec3(vertices[v].normal); + data.WriteVec2(vertices[v].uv); + } + } + + file.WriteSerializer_64(data); + file.Truncate(data.Size()); + } + + void Model::FromEHM(File& file) + { + Serializer data = file.ReadSerializer_64(Endianness::LE, file.Size()); + + Version ver = data.ReadVersion(); + if (ver != Version(1, 0, 0)) + { + EHS_LOG_INT("Error", 0, "Cannot decode EHM file version " + Str_8::FromNum(ver.major) + "." + + Str_8::FromNum(ver.minor) + "." + Str_8::FromNum(ver.patch) + + ", must be version 0.0.0."); + return; + } + + meshes.Resize(data.Read()); + for (UInt_64 i = 0; i < meshes.Size(); ++i) + { + meshes[i].SetId(data.ReadStr()); + + Array& vertices = meshes[i].GetVertices(); + vertices.Resize(data.Read()); + + for (UInt_64 v = 0; v < vertices.Size(); ++v) + { + vertices[v].pos = data.ReadVec3(); + vertices[v].normal = data.ReadVec3(); + vertices[v].uv = data.ReadVec2(); + vertices[v].bones = data.ReadVec4(); + vertices[v].weights = data.ReadVec4(); + } + + meshes[i].SetIndices(data.ReadArray()); + + UInt_8 boneCount = data.Read(); + for (UInt_8 b = 0; b < boneCount; ++b) + { + Str_8 name = data.ReadStr(); + UInt_8 parentId = data.Read(); + Mat4_f localBindTrans = data.ReadMat4(); + Mat4_f invBindTrans = data.ReadMat4(); + if (b == 0) + { + skeleton = {name, b, localBindTrans, invBindTrans}; + } + else + { + Bone* parent = skeleton.GetBone(parentId); + if (!parent) + { + EHS_LOG_INT("Error", 0, "Invalid bone order."); + return; + } + + parent->AddBone({name, b, localBindTrans, invBindTrans}); + } + } + + animations = Array(data.Read()); + for (UInt_64 a = 0; a < animations.Size(); ++a) + { + Str_8 animId = data.ReadStr(); + float duration = data.Read(); + + animations[a] = Animation(animId, duration, data.Read()); + Array& animated = animations[a].GetAnimated(); + for (UInt_8 ba = 0; ba < (UInt_8)animated.Size(); ++ba) + { + UInt_8 boneId = data.Read(); + + animated[ba] = AnimBone(boneId, data.Read()); + Array& keyFrames = animated[ba].GetKeyFrames(); + for (UInt_64 kf = 0; kf < keyFrames.Size(); ++kf) + { + float num = data.Read(); + float timeStamp = data.Read(); + Vec3_f pos = data.ReadVec3(); + Quat_f rot = data.ReadQuat(); + Vec3_f scale = data.ReadVec3(); + + keyFrames[kf] = KeyFrame(num, timeStamp, pos, rot, scale); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/io/model/PropertyChange.cpp b/src/io/model/PropertyChange.cpp new file mode 100644 index 0000000..5606e5f --- /dev/null +++ b/src/io/model/PropertyChange.cpp @@ -0,0 +1,14 @@ +#include "ehs/io/model/PropertyChange.h" + +namespace ehs +{ + PropertyChange::PropertyChange() + : type(ChangeType::INVALID), value(0.0f) + { + } + + PropertyChange::PropertyChange(const ChangeType type, const float value) + : type(type), value(value) + { + } +} \ No newline at end of file diff --git a/src/io/socket/BaseTCP.cpp b/src/io/socket/BaseTCP.cpp new file mode 100644 index 0000000..959aa31 --- /dev/null +++ b/src/io/socket/BaseTCP.cpp @@ -0,0 +1,336 @@ +#include "ehs/io/socket/BaseTCP.h" +#include "ehs/Log.h" + +namespace ehs +{ + BaseTCP::BaseTCP() + : addrType(AddrType::IPV6), localPort(0), remotePort(0), connection(false), bound(false), listening(false), + connected(false) + { + } + + BaseTCP::BaseTCP(const AddrType addrType) + : addrType(addrType), localPort(0), remotePort(0), connection(false), bound(false), listening(false), + connected(false) + { + } + + BaseTCP::BaseTCP(BaseTCP&& tcp) noexcept + : addrType(tcp.addrType), localAddr(std::move(tcp.localAddr)), localPort(tcp.localPort), + remoteHostName(std::move(tcp.remoteHostName)), remoteAddr(std::move(tcp.remoteAddr)), remotePort(tcp.remotePort), + connection(tcp.connection), bound(tcp.bound), listening(tcp.listening), connected(tcp.connected) + { + } + + BaseTCP::BaseTCP(const BaseTCP& tcp) + : addrType(tcp.addrType), localPort(0), remotePort(0), connection(false), bound(false), listening(false), + connected(false) + { + } + + BaseTCP& BaseTCP::operator=(BaseTCP&& tcp) noexcept + { + if (this == &tcp) + return *this; + + addrType = tcp.addrType; + localAddr = std::move(tcp.localAddr); + localPort = tcp.localPort; + remoteHostName = std::move(tcp.remoteHostName); + remoteAddr = std::move(tcp.remoteAddr); + remotePort = tcp.remotePort; + connection = tcp.connection; + bound = tcp.bound; + listening = tcp.listening; + connected = tcp.connected; + + tcp.addrType = AddrType::IPV6; + tcp.localPort = 0; + tcp.remotePort = 0; + tcp.connection = false; + tcp.bound = false; + tcp.listening = false; + tcp.connected = false; + + return *this; + } + + BaseTCP& BaseTCP::operator=(const BaseTCP& tcp) + { + if (this == &tcp) + return *this; + + addrType = tcp.addrType; + localAddr = Str_8(); + localPort = 0; + remoteHostName = Str_8(); + remoteAddr = Str_8(); + remotePort = 0; + connection = false; + bound = false; + listening = false; + connected = false; + + return *this; + } + + void BaseTCP::SendStr(const Str_8& str) + { + if (!IsValid()) + return; + + UInt_64 offset = 0; + UInt_64 size = str.Size(true); + + while (offset < size) + { + UInt_64 sent = Send((Byte*)&str[offset], size - offset); + if (!sent) + { + EHS_LOG_INT("Error", 0, "Failed to send data."); + return; + } + + offset += sent; + } + } + + void BaseTCP::SendRes(const Response& res) + { + if (!IsValid()) + return; + + SendStr(res.FormResult()); + } + + void BaseTCP::SendReq(Request& req) + { + if (!IsValid()) + return; + + req.AddToHeader("Host", remoteHostName); + + SendStr(req.FormResult()); + } + + Response BaseTCP::RecvRes() + { + if (!IsValid()) + return {}; + + Str_8 header = RecvHeader(); + if (!header.Size()) + return {}; + + Response response(header); + + Str_8 encoding = response.GetHeader("Transfer-Encoding"); + if (!encoding.Size()) + { + int bodySize = response.GetHeader("content-length").ToDecimal(); + if (!bodySize) + return response; + + response.SetBody(RecvBody(bodySize)); + } + else if (encoding == "chunked") + { + Str_8 body; + UInt_64 chunkSize = RecvChunkSize(); + while (chunkSize) + { + body += RecvChunk(chunkSize); + chunkSize = RecvChunkSize(); + } + + response.SetBody(body); + } + + return response; + } + + Request BaseTCP::RecvReq() + { + if (!IsValid()) + return {}; + + Str_8 header = RecvHeader(); + if (!header.Size()) + return {}; + + Request request(header); + + if (request.GetVerb() == Verb::GET) + return request; + + Str_8 encoding = request.GetHeader("Transfer-Encoding"); + if (!encoding.Size()) + { + int bodySize = request.GetHeader("Content-Length").ToDecimal(); + if (!bodySize) + return request; + + request.SetBody(RecvBody(bodySize)); + } + else if (encoding == "chunked") + { + Str_8 body; + UInt_64 chunkSize = RecvChunkSize(); + while (chunkSize) + { + body += RecvChunk(chunkSize); + chunkSize = RecvChunkSize(); + } + + request.SetBody(body); + } + + return request; + } + + AddrType BaseTCP::GetAddressType() const + { + return addrType; + } + + Str_8 BaseTCP::GetLocalAddress() const + { + return localAddr; + } + + unsigned short BaseTCP::GetLocalPort() const + { + return localPort; + } + + Str_8 BaseTCP::GetRemoteAddress() const + { + return remoteAddr; + } + + unsigned short BaseTCP::GetRemotePort() const + { + return remotePort; + } + + bool BaseTCP::IsConnection() const + { + return connection; + } + + bool BaseTCP::IsBound() const + { + return bound; + } + + bool BaseTCP::IsListening() const + { + return listening; + } + + bool BaseTCP::IsConnected() const + { + return connected; + } + + Str_8 BaseTCP::RecvHeader() + { + Byte buffer[MaxHeaderSize]; + + UInt_64 offset = 0; + + while (true) + { + UInt_64 received = Receive(&buffer[offset], 1); + if (!received) + { + return {}; + } + else if (buffer[offset] == '\n' && offset - 3 && buffer[offset - 1] == '\r' && buffer[offset - 2] == '\n' && buffer[offset - 3] == '\r') + { + offset -= 3; + break; + } + + offset += received; + } + + return {(Char_8*)buffer, (UInt_64)offset}; + } + + Str_8 BaseTCP::RecvBody(const UInt_64 contentLength) + { + Str_8 buffer(contentLength); + + UInt_64 offset = 0; + + while (offset < contentLength) + { + UInt_64 received = Receive((Byte*)&buffer[offset], contentLength - offset); + if (!received) + { + EHS_LOG_INT("Error", 0, "Failed to receive data."); + return {}; + } + + offset += received; + } + + return buffer; + } + + UInt_64 BaseTCP::RecvChunkSize() + { + Str_8 hexSize(10); + + UInt_64 offset = 0; + + bool cr = false; + + while (true) + { + UInt_64 received = Receive((Byte*)&hexSize[offset], 1); + if (!received) + { + EHS_LOG_INT("Error", 0, "Failed to receive data."); + return 0; + } + else if (hexSize[offset] == '\r') + cr = true; + else if (cr && hexSize[offset] == '\n') + break; + + ++offset; + } + + if (hexSize[0] == '0') + Receive((Byte*)&hexSize[offset + 1], 2); + + hexSize.Resize(offset - 1); + + return hexSize.HexToNum(); + } + + Str_8 BaseTCP::RecvChunk(const UInt_64 chunkSize) + { + Str_8 buffer(chunkSize + 2); + + UInt_64 offset = 0; + + while (offset < chunkSize + 2) + { + UInt_64 received = Receive((Byte*)&buffer[offset], chunkSize + 2 - offset); + if (!received) + { + EHS_LOG_INT("Error", 0, "Failed to receive data."); + return {}; + } + + offset += received; + } + + buffer.Resize(offset - 2); + + return buffer; + } +} \ No newline at end of file diff --git a/src/io/socket/BaseUDP.cpp b/src/io/socket/BaseUDP.cpp new file mode 100644 index 0000000..6af17a9 --- /dev/null +++ b/src/io/socket/BaseUDP.cpp @@ -0,0 +1,77 @@ +#include "ehs/io/socket/BaseUDP.h" + +namespace ehs +{ + BaseUDP::BaseUDP() + : type(AddrType::IPV6), port(0), bound(false) + { + } + + BaseUDP::BaseUDP(const AddrType type) + : type(type), port(0), bound(false) + { + } + + BaseUDP::BaseUDP(BaseUDP&& udp) noexcept + : type(udp.type), address(std::move(udp.address)), port(udp.port), bound(true) + { + udp.type = AddrType::IPV6; + udp.port = 0; + udp.bound = false; + } + + BaseUDP::BaseUDP(const BaseUDP& udp) + : type(udp.type), address(udp.address), port(udp.port), bound(false) + { + } + + BaseUDP& BaseUDP::operator=(BaseUDP&& udp) noexcept + { + if (this == &udp) + return *this; + + type = udp.type; + address = std::move(udp.address); + port = udp.port; + bound = udp.bound; + + udp.type = AddrType::IPV6; + udp.port = 0; + udp.bound = false; + + return *this; + } + + BaseUDP& BaseUDP::operator=(const BaseUDP& udp) + { + if (this == &udp) + return *this; + + type = udp.type; + address = udp.address; + port = udp.port; + bound = false; + + return *this; + } + + bool BaseUDP::IsBound() const + { + return bound; + } + + AddrType BaseUDP::GetLocalAddressType() const + { + return type; + } + + Str_8 BaseUDP::GetLocalAddress() const + { + return address; + } + + UInt_16 BaseUDP::GetLocalPort() const + { + return port; + } +} \ No newline at end of file diff --git a/src/io/socket/DNS.cpp b/src/io/socket/DNS.cpp new file mode 100644 index 0000000..d54d521 --- /dev/null +++ b/src/io/socket/DNS.cpp @@ -0,0 +1,75 @@ +#include "ehs/io/socket/DNS.h" +#include "ehs/Log.h" + +#if defined(EHS_OS_WINDOWS) + #include + #include +#elif defined(EHS_OS_LINUX) + #include + #include + #include +#endif + +namespace ehs +{ + Str_8 DNS::Resolve(const AddrType addrType, const Str_8& hostName) + { + #if defined(EHS_OS_WINDOWS) + WSADATA data = {}; + + Int_32 wsaCode = WSAStartup(MAKEWORD(2, 2), &data); + if (wsaCode) + { + EHS_LOG_INT("Error", 0, "Failed to start WSA with error #" + Str_8::FromNum(wsaCode) + "."); + return {}; + } + #endif + + addrinfo hints = {}; + if (addrType == AddrType::IPV6) + hints.ai_family = AF_INET6; + else if (addrType == AddrType::IPV4) + hints.ai_family = AF_INET; + + addrinfo* result = nullptr; + + Int_32 code = getaddrinfo(hostName, nullptr, &hints, &result); + if (code) + { + EHS_LOG_INT("Error", 1, "Failed to resolve host with error #" + Str_8::FromNum(code) + "."); + return {}; + } + + #if defined(EHS_OS_WINDOWS) + if (WSACleanup() == SOCKET_ERROR) + { + EHS_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + return {}; + } + #endif + + + if (addrType == AddrType::IPV6) + { + Char_8 ipResult[INET6_ADDRSTRLEN]; + + inet_ntop(result->ai_family, &((sockaddr_in6*)result->ai_addr)->sin6_addr, ipResult, INET6_ADDRSTRLEN); + + freeaddrinfo(result); + + return ipResult; + } + else if (addrType == AddrType::IPV4) + { + Char_8 ipResult[INET_ADDRSTRLEN]; + + inet_ntop(result->ai_family, &((sockaddr_in*)result->ai_addr)->sin_addr, ipResult, INET_ADDRSTRLEN); + + freeaddrinfo(result); + + return ipResult; + } + + return {}; + } +} \ No newline at end of file diff --git a/src/io/socket/Request.cpp b/src/io/socket/Request.cpp new file mode 100644 index 0000000..c61c7eb --- /dev/null +++ b/src/io/socket/Request.cpp @@ -0,0 +1,337 @@ +#include "ehs/io/socket/Request.h" +#include "ehs/Base64.h" + +namespace ehs +{ + Request::Request() + : verb(Verb::GET), cType(ContentType::NONE) + { + } + + Request::Request(const Verb verb, const Str_8& rsrc) + : verb(verb), rsrc(rsrc), cType(ContentType::NONE) + { + } + + Request::Request(const char* data, const UInt_64 size) + : verb(Verb::POST), cType(ContentType::NONE) + { + ReadData(Str_8(data, size)); + } + + Request::Request(const Str_8& data) + : verb(Verb::POST), cType(ContentType::NONE) + { + ReadData(data); + } + + Request& Request::operator=(const Request &req) + { + if (this == &req) + return *this; + + verb = req.verb; + rsrc = req.rsrc; + queries = req.queries; + header = req.header; + cType = req.cType; + body = req.body; + + return* this; + } + + Verb Request::GetVerb() const + { + return verb; + } + + void Request::SetContentType(const ContentType cType) + { + if (body.Size()) + body.Resize(0); + + this->cType = cType; + } + + ContentType Request::GetContentType() const + { + return cType; + } + + void Request::SetResource(const Str_8& rsrc) + { + this->rsrc = rsrc; + } + + Str_8 Request::GetResource() const + { + return rsrc; + } + + void Request::AddQuery(const Str_8& var, const Str_8& value) + { + queries.Push(var + "=" + value); + } + + Str_8 Request::GetQuery(const Str_8& var) + { + for (UInt_64 i = 0; i < queries.Size(); ++i) + { + Vector data = queries[i].Split("="); + + if (data[0] == var) + return data[1]; + } + + return ""; + } + + Vector Request::GetQueries() const + { + return queries; + } + + void Request::BasicAuth(const Str_8& id, const Str_8& secret) + { + AddToHeader("Authorization", Str_8("Basic ") + Base64::Encode(id + ":" + secret)); + } + + void Request::BearerAuth(const Str_8& token) + { + AddToHeader("Authorization", "Bearer " + token); + } + + void Request::BearerAuth(const Str_8& token, const Str_8& clientId) + { + AddToHeader("Authorization", "Bearer " + token); + AddToHeader("Client-Id", clientId); + } + + void Request::BotAuth(const Str_8& token) + { + AddToHeader("Authorization", "Bot " + token); + } + + void Request::AddToHeader(const Str_8& var, const Str_8& value) + { + header.Push(var + ": " + value); + } + + Str_8 Request::GetHeader(const Str_8& var) const + { + for (UInt_64 i = 0; i < header.Size(); ++i) + { + Vector data = header[i].Split(": "); + + if (data[0] == var) + return data[1]; + } + + return ""; + } + + Vector Request::GetHeader() const + { + return header; + } + + void Request::AddToBody(const Str_8& var, const Str_8& value) + { + if (body.Size()) + { + if (cType == ContentType::APP_FORMURLENCODED) + body.Push('&'); + } + + body += var; + + if (cType == ContentType::APP_FORMURLENCODED) + body += "="; + + body += value; + } + + void Request::AddToBody(const Str_8& data) + { + if (body.Size()) + { + if (cType == ContentType::APP_FORMURLENCODED) + body.Push('&'); + } + + body += data; + } + + void Request::SetBody(const Str_8& body) + { + this->body = body; + } + + Str_8 Request::GetVar(const Str_8& var) const + { + Vector vars; + + if (cType == ContentType::APP_FORMURLENCODED) + vars = body.Split("&"); + + for (UInt_64 i = 0; i < vars.Size(); ++i) + { + Vector data = vars[i].Split("="); + + if (data[0] == var) + return data[1]; + } + + return ""; + } + + Str_8 Request::GetBody() const + { + return body; + } + + Json Request::GetJson() const + { + return {body, 5}; + } + + Str_8 Request::FormResult() const + { + Str_8 result = VerbToStr(verb) + " " + rsrc; + + if (queries.Size()) + result += "?" + queries[0]; + + for (UInt_64 i = 1; i < queries.Size(); ++i) + result += "&" + queries[i]; + + result += " HTTP/1.1\r\n"; + + for (UInt_64 i = 0; i < header.Size(); ++i) + { + result += header[i] + "\r\n"; + } + + result += "Content-Type: " + ContentTypeToStr(cType) + "\r\n"; + + if (verb == Verb::GET) + result += "\r\n"; + else + result += "Content-Length: " + Str_8::FromNum(body.Size()) + "\r\n\r\n" + body; + + return result; + } + + bool Request::IsValid() const + { + return rsrc.Size() || queries.Size() || header.Size() || body.Size(); + } + + Str_8 Request::VerbToStr(const Verb verb) + { + switch (verb) + { + case Verb::POST: + return "POST"; + case Verb::GET: + return "GET"; + case Verb::PUT: + return "PUT"; + case Verb::DEL: + return "DELETE"; + default: + return ""; + } + } + + Str_8 Request::ContentTypeToStr(const ContentType cType) + { + switch (cType) + { + case ContentType::APP_MULTIPART_FORMDATA: + return "multipart/form-data"; + case ContentType::APP_FORMURLENCODED: + return "application/x-www-form-urlencoded"; + case ContentType::APP_JAVASCRIPT: + return "application/javascript"; + case ContentType::APP_JSON: + return "application/json"; + case ContentType::APP_XML: + return "application/xml"; + case ContentType::TEXT_PLAIN: + return "text/plain"; + case ContentType::TEXT_HTML: + return "text/html"; + case ContentType::TEXT_XML: + return "text/xml"; + default: + return ""; + } + } + + ContentType Request::StrToContentType(const Str_8& value) + { + if (value == "multipart/form-data") + return ContentType::APP_MULTIPART_FORMDATA; + else if (value == "application/x-www-form-urlencoded") + return ContentType::APP_FORMURLENCODED; + else if (value == "application/javascript") + return ContentType::APP_JAVASCRIPT; + else if (value == "application/json") + return ContentType::APP_JSON; + else if (value == "application/xml") + return ContentType::APP_XML; + else if (value == "text/plain") + return ContentType::TEXT_PLAIN; + else if (value == "text/html") + return ContentType::TEXT_HTML; + else if (value == "text/xml") + return ContentType::TEXT_XML; + else + return ContentType::NONE; + } + + void Request::ReadData(const Str_8& data) + { + Vector lines = data.Split("\r\n"); + Vector meta = lines[0].Split(" "); + + if (meta[0] == "POST") + verb = Verb::POST; + else if (meta[0] == "GET") + verb = Verb::GET; + else if (meta[0] == "PUT") + verb = Verb::PUT; + + UInt_64 queryIndex = 0; + if (meta[1].Find("?", &queryIndex)) + { + rsrc = meta[1].Sub(0, queryIndex); + cType = ContentType::APP_FORMURLENCODED; + queries = meta[1].Sub(queryIndex + 1).Split("&"); + } + else + { + rsrc = meta[1]; + } + + for (UInt_64 i = 1; i < lines.Size(); ++i) + { + if (!lines[i].Size()) + break; + + Vector var = lines[i].Split(": "); + + if (var[0] == "Content-Type") + { + cType = StrToContentType(var[1]); + continue; + } + + if (var[0] == "Content-Length") + continue; + + header.Push(lines[i]); + } + } +} \ No newline at end of file diff --git a/src/io/socket/Response.cpp b/src/io/socket/Response.cpp new file mode 100644 index 0000000..c5065dd --- /dev/null +++ b/src/io/socket/Response.cpp @@ -0,0 +1,403 @@ +#include "ehs/io/socket/Response.h" + +namespace ehs +{ + Response::Response(const UInt_32 code, const Str_8& server) + : code(code), server(server), cType(ContentType::NONE) + { + } + + Response::Response(const char* data, const UInt_64 size) + : code(0), cType(ContentType::NONE) + { + ReadData(Str_8(data, size)); + } + + Response::Response(const Str_8& data) + : code(0), cType(ContentType::NONE) + { + ReadData(data); + } + + Response::Response() + : code(0), cType(ContentType::NONE) + { + } + + Response& Response::operator=(const Response& res) + { + if (this == &res) + return *this; + + code = res.code; + server = res.server; + cType = res.cType; + header = res.header; + body = res.body; + + return *this; + } + + void Response::SetCode(const UInt_32 code) + { + this->code = code; + } + + UInt_32 Response::GetCode() const + { + return code; + } + + void Response::SetServer(const Str_8& server) + { + this->server = server; + } + + Str_8 Response::GetServer() const + { + return server; + } + + void Response::SetContentType(const ContentType cType) + { + this->cType = cType; + } + + ContentType Response::GetContentType() const + { + return cType; + } + + void Response::AddToHeader(const Str_8& var, const Str_8& value) + { + header.Push(var + ": " + value); + } + + Str_8 Response::GetHeader(const Str_8& var) const + { + Str_8 lIdentity = var.GetLower(); + + for (UInt_64 i = 0; i < header.Size(); ++i) + { + Vector data = header[i].Split(": "); + + if (data[0].GetLower() == lIdentity) + return data[1]; + } + + return ""; + } + + Vector Response::GetHeader() const + { + return header; + } + + void Response::AddToBody(const Str_8& var, const Str_8& value) + { + if (body.Size()) + { + if (cType == ContentType::APP_FORMURLENCODED) + body.Push('&'); + } + + body += var; + + if (cType == ContentType::APP_FORMURLENCODED) + body += "="; + + body += value; + } + + void Response::AddToBody(const Str_8& data) + { + if (body.Size()) + { + if (cType == ContentType::APP_FORMURLENCODED) + body.Push('&'); + } + + body += data; + } + + void Response::SetBody(const Str_8& body) + { + this->body = body; + } + + Str_8 Response::GetVar(const Str_8& var) const + { + Vector vars; + + if (cType == ContentType::APP_FORMURLENCODED) + vars = body.Split("&"); + + for (UInt_64 i = 0; i < vars.Size(); ++i) + { + Vector data = vars[i].Split("="); + + if (data[0] == var) + return data[1]; + } + + return ""; + } + + Str_8 Response::GetBody() const + { + return body; + } + + Json Response::GetJson() const + { + return {body, 5}; + } + + Str_8 Response::FormResult() const + { + Str_8 result = "HTTP/1.1 " + Str_8::FromNum(code) + " " + CodeToStr(code) + "\r\nServer: " + server + "\r\n"; + + for (UInt_64 i = 0; i < header.Size(); ++i) + { + if (header[i].Find("Content-Length", nullptr, SearchPattern::LEFT_RIGHT, IndexResult::ENDING)) + continue; + else if (header[i].Find("Server", nullptr, SearchPattern::LEFT_RIGHT, IndexResult::ENDING)) + continue; + else if (header[i].Find("Content-Type", nullptr, SearchPattern::LEFT_RIGHT, IndexResult::ENDING)) + continue; + + result += header[i] + "\r\n"; + } + + result += "Content-Type: " + ContentTypeToStr(cType) + "\r\nContent-Length: " + Str_8::FromNum(body.Size()) + "\r\n\r\n" + body; + + return result; + } + + bool Response::IsValid() const + { + return server.Size() || header.Size() || body.Size(); + } + + Str_8 Response::CodeToStr(const UInt_32 code) + { + if (code == 100) + return "Continue"; + else if (code == 101) + return "Switching Protocols"; + else if (code == 102) + return "Processing (WebDAV)"; + else if (code == 200) + return "OK"; + else if (code == 201) + return "Created"; + else if (code == 202) + return "Accepted"; + else if (code == 203) + return "Non-Authoritative Information"; + else if (code == 204) + return "No Content"; + else if (code == 205) + return "Reset Content"; + else if (code == 206) + return "Partial Content"; + else if (code == 207) + return "Multi-Status (WebDAV)"; + else if (code == 208) + return "Already Reported (WebDAV)"; + else if (code == 226) + return "IM Used"; + else if (code == 300) + return "Multiple Choices"; + else if (code == 301) + return "Moved Permanently"; + else if (code == 302) + return "Found"; + else if (code == 303) + return "See Others"; + else if (code == 304) + return "Not Modified"; + else if (code == 305) + return "Use Proxy"; + else if (code == 306) + return "(Unused)"; + else if (code == 307) + return "Temporary Redirect"; + else if (code == 308) + return "Permanent Redirect (experimental)"; + else if (code == 400) + return "Bad Request"; + else if (code == 401) + return "Unauthorized"; + else if (code == 402) + return "Payment Required"; + else if (code == 403) + return "Forbidden"; + else if (code == 404) + return "Not Found"; + else if (code == 405) + return "Method Not Allowed"; + else if (code == 406) + return "Not Acceptable"; + else if (code == 407) + return "Proxy Authentication Required"; + else if (code == 408) + return "Request Timeout"; + else if (code == 409) + return "Conflict"; + else if (code == 410) + return "Gone"; + else if (code == 411) + return "Length Required"; + else if (code == 412) + return "Precondition Failed"; + else if (code == 413) + return "Request Entity Too Large"; + else if (code == 414) + return "Request-URI Too Long"; + else if (code == 415) + return "Unsupported Media Type"; + else if (code == 416) + return "Requested Range Not Satisfiable"; + else if (code == 417) + return "Expectation Failed"; + else if (code == 418) + return "I'm a teapot (RFC 2324)"; + else if (code == 420) + return "Enhance Your Calm (Twitter)"; + else if (code == 422) + return "Unprocessable Entity (WebDAV)"; + else if (code == 423) + return "Locked (WebDAV)"; + else if (code == 424) + return "Failed Dependency (Nginx)"; + else if (code == 425) + return "Reserved for WebDAV"; + else if (code == 426) + return "Upgrade Required"; + else if (code == 428) + return "Precondition Required"; + else if (code == 429) + return "Too Many Requests"; + else if (code == 431) + return "Request Header Fields Too Large"; + else if (code == 444) + return "No Response (Nginx)"; + else if (code == 449) + return "Retry With (Microsoft)"; + else if (code == 450) + return "Blocked by Windows Parental Controls (Microsoft)"; + else if (code == 451) + return "Unavailable For Legal Reasons"; + else if (code == 499) + return "Client Closed Request (Nginx)"; + else if (code == 500) + return "Internal Server Error"; + else if (code == 501) + return "Not Implemented"; + else if (code == 502) + return "Bad Gateway"; + else if (code == 503) + return "Service Unavailable"; + else if (code == 504) + return "Gateway Timeout"; + else if (code == 505) + return "HTTP Version Not Supported"; + else if (code == 506) + return "Variant Also Negotiates (Experimental)"; + else if (code == 507) + return "Insufficient Storage (WebDAV)"; + else if (code == 508) + return "Loop Detected (WebDAV)"; + else if (code == 509) + return "Bandwidth Limit Exceeded (Apache)"; + else if (code == 510) + return "Not Extended"; + else if (code == 511) + return "Network Authentication Required"; + else if (code == 598) + return "Network read timeout error"; + else if (code == 599) + return "Network connect timeout error"; + else + return "Unused Status Code"; + } + + Str_8 Response::ContentTypeToStr(const ContentType cType) + { + switch (cType) + { + case ContentType::APP_MULTIPART_FORMDATA: + return "multipart/form-data"; + case ContentType::APP_FORMURLENCODED: + return "application/x-www-form-urlencoded"; + case ContentType::APP_JAVASCRIPT: + return "application/javascript"; + case ContentType::APP_JSON: + return "application/json"; + case ContentType::APP_XML: + return "application/xml"; + case ContentType::TEXT_PLAIN: + return "text/plain"; + case ContentType::TEXT_HTML: + return "text/html"; + case ContentType::TEXT_XML: + return "text/xml"; + default: + return ""; + } + } + + ContentType Response::StrToContentType(const Str_8& value) + { + if (value == "multipart/form-data") + return ContentType::APP_MULTIPART_FORMDATA; + else if (value == "application/x-www-form-urlencoded") + return ContentType::APP_FORMURLENCODED; + else if (value == "application/javascript") + return ContentType::APP_JAVASCRIPT; + else if (value == "application/json") + return ContentType::APP_JSON; + else if (value == "application/xml") + return ContentType::APP_XML; + else if (value == "text/plain") + return ContentType::TEXT_PLAIN; + else if (value == "text/html") + return ContentType::TEXT_HTML; + else if (value == "text/xml") + return ContentType::TEXT_XML; + else + return ContentType::NONE; + } + + void Response::ReadData(const Str_8& data) + { + Vector lines = data.Split("\r\n"); + Vector meta = lines[0].Split(" "); + + code = meta[1].ToDecimal(); + + for (UInt_64 i = 1; i < lines.Size(); ++i) + { + if (!lines[i].Size()) + break; + + Vector var = lines[i].Split(": "); + + if (var[0].GetLower() == "server") + { + server = var[1]; + continue; + } + else if (var[0].GetLower() == "content-type") + { + Vector ctData = var[1].Split(";"); + + cType = StrToContentType(ctData[0].GetLower()); + continue; + } + + header.Push(lines[i]); + } + } +} \ No newline at end of file diff --git a/src/io/socket/SSL.cpp b/src/io/socket/SSL.cpp new file mode 100644 index 0000000..a0764e1 --- /dev/null +++ b/src/io/socket/SSL.cpp @@ -0,0 +1,236 @@ +#include "ehs/io/socket/SSL.h" + +#include +#include +#include +#include + +namespace ehs +{ + SSL::~SSL() + { + if (!IsValid()) + return; + + if (sslHdl) + { + if (connection) + SSL_shutdown(sslHdl); + + SSL_free(sslHdl); + } + + if (ctx) + SSL_CTX_free(ctx); + } + + SSL::SSL() + : ctx(nullptr), sslHdl(nullptr) + { + } + + SSL::SSL(const AddrType type) + : TCP(type), ctx(nullptr), sslHdl(nullptr) + { + SSL::Initialize(); + } + + SSL::SSL(TCP&& tcp) noexcept + : TCP(std::move(tcp)), ctx(nullptr), sslHdl(nullptr) + { + } + + SSL::SSL(const TCP& tcp) + : TCP(tcp), ctx(nullptr), sslHdl(nullptr) + { + } + + SSL::SSL(const SSL& ssl) + : TCP(ssl), ctx(nullptr), sslHdl(nullptr) + { + } + + SSL& SSL::operator=(const SSL& ssl) + { + if (this == &ssl) + return *this; + + TCP::operator=(ssl); + + ctx = nullptr; + sslHdl = nullptr; + + return *this; + } + + void SSL::Initialize() + { + TCP::Initialize(); + + if (IsValid()) + return; + + SSL_library_init(); + } + + void SSL::Release() + { + TCP::Release(); + + if (!IsValid()) + return; + + if (sslHdl) + { + if (connection) + SSL_shutdown(sslHdl); + + SSL_free(sslHdl); + + sslHdl = nullptr; + } + + if (ctx) + { + SSL_CTX_free(ctx); + ctx = nullptr; + } + } + + void SSL::Bind(const Str_8& address, unsigned short port) + { + if (bound) + return; + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + ctx = SSL_CTX_new(SSLv23_server_method()); + + sslHdl = SSL_new(ctx); + SSL_set_fd(sslHdl, hdl); + + TCP::Bind(address, port); + } + + SSL* SSL::Accept() + { + if (!bound) + return nullptr; + + TCP* tcp = TCP::Accept(); + + SSL* client = new SSL(std::move(*tcp)); + + delete tcp; + + client->ctx = nullptr; + client->sslHdl = SSL_new(ctx); + SSL_set_fd(client->sslHdl, client->hdl); + + int err = SSL_accept(client->sslHdl); + if (!err) + { + EHS_LOG_INT("Error", 0, "Failed SSL handshake with error #" + Str_8::FromNum(SSL_get_error(client->sslHdl, err)) + "."); + return {}; + } + + return client; + } + + void SSL::Connect(const Str_8& address, const UInt_16 port) + { + if (bound) + return; + + TCP::Connect(address, port); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + ctx = SSL_CTX_new(SSLv23_client_method()); + sslHdl = SSL_new(ctx); + SSL_set_fd(sslHdl, hdl); + SSL_connect(sslHdl); + } + + UInt_64 SSL::Send(const Byte* const buffer, const UInt_32 size) + { + int written = SSL_write(sslHdl, buffer, (int)size); + if (written <= 0) + { + int code = SSL_get_error(sslHdl, written); + ERR_print_errors_fp(stderr); + EHS_LOG_INT("Error", 0, "Failed to send data with error #" + Str_8::FromNum(code) + "."); + return 0; + } + + return written; + } + + UInt_64 SSL::Receive(Byte* const buffer, const UInt_32 size) + { + int received = SSL_read(sslHdl, buffer, (int)size); + if (received <= 0) + { + int code = SSL_get_error(sslHdl, received); + ERR_print_errors_fp(stderr); + EHS_LOG_INT("Error", 0, "Failed to receive data with error #" + Str_8::FromNum(code) + "."); + return 0; + } + + return received; + } + + void SSL::UseCertificate(const Byte* data, const UInt_64 size) + { + X509 *cert = d2i_X509(nullptr, &data, (long)size); + if (!cert) + { + EHS_LOG_INT("Error", 0, "Invalid certificate."); + return; + } + + if (SSL_CTX_use_certificate(ctx, cert) != 1) + { + EHS_LOG_INT("Error", 1, "Failed to use certificate."); + return; + } + + X509_free(cert); + } + + void SSL::UsePrivateKey(const Byte* data, const UInt_64 size) + { + EVP_PKEY *key = d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &data, (long)size); + if (!key) + { + EHS_LOG_INT("Error", 0, "Invalid private key."); + return; + } + + if (SSL_CTX_use_PrivateKey(ctx, key) != 1) + { + EHS_LOG_INT("Error", 1, "Failed to use private key."); + return; + } + + EVP_PKEY_free(key); + } + + bool SSL::IsValid() + { + return TCP::IsValid() && sslHdl; + } +} + + + + + + + + + + + + + + diff --git a/src/io/socket/Socket.cpp b/src/io/socket/Socket.cpp new file mode 100644 index 0000000..a871291 --- /dev/null +++ b/src/io/socket/Socket.cpp @@ -0,0 +1,1316 @@ +#include "io/socket/Socket.h" +#include "io/socket/Endpoint.h" +#include "system/System.h" +#include "ehs/Encryption.h" + +#if defined(EHS_OS_WINDOWS) + #include + #include +#elif defined(EHS_OS_LINUX) + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +namespace ehc +{ + const Version Socket::ver(1, 0, 0); + const UInt_64 Socket::internalSys = Str_8::Hash_64("Internal"); + const UInt_64 Socket::connectOp = Str_8::Hash_64("Connect"); + const UInt_64 Socket::connectedOp = Str_8::Hash_64("Connected"); + const UInt_64 Socket::rejectedOp = Str_8::Hash_64("Rejected"); + const UInt_64 Socket::disconnectOp = Str_8::Hash_64("Disconnect"); + const UInt_64 Socket::disconnectedOp = Str_8::Hash_64("Disconnected"); + const UInt_64 Socket::statusUpdateOp = Str_8::Hash_64("StatusUpdate"); + const UInt_64 Socket::pingOp = Str_8::Hash_64("Ping"); + const UInt_64 Socket::pongOp = Str_8::Hash_64("Pong"); + const UInt_64 Socket::latencyOp = Str_8::Hash_64("Latency"); + const UInt_64 Socket::receivedOp = Str_8::Hash_64("Received"); + + Socket::~Socket() + { + UnInitialize(); + } + + Socket::Socket() + : hdl(EHS_INVALID_SOCKET), type(AddrType::IPV4), port(0), bound(false), appVer(0, 0, 0), + disposition(Disposition::UNKNOWN), dropPackets(false), hashId(0), buffer(nullptr), bufferSize(0), + maxEndpoints(0), lastTSC(0), delta(0.0f), maxTimeout(5.0f), resendRate(0.5f), connectedCb(nullptr), + activeCb(nullptr), disconnectedCb(nullptr) + { + AddType("Socket"); + } + + Socket::Socket(const Version& ver, const Disposition disposition, const Str_8& id, const UInt_64 maxEndpoints) + : hdl(EHS_INVALID_SOCKET), type(AddrType::IPV4), port(0), bound(false), appVer(ver), disposition(disposition), + dropPackets(false), id(id), hashId(id.Hash_32()), buffer(nullptr), bufferSize(0), + maxEndpoints(maxEndpoints), lastTSC(CPU::GetTSC()), delta(0.0f), maxTimeout(5.0f), resendRate(0.5f), + connectedCb(nullptr), activeCb(nullptr), disconnectedCb(nullptr) + { + } + + Socket::Socket(const Socket& sock) + : BaseObj(sock), hdl(EHS_INVALID_SOCKET), type(sock.type), address(sock.address), port(sock.port), bound(false), + appVer(sock.appVer), disposition(sock.disposition), dropPackets(sock.dropPackets), + id(sock.id), hashId(sock.hashId), buffer(nullptr), bufferSize(0), maxEndpoints(sock.maxEndpoints), + lastTSC(CPU::GetTSC()), delta(0.0f), maxTimeout(sock.maxTimeout), resendRate(sock.resendRate), + connectedCb(nullptr), activeCb(nullptr), disconnectedCb(nullptr) + { + AddType("Socket"); + } + + Socket& Socket::operator=(const Socket& sock) + { + if (this == &sock) + return *this; + + BaseObj::operator=(sock); + + hdl = EHS_INVALID_SOCKET; + type = sock.type; + address = sock.address; + port = sock.port; + bound = false; + appVer = sock.appVer; + disposition = sock.disposition; + dropPackets = sock.dropPackets; + id = sock.id; + hashId = sock.hashId; + buffer = nullptr; + bufferSize = 0; + systems = Array(); + endpoints = Vector(); + maxEndpoints = sock.maxEndpoints; + lastTSC = 0; + delta = 0.0f; + maxTimeout = sock.maxTimeout; + resendRate = sock.resendRate; + connectedCb = nullptr; + activeCb = nullptr; + disconnectedCb = nullptr; + + return *this; + } + + void Socket::Initialize() + { + if (hdl != EHS_INVALID_SOCKET) + return; + + #if defined(EHS_OS_WINDOWS) + WSADATA data = {}; + + int wsaCode = WSAStartup(MAKEWORD(2, 2), &data); + if (wsaCode) + { + EHS_LOG_INT("Error", 0, "WSAStartup failed with the error #" + Str_8::FromNum(wsaCode) + "."); + return; + } + #endif + + if (type == AddrType::IPV6) + hdl = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + else if (type == AddrType::IPV4) + hdl = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + else + return; + + if (hdl == EHS_INVALID_SOCKET) + { + UInt_32 code = 0; + + #if defined(EHS_OS_WINDOWS) + code = WSAGetLastError(); + #elif defined(EHS_OS_LINUX) + code = errno; + #endif + + EHS_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + "."); + + #if defined(EHS_OS_WINDOWS) + if (WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + #endif + + return; + } + + if (type == AddrType::IPV4) + { + buffer = new Byte[EHS_IPV4_UDP_PAYLOAD]; + bufferSize = EHS_IPV4_UDP_PAYLOAD; + } + else if (type == AddrType::IPV6) + { + buffer = new Byte[EHS_IPV6_UDP_PAYLOAD]; + bufferSize = EHS_IPV6_UDP_PAYLOAD; + } + } + + void Socket::UnInitialize() + { + if (hdl == EHS_INVALID_SOCKET) + return; + + delete[] buffer; + buffer = nullptr; + bufferSize = 0; + + Serializer payload(Endianness::LE); + payload.Write(0); + + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetStatus() != Status::PENDING) + endpoints[i]->Send(false, true, false, internalSys, disconnectOp, payload); + + delete endpoints[i]; + } + endpoints.Clear(); + + for (UInt_64 i = 0; i < systems.Size(); ++i) + delete systems[i]; + systems.Clear(); + + Int_32 code = 0; + + #if defined(EHS_OS_WINDOWS) + code = closesocket(hdl); + if (code == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + "."); + #elif defined(EHS_OS_LINUX) + code = close(hdl); + if (code == -1) + EHS_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + "."); + #endif + + hdl = EHS_INVALID_SOCKET; + + #if defined(EHS_OS_WINDOWS) + if (WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 1, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + #endif + + bound = false; + } + + void Socket::Bind(const Str_8& newAddress, const UInt_16 newPort) + { + if (hdl == EHS_INVALID_SOCKET || bound) + return; + + if (type == AddrType::IPV6) + Bind_v6(newAddress, newPort); + else if (type == AddrType::IPV4) + Bind_v4(newAddress, newPort); + + address = newAddress; + port = newPort; + + bound = true; + } + + void Socket::Connect(const Str_8& address, const UInt_16 port) + { + if (hdl == EHS_INVALID_SOCKET) + return; + + Serializer payload(Endianness::LE); + payload.Write(CPU::GetArchitecture()); + payload.WriteStr(id); + payload.WriteVersion(ver); + payload.WriteVersion(appVer); + + Endpoint* end = new Endpoint(hdl, type, address, port); + end->SetParent(this); + end->Send(false, true, false, "Internal", "Connect", payload); + + endpoints.Push(end); + } + + bool Socket::Disconnect(const Disposition disposition, const UInt_64 hashId, const Str_8& msg) + { + if (hdl == EHS_INVALID_SOCKET) + return false; + + Endpoint* end = GetEndpoint(disposition, hashId); + if (!end) + return false; + + Str_8 dcMsg = end->GetId() + " has been disconnected."; + if (msg.Size()) + dcMsg += " Reason: " + msg; + + EHS_LOG_INT("Info", 0, dcMsg); + + Serializer<> payload(Endianness::LE); + payload.WriteStr(msg); + + end->Send(false, true, false, internalSys, disconnectOp, payload); + + return true; + } + + bool Socket::Disconnect(const Disposition disposition, const Str_8& id, const Str_8& msg) + { + return Disconnect(disposition, id.Hash_32(), msg); + } + + void Socket::Broadcast(const Disposition disposition, const Status status, const bool deltaLocked, const bool encrypted, + const bool ensure, const UInt_64 sysHashId, const UInt_64 opHashId, + const Serializer<>& payload) + { + if (hdl == EHS_INVALID_SOCKET) + return; + + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetStatus() == status) + endpoints[i]->Send(deltaLocked, encrypted, ensure, sysHashId, opHashId, payload); + } + } + + void Socket::Broadcast(const Disposition disposition, const Status status, const bool deltaLocked, const bool encrypted, + const bool ensure, const Str_8& sysId, const Str_8& opId, + const Serializer<>& payload) + { + Broadcast(disposition, status, deltaLocked, encrypted, ensure, sysId.Hash_64(), opId.Hash_64(), payload); + } + + void Socket::Poll() + { + if (hdl == EHS_INVALID_SOCKET) + return; + + UInt_64 newTSC = CPU::GetTSC(); + delta = (float)(newTSC - lastTSC) / (float)CPU::GetTSC_Freq(); + lastTSC = newTSC; + + Str_8 rAddress; + UInt_16 rPort = 0; + + UInt_16 received = 0; + + while ((received = Receive(&rAddress, &rPort, buffer, bufferSize))) + { + Serializer<> payload(Endianness::LE, buffer, received); + + bool encrypted = payload.Read(); + if (encrypted) + Encryption::Encrypt_64(payload.Size() - payload.GetOffset(), &payload[payload.GetOffset()]); + + payload.SetOffset(0); + + Header header = payload.Read
(); + + if (!header.ensure && header.endpointId && header.system == internalSys && header.op == connectOp) + { + Architecture rArch = payload.Read(); + Str_8 rId = payload.ReadStr(); + + Endpoint* end = new Endpoint(hdl, header.disposition, rArch, rId, type, rAddress, rPort); + end->SetStatus(Status::PENDING); + end->SetParent(this); + + Serializer sPayload(Endianness::LE); + + Version rVer = payload.ReadVersion(); + if (rVer != ver) + { + sPayload.WriteStr("Your Event Horizon Socket Layer version " + + Str_8::FromNum(rVer.major) + "." + Str_8::FromNum(rVer.minor) + "." + Str_8::FromNum(rVer.patch) + + " does not match remote endpoint version " + + Str_8::FromNum(ver.major) + "." + Str_8::FromNum(ver.minor) + "." + Str_8::FromNum(ver.patch) + + ". Connection rejected."); + + end->Send(false, true, false, internalSys, rejectedOp, sPayload); + continue; + } + + Version rAppVer = payload.ReadVersion(); + if (rAppVer != appVer) + { + sPayload.WriteStr("Your application version " + + Str_8::FromNum(rAppVer.major) + "." + Str_8::FromNum(rAppVer.minor) + "." + Str_8::FromNum(rAppVer.patch) + + " does not match remote endpoint application version " + + Str_8::FromNum(appVer.major) + "." + Str_8::FromNum(appVer.minor) + "." + Str_8::FromNum(appVer.patch) + + ". Connection rejected."); + + end->Send(false, true, false, internalSys, rejectedOp, sPayload); + continue; + } + + if (HasEndpoint(header.disposition, header.endpointId)) + { + if (header.disposition == Disposition::SERVICE) + { + sPayload.WriteStr( + "The service, \"" + end->GetId() + "\", is taken. Connection rejected."); + end->Send(false, true, false, internalSys, rejectedOp, sPayload); + continue; + } + else + { + sPayload.WriteStr( + "The username, \"" + end->GetId() + "\", is taken. Connection rejected."); + end->Send(false, true, false, internalSys, rejectedOp, sPayload); + continue; + } + } + + if (connectedCb && !connectedCb(this, end)) + { + sPayload.WriteStr("Connection rejected."); + end->Send(false, true, false, internalSys, rejectedOp, sPayload); + continue; + } + + endpoints.Push(end); + + sPayload.Write(CPU::GetArchitecture()); + sPayload.WriteStr(id); + + UInt_64 active = GetEndpointsCount(Disposition::ENDPOINT, Status::ACTIVE); + + if (maxEndpoints && active >= maxEndpoints) + { + end->SetStatus(Status::IN_LOCAL_QUEUE); + + UpdateQueue(active); + + sPayload.Write(Status::IN_REMOTE_QUEUE); + sPayload.Write(end->GetQueueSlot()); + + EHS_LOG_INT("Info", 1, end->GetId() + " connected and is in queue slot " + end->GetQueueSlot() + "."); + } + else + { + end->SetStatus(Status::ACTIVE); + + if (activeCb) + activeCb(this, end); + + sPayload.Write(Status::ACTIVE); + sPayload.Write(0); + + EHS_LOG_INT("Info", 1, end->GetId() + " connected."); + } + + end->Send(false, true, false, internalSys, connectedOp, sPayload); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == connectedOp) + { + Endpoint* end = GetEndpoint(rAddress, rPort); + if (!end || end->GetStatus() != Status::PENDING) + continue; + + Architecture arch = payload.Read(); + Str_8 id = payload.ReadStr(); + + *end = Endpoint(hdl, header.disposition, arch, id, type, rAddress, rPort); + end->SetStatus(payload.Read()); + end->SetQueueSlot(payload.Read()); + end->SetParent(this); + + if (connectedCb) + connectedCb(this, end); + + Str_8 msg = "Successfully connected to " + end->GetId(); + if (end->GetStatus() == Status::IN_REMOTE_QUEUE) + msg += " and in queue slot " + Str_8::FromNum(end->GetQueueSlot()) + "."; + else + msg += "."; + + EHS_LOG_INT("Info", 2, msg); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == rejectedOp) + { + if (!RemoveEndpoint(rAddress, rPort)) + continue; + + Str_8 msg = payload.ReadStr(); + if (msg.Size()) + EHS_LOG_INT("Info", 3, msg); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == disconnectOp) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + end->Send(false, true, false, internalSys, disconnectedOp, {}); + + if (disconnectedCb) + disconnectedCb(this, end); + + Str_8 dcMsg; + + if (header.disposition == Disposition::SERVICE) + dcMsg = "You have been disconnected from, \"" + end->GetId() + "\"."; + else + dcMsg = end->GetId() + " has disconnected."; + + Str_8 msg = payload.ReadStr(); + if (msg.Size()) + dcMsg += " Reason: " + msg; + + EHS_LOG_INT("Info", 4, dcMsg); + + RemoveEndpoint(header.disposition, end->GetHashId()); + + UpdateQueue(); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == disconnectedOp) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + if (disconnectedCb) + disconnectedCb(this, end); + + RemoveEndpoint(end); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == statusUpdateOp) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + Status newStatus = payload.Read(); + UInt_64 newSlot = payload.Read(); + + if (end->GetStatus() == Status::ACTIVE) + { + if (activeCb) + activeCb(this, end); + + EHS_LOG_INT("Info", 5, "Your connection status to " + end->GetId() + " has now become active."); + } + else if (end->GetStatus() == Status::IN_REMOTE_QUEUE && newStatus == Status::IN_REMOTE_QUEUE) + { + EHS_LOG_INT("Info", 5, "Your queue slot for " + end->GetId() + " is now " + newSlot + "."); + } + + end->SetStatus(newStatus); + end->SetQueueSlot(newSlot); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == pingOp) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + end->SetDeltaRate(payload.Read()); + end->Pong(delta); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == pongOp) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + end->SetDeltaRate(payload.Read()); + end->SendLatency(); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == latencyOp) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + end->SetLatency(payload.Read()); + } + else if (!header.ensure && header.endpointId && header.system == internalSys && header.op == receivedOp) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + UInt_64 msgId = payload.Read(); + UInt_64 fragment = payload.Read(); + + end->RemoveInsurance(msgId, fragment); + } + else if (header.endpointId) + { + Endpoint* end = GetEndpoint(header.disposition, header.endpointId); + if (!end) + continue; + + if (dropPackets && !header.ensure && header.id < end->GetNextRecvId()) + { + EHS_LOG_INT("Info", 6, "Old packet intentionally dropped."); + continue; + } + + if (header.ensure) + { + Serializer sPayload(Endianness::LE); + sPayload.Write(header.id); + sPayload.Write(header.fragment); + + end->Send(false, true, false, internalSys, receivedOp, sPayload); + } + + end->AddReceived( + header, + Serializer<>(Endianness::LE, &payload[payload.GetOffset()], payload.Size() - payload.GetOffset()) + ); + } + else + { + EHS_LOG_INT("Info", 7, "Corrupted packet."); + } + } + + PollEndpoints(endpoints); + } + + bool Socket::IsInitialized() const + { + return hdl != EHS_INVALID_SOCKET; + } + + void Socket::SetAddressType(const AddrType newType) + { + if (hdl != EHS_INVALID_SOCKET) + return; + + type = newType; + } + + AddrType Socket::GetAddressType() const + { + return type; + } + + Str_8 Socket::GetAddress() const + { + return address; + } + + UInt_16 Socket::GetPort() const + { + return port; + } + + bool Socket::IsBound() const + { + return bound; + } + + Version Socket::GetVersion() const + { + return ver; + } + + Version Socket::GetAppVersion() const + { + return appVer; + } + + Disposition Socket::GetDisposition() const + { + return disposition; + } + + void Socket::EnableDropPackets(const bool enable) + { + dropPackets = enable; + } + + bool Socket::IsDropPacketsEnabled() const + { + return dropPackets; + } + + Str_8 Socket::GetId() const + { + return id; + } + + UInt_64 Socket::GetHashId() const + { + return hashId; + } + + bool Socket::HasSystem(const UInt_64 hashId) const + { + if (internalSys == hashId) + return true; + + for (UInt_64 i = 0; i < systems.Size(); ++i) + if (systems[i]->GetHashId() == hashId) + return true; + + return false; + } + + bool Socket::HasSystem(const Str_8& id) const + { + return HasSystem(id.Hash_64()); + } + + bool Socket::AddSystem(System* sys) + { + if (HasSystem(sys->GetHashId())) + return false; + + systems.Push(sys); + + return true; + } + + System* Socket::GetSystem(const UInt_64 hashId) + { + for (UInt_64 i = 0; i < systems.Size(); ++i) + if (systems[i]->GetHashId() == hashId) + return systems[i]; + + return nullptr; + } + + System* Socket::GetSystem(const Str_8& id) + { + return GetSystem(id.Hash_32()); + } + + bool Socket::HasEndpoint(const Disposition disposition, const Status status, const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetStatus() != status) + continue; + + if (endpoints[i]->GetHashId() == hashId) + return true; + } + + return false; + } + + bool Socket::HasEndpoint(const Disposition disposition, const Status status, const Str_8& id) const + { + return HasEndpoint(disposition, status, id.Hash_32()); + } + + bool Socket::HasEndpoint(const Disposition disposition, const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetHashId() == hashId) + return true; + } + + return false; + } + + bool Socket::HasEndpoint(const Disposition disposition, const Str_8& id) const + { + return HasEndpoint(disposition, id.Hash_64()); + } + + bool Socket::HasEndpoint(const Str_8& address, const UInt_16 port) const + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + if (endpoints[i]->GetAddress() == address && endpoints[i]->GetPort() == port) + return true; + + return false; + } + + Endpoint* Socket::GetEndpoint(const Disposition disposition, const Status status, const UInt_64 hashId) + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetStatus() != status) + continue; + + if (endpoints[i]->GetHashId() == hashId) + return endpoints[i]; + } + + return nullptr; + } + + Endpoint* Socket::GetEndpoint(const Disposition disposition, const Status status, const Str_8& id) + { + return GetEndpoint(disposition, status, id.Hash_32()); + } + + Endpoint* Socket::GetEndpoint(const Disposition disposition, const UInt_64 hashId) + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetHashId() == hashId) + return endpoints[i]; + } + + return nullptr; + } + + Endpoint* Socket::GetEndpoint(const Disposition disposition, const Str_8& id) + { + return GetEndpoint(disposition, id.Hash_32()); + } + + Endpoint* Socket::GetEndpoint(const Str_8& address, const UInt_16 port) + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + if (endpoints[i]->GetAddress() == address && endpoints[i]->GetPort() == port) + return endpoints[i]; + + return nullptr; + } + + Array Socket::GetEndpoints(const Disposition disposition, const Status status) + { + Array result(endpoints.Size()); + UInt_64 count = 0; + + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetStatus() == status) + result[count++] = endpoints[i]; + } + + result.Resize(count); + + return result; + } + + Array Socket::GetEndpoints(const Disposition disposition) + { + Array result(endpoints.Size()); + UInt_64 count = 0; + + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + if (endpoints[i]->GetDisposition() == disposition) + result[count++] = endpoints[i]; + + result.Resize(count); + + return result; + } + + UInt_64 Socket::GetEndpointsCount(const Disposition disposition, const Status status) + { + UInt_64 count = 0; + + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetStatus() == status) + ++count; + } + + return count; + } + + UInt_64 Socket::GetEndpointsCount(const Disposition disposition) + { + UInt_64 count = 0; + + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + if (endpoints[i]->GetDisposition() == disposition) + ++count; + + return count; + } + + UInt_64 Socket::GetMaxEndpoints() const + { + return maxEndpoints; + } + + void Socket::SetBlocking(const bool blocking) + { + if (hdl == EHS_INVALID_SOCKET) + { + EHS_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized."); + return; + } + + #if defined(EHS_OS_WINDOWS) + u_long r = (u_long)!blocking; + + int result = ioctlsocket(hdl, FIONBIO, &r); + if (result != NO_ERROR) + EHS_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(result) + "."); + #elif defined(EHS_OS_LINUX) + if (fcntl(hdl, F_SETFL, O_NONBLOCK, blocking) == -1) + EHS_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + "."); + #endif + } + + bool Socket::IsBlocking() const + { + #if defined(EHS_OS_WINDOWS) + u_long r = 0; + if (ioctlsocket(hdl, FIONREAD, &r) == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to retrieve socket info."); + + return (bool)r; + #elif defined(EHS_OS_LINUX) + return (bool)fcntl(hdl, F_GETFL, O_NONBLOCK); + #else + return true; + #endif + } + + void Socket::SetMaxTimeout(const float seconds) + { + maxTimeout = seconds; + } + + float Socket::GetMaxTimeout() const + { + return maxTimeout; + } + + void Socket::SetResendRate(const float seconds) + { + resendRate = seconds; + } + + float Socket::GetResendRate() const + { + return resendRate; + } + + void Socket::SetConnectedCb(bool (*newCb)(Socket*, Endpoint*)) + { + connectedCb = newCb; + } + + void Socket::SetActiveCb(void (*newCb)(Socket*, Endpoint*)) + { + activeCb = newCb; + } + + void Socket::SetDisconnectedCb(void (*newCb)(Socket*, Endpoint*)) + { + disconnectedCb = newCb; + } + + void Socket::UpdateQueue(UInt_64 active) + { + UInt_64 slot = 0; + + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetStatus() == Status::IN_LOCAL_QUEUE) + { + if (active < maxEndpoints) + { + endpoints[i]->SetStatus(Status::ACTIVE); + endpoints[i]->SetQueueSlot(0); + + Serializer payload(Endianness::LE); + payload.Write(Status::ACTIVE); + payload.Write(0); + + endpoints[i]->Send(false, true, false, internalSys, statusUpdateOp, payload); + + if (activeCb) + activeCb(this, endpoints[i]); + + ++active; + } + else + { + if (endpoints[i]->GetQueueSlot() != slot) + { + Serializer payload(Endianness::LE); + payload.Write(Status::IN_REMOTE_QUEUE); + payload.Write(slot); + + endpoints[i]->Send(false, true, false, internalSys, statusUpdateOp, payload); + + endpoints[i]->SetQueueSlot(slot++); + } + else + { + ++slot; + } + } + } + } + } + + void Socket::UpdateQueue() + { + UpdateQueue(GetEndpointsCount(Disposition::ENDPOINT, Status::ACTIVE)); + } + + bool Socket::RemoveEndpoint(const Disposition disposition, const UInt_64 hashId) + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetDisposition() != disposition) + continue; + + if (endpoints[i]->GetHashId() == hashId) + { + delete endpoints[i]; + + if (i != endpoints.Size() - 1) + endpoints.Swap(i, endpoints.End()); + + endpoints.Pop(); + + return true; + } + } + + return false; + } + + bool Socket::RemoveEndpoint(const Str_8& address, const UInt_16 port) + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i]->GetAddress() == address && endpoints[i]->GetPort() == port) + { + delete endpoints[i]; + + if (i != endpoints.Size() - 1) + endpoints.Swap(i, endpoints.End()); + + endpoints.Pop(); + + return true; + } + } + + return false; + } + + bool Socket::RemoveEndpoint(const Endpoint* const end) + { + for (UInt_64 i = 0; i < endpoints.Size(); ++i) + { + if (endpoints[i] == end) + { + delete endpoints[i]; + + if (i != endpoints.Size() - 1) + endpoints.Swap(i, endpoints.End()); + + endpoints.Pop(); + + return true; + } + } + + return false; + } + + void Socket::PollEndpoints(Vector& endpoints) + { + UInt_64 i = 0; + while (i < endpoints.Size()) + { + endpoints[i]->Poll(delta); + + if (endpoints[i]->GetStatus() == Status::PENDING) + { + if (endpoints[i]->GetTimeout() >= maxTimeout) + { + EHS_LOG_INT("Info", 0, "Failed to connect to, \"" + endpoints[i]->GetAddress() + ":" + Str_8::FromNum(endpoints[i]->GetPort()) + "\"."); + + delete endpoints[i]; + + if (i != endpoints.Size() - 1) + endpoints.Swap(i, endpoints.End()); + + endpoints.Pop(); + + continue; + } + } + else + { + if (endpoints[i]->GetTimeout() >= maxTimeout) + { + EHS_LOG_INT("Info", 6, endpoints[i]->GetId() + " timed out."); + + if (disconnectedCb) + disconnectedCb(this, endpoints[i]); + + delete endpoints[i]; + + if (i != endpoints.Size() - 1) + endpoints.Swap(i, endpoints.End()); + + endpoints.Pop(); + + UpdateQueue(); + + continue; + } + + Vector* frags = endpoints[i]->GetReceived(); + + UInt_64 f = 0; + while (f < frags->Size()) + { + if (!(*frags)[f].IsComplete()) + { + ++f; + continue; + } + + Packet packet = (*frags)[f].Combine(); + + System* sys = GetSystem(packet.header.system); + if (!sys) + { + ++f; + continue; + } + + sys->Execute(this, endpoints[i], packet.header.op, packet.payload); + + frags->Swap(f, frags->End()); + frags->Pop(); + } + } + + ++i; + } + } + + void Socket::Bind_v6(const Str_8& address, const UInt_16 port) + { + Int_32 code = 0; + + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + if (address.Size()) + { + Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = 0; + + #if defined(EHS_OS_WINDOWS) + dCode = WSAGetLastError(); + #elif defined(EHS_OS_LINUX) + dCode = errno; + #endif + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin6_addr = in6addr_any; + } + + code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); + + #if defined(EHS_OS_WINDOWS) + if (code == SOCKET_ERROR) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + return; + } + #elif defined(EHS_OS_LINUX) + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); + return; + } + #endif + } + + void Socket::Bind_v4(const Str_8& address, const UInt_16 port) + { + Int_32 code = 0; + + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + if (address.Size()) + { + code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = 0; + + #if defined(EHS_OS_WINDOWS) + dCode = WSAGetLastError(); + #elif defined(EHS_OS_LINUX) + dCode = errno; + #endif + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin_addr.S_un.S_addr = htonl(INADDR_ANY); + } + + code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); + + #if defined(EHS_OS_WINDOWS) + if (code == SOCKET_ERROR) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + return; + } + #elif defined(EHS_OS_LINUX) + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); + return; + } + #endif + } + + UInt_16 Socket::Receive(Str_8* address, UInt_16* port, Byte* const data, const UInt_16 size) + { + if (hdl == EHS_INVALID_SOCKET) + { + EHS_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized."); + return 0; + } + + if (type == AddrType::IPV4 && size > EHS_IPV4_UDP_PAYLOAD) + { + EHS_LOG_INT("Error", 1, "Attempted to receive with a buffer size of, \"" + Str_8::FromNum(size) + + "\", that exceeds, \"" + Str_8::FromNum(EHS_IPV4_UDP_PAYLOAD) + "."); + } + + sockaddr_in6 remote = {}; + UInt_32 addrLen = type == AddrType::IPV6 ? sizeof(sockaddr_in6) : sizeof(sockaddr_in); + SInt_64 received = 0; + + #if defined(EHS_OS_WINDOWS) + received = recvfrom(hdl, (char*)data, (int)size, 0, (sockaddr*)&remote, (int*)&addrLen); + if (received == SOCKET_ERROR) + { + int code = WSAGetLastError(); + if (code == WSAEMSGSIZE) + { + UnInitialize(); + + EHS_LOG_INT("Error", 2, "The buffer size, \"" + Str_8::FromNum(size) + "\" is too small."); + } + else if (code != WSAECONNRESET && code != WSAEWOULDBLOCK) + { + UnInitialize(); + + EHS_LOG_INT("Error", 3, "Failed to receive with error #" + Str_8::FromNum(code) + "."); + } + + return 0; + } + #elif defined(EHS_OS_LINUX) + received = recvfrom(hdl, data, size, 0, (sockaddr*)&remote, &addrLen); + if (received == -1) + { + int code = errno; + if (code != ECONNRESET && code != EWOULDBLOCK) + { + UnInitialize(); + + EHS_LOG_INT("Error", 2, "Failed to receive with error #" + Str_8::FromNum(code) + "."); + } + + return 0; + } + #endif + + + if (addrLen == sizeof(sockaddr_in6)) + { + char tmpAddr[INET6_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) + { + Int_32 code = 0; + + #if defined(EHS_OS_WINDOWS) + code = WSAGetLastError(); + #elif defined(EHS_OS_LINUX) + code = errno; + #endif + + EHS_LOG_INT("Error", 2, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + "."); + + return received; + } + + *address = tmpAddr; + *port = ntohs(remote.sin6_port); + } + else if (addrLen == sizeof(sockaddr_in)) + { + char tmpAddr[INET_ADDRSTRLEN]; + + if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN)) + { + Int_32 code = 0; + + #if defined(EHS_OS_WINDOWS) + code = WSAGetLastError(); + #elif defined(EHS_OS_LINUX) + code = errno; + #endif + + EHS_LOG_INT("Error", 2, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + "."); + + return (UInt_16)received; + } + + *address = tmpAddr; + *port = ntohs(((sockaddr_in*)&remote)->sin_port); + } + + return (UInt_16)received; + } +} \ No newline at end of file diff --git a/src/io/socket/TCP_BSD.cpp b/src/io/socket/TCP_BSD.cpp new file mode 100644 index 0000000..ebdf889 --- /dev/null +++ b/src/io/socket/TCP_BSD.cpp @@ -0,0 +1,478 @@ +#include "ehs/io/socket/TCP_BSD.h" +#include "ehs/io/socket/DNS.h" + +#include +#include +#include +#include +#include +#include + +namespace ehs +{ + TCP::~TCP() + { + if (hdl == EHS_INVALID_SOCKET) + return; + + if (connection) + { + if (shutdown(hdl, SHUT_RDWR) == -1) + EHS_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(errno) + "."); + } + + if (close(hdl) == -1) + EHS_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(errno) + "."); + } + + TCP::TCP() + : hdl(EHS_INVALID_SOCKET) + { + } + + TCP::TCP(const AddrType addrType) + : BaseTCP(addrType), hdl(EHS_INVALID_SOCKET) + { + TCP::Initialize(); + } + + TCP::TCP(TCP&& tcp) noexcept + : BaseTCP(std::move(tcp)), hdl(tcp.hdl) + { + tcp.hdl = EHS_INVALID_SOCKET; + } + + TCP::TCP(const TCP& tcp) + : BaseTCP(tcp), hdl(EHS_INVALID_SOCKET) + { + } + + TCP& TCP::operator=(TCP&& tcp) noexcept + { + if (this == &tcp) + return *this; + + BaseTCP::operator=(std::move(tcp)); + + hdl = tcp.hdl; + + tcp.hdl = EHS_INVALID_SOCKET; + + return *this; + } + + TCP& TCP::operator=(const TCP& tcp) + { + if (this == &tcp) + return *this; + + BaseTCP::operator=(tcp); + + hdl = EHS_INVALID_SOCKET; + + return *this; + } + + void TCP::Initialize() + { + if (IsValid()) + return; + + if (addrType == AddrType::IPV6) + hdl = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + else if (addrType == AddrType::IPV4) + hdl = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + else + return; + + if (hdl == EHS_INVALID_SOCKET) + { + UInt_32 code = errno; + + EHS_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + "."); + } + } + + void TCP::Release() + { + if (!IsValid()) + return; + + if (connection) + { + if (shutdown(hdl, SHUT_RDWR) == -1) + EHS_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(errno) + "."); + } + + if (close(hdl) == -1) + EHS_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(errno) + "."); + + connection = false; + bound = false; + listening = false; + connected = false; + hdl = EHS_INVALID_SOCKET; + } + + void TCP::Bind(const Str_8& address, unsigned short port) + { + if (!IsValid() || bound || connection) + return; + + if (addrType == AddrType::IPV6) + Bind_v6(address, port); + else if (addrType == AddrType::IPV4) + Bind_v4(address, port); + + this->localAddr = address; + this->localPort = port; + + bound = true; + } + + void TCP::Listen() + { + if (connection || !IsValid() || !bound || listening) + return; + + int code = listen(hdl, SOMAXCONN); + if (code == -1) + { + EHS_LOG_INT("Error", 0, "Failed to listen with error #" + Str_8::FromNum(errno) + "."); + + return; + } + + listening = true; + } + + TCP* TCP::Accept() + { + if (connection || !IsValid() || !bound || !listening) + return nullptr; + + sockaddr_in6 remote = {}; + UInt_32 addrLen = sizeof(sockaddr_in6); + + TCP* client = new TCP(); + client->addrType = addrType; + client->localAddr = localAddr; + client->localPort = localPort; + client->connection = true; + client->hdl = accept(hdl, (sockaddr*)&remote, &addrLen); + + if (client->hdl == EHS_INVALID_SOCKET) + { + if (errno != EWOULDBLOCK) + EHS_LOG_INT("Error", 0, "Failed to accept client with error #" + Str_8::FromNum(errno) + "."); + + delete client; + + return nullptr; + } + + if (addrLen == sizeof(sockaddr_in6)) + { + char tmpAddr[INET6_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) + { + EHS_LOG_INT("Error", 1, "Failed to convert IPv6 address with error #" + Str_8::FromNum(errno) + "."); + + delete client; + + return nullptr; + } + + client->remoteAddr = tmpAddr; + client->remotePort = ntohs(remote.sin6_port); + } + else if (addrLen == sizeof(sockaddr_in)) + { + char tmpAddr[INET_ADDRSTRLEN]; + + if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN)) + { + EHS_LOG_INT("Error", 1, "Failed to convert IPv4 address with error #" + Str_8::FromNum(errno) + "."); + + delete client; + + return nullptr; + } + + client->remoteAddr = tmpAddr; + client->remotePort = ntohs(((sockaddr_in*)&remote)->sin_port); + } + + return client; + } + + void TCP::Connect(const Str_8& address, const unsigned short port) + { + if (connection || !IsValid() || listening) + return; + + remoteHostName = address; + remoteAddr = DNS::Resolve(addrType, address); + remotePort = port; + + if (addrType == AddrType::IPV6) + Connect_v6(remoteAddr, port); + else if (addrType == AddrType::IPV4) + Connect_v4(remoteAddr, port); + + connected = true; + } + + UInt_64 TCP::Send(const Byte *const buffer, const UInt_32 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to send while socket is not initialized."); + return 0; + } + + if ((!connection && !connected)) + { + EHS_LOG_INT("Error", 1, "Attempted to send while socket is not connected or a connection."); + return 0; + } + + SInt_64 sent = send(hdl, (char*)buffer, size, 0); + if (sent == -1) + { + int err = errno; + if (err == ECONNRESET) + { + Release(); + EHS_LOG_INT("Information", 0, "Connection dropped."); + } + else + EHS_LOG_INT("Error", 1, "Failed to send with error #" + Str_8::FromNum(err) + "."); + + return 0; + } + + return (UInt_64)sent; + } + + UInt_64 TCP::Receive(Byte* const buffer, const UInt_32 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized."); + return 0; + } + + if ((!connection && !connected)) + { + EHS_LOG_INT("Error", 1, "Attempted to receive while socket is not connected or a connection."); + return 0; + } + + SInt_64 received = recv(hdl, (char*)buffer, size, 0); + if (received == -1) + { + int err = errno; + if (err == ECONNRESET) + { + Release(); + EHS_LOG_INT("Information", 0, "Connection dropped."); + } + else if (err != EWOULDBLOCK) + { + EHS_LOG_INT("Error", 2, "Failed to receive with error #" + Str_8::FromNum(err) + "."); + } + + return 0; + } + + return (UInt_64)received; + } + + void TCP::SetBlocking(const bool blocking) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized."); + return; + } + + int flags = fcntl(hdl, F_GETFL, 0); + if (flags == -1) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve flags."); + return; + } + + if (blocking) + flags ^= O_NONBLOCK; + else + flags |= O_NONBLOCK; + + if (fcntl(hdl, F_SETFL, flags) == -1) + EHS_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + "."); + } + + bool TCP::IsBlocking() const + { + int flags = fcntl(hdl, F_GETFL, 0); + if (flags == -1) + { + EHS_LOG_INT("Error", 0, "Failed to retrieve flags."); + return true; + } + + return !(flags & O_NONBLOCK); + } + + bool TCP::IsValid() const + { + return hdl != EHS_INVALID_SOCKET; + } + + void TCP::Bind_v6(const Str_8& address, unsigned short port) + { + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + if (address.Size()) + { + int code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin6_addr = in6addr_any; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); + + return; + } + } + + void TCP::Bind_v4(const Str_8& address, unsigned short port) + { + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + if (address.Size()) + { + int code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + { + result.sin_addr.s_addr = INADDR_ANY; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); + + return; + } + } + + void TCP::Connect_v6(const Str_8& address, unsigned short port) + { + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + int code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + + code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (code == -1) + { + int err = errno; + if (err == ETIMEDOUT) + { + EHS_LOG_INT("Information", 2, "Connection attempt timed-out."); + } + else + { + EHS_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + "."); + } + + return; + } + } + + void TCP::Connect_v4(const Str_8& address, unsigned short port) + { + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + int code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + + code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); + if (code == -1) + { + int err = errno; + if (err == ETIMEDOUT) + { + EHS_LOG_INT("Information", 2, "Connection attempt timed-out."); + } + else + { + EHS_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + "."); + } + + return; + } + } +}; \ No newline at end of file diff --git a/src/io/socket/TCP_W32.cpp b/src/io/socket/TCP_W32.cpp new file mode 100644 index 0000000..3c09e88 --- /dev/null +++ b/src/io/socket/TCP_W32.cpp @@ -0,0 +1,500 @@ +#include "io/socket/TCP_W32.h" +#include "io/socket/DNS.h" + +#include +#include + +namespace ehs +{ + TCP::~TCP() + { + if (hdl == EHS_INVALID_SOCKET) + return; + + Int_32 code; + + if (connection) + { + code = shutdown(hdl, SD_SEND); + if (code == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + code = closesocket(hdl); + if (code == SOCKET_ERROR) + EHS_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + "."); + + if (!connection && WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + } + + TCP::TCP() + : hdl(EHS_INVALID_SOCKET) + { + } + + TCP::TCP(const AddrType addrType) + : BaseTCP(addrType), hdl(EHS_INVALID_SOCKET) + { + TCP::Initialize(); + } + + TCP::TCP(TCP&& tcp) noexcept + : BaseTCP(std::move(tcp)), hdl(tcp.hdl) + { + tcp.hdl = EHS_INVALID_SOCKET; + } + + TCP::TCP(const TCP& tcp) + : BaseTCP(tcp), hdl(EHS_INVALID_SOCKET) + { + TCP::Initialize(); + } + + TCP& TCP::operator=(TCP&& tcp) noexcept + { + if (this == &tcp) + return *this; + + BaseTCP::operator=(tcp); + + hdl = tcp.hdl; + + tcp.hdl = EHS_INVALID_SOCKET; + + return *this; + } + + TCP& TCP::operator=(const TCP& tcp) + { + if (this == &tcp) + return *this; + + BaseTCP::operator=(tcp); + + hdl = EHS_INVALID_SOCKET; + + TCP::Initialize(); + + return *this; + } + + void TCP::Initialize() + { + WSADATA data = {}; + + int code = WSAStartup(MAKEWORD(2, 2), &data); + if (code) + { + EHS_LOG_INT("Error", 0, "WSAStartup failed with the error #" + Str_8::FromNum(code) + "."); + return; + } + + if (addrType == AddrType::IPV6) + hdl = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + else if (addrType == AddrType::IPV4) + hdl = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + else + return; + + if (hdl == EHS_INVALID_SOCKET) + { + UInt_32 code = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + "."); + + if (WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + } + } + + void TCP::Release() + { + if (!IsValid()) + return; + + Int_32 code; + + if (connection) + { + code = shutdown(hdl, SD_SEND); + if (code == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + code = closesocket(hdl); + if (code == SOCKET_ERROR) + EHS_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + "."); + + hdl = EHS_INVALID_SOCKET; + + if (!connection && WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + + connection = false; + bound = false; + listening = false; + connected = false; + } + + void TCP::Bind(const Str_8& address, unsigned short port) + { + if (!IsValid() || bound || connection) + return; + + if (addrType == AddrType::IPV6) + Bind_v6(address, port); + else if (addrType == AddrType::IPV4) + Bind_v4(address, port); + + this->localAddr = address; + this->localPort = port; + + bound = true; + } + + void TCP::Listen() + { + if (connection || !IsValid() || !bound || listening) + return; + + int code = listen(hdl, SOMAXCONN); + if (code == -1) + { + EHS_LOG_INT("Error", 0, "Failed to listen with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + + return; + } + + listening = true; + } + + TCP* TCP::Accept() + { + if (connection || !IsValid() || !bound || !listening) + return nullptr; + + sockaddr_in6 remote = {}; + UInt_32 addrLen = sizeof(sockaddr_in6); + + TCP* client = new TCP(); + client->addrType = addrType; + client->localAddr = localAddr; + client->localPort = localPort; + client->connection = true; + client->hdl = accept(hdl, (sockaddr*)&remote, (int*)&addrLen); + + if (client->hdl == EHS_INVALID_SOCKET) + { + Int_32 code = WSAGetLastError(); + + if (code != WSAEWOULDBLOCK) + EHS_LOG_INT("Error", 0, "Failed to accept client with error #" + Str_8::FromNum(code) + "."); + + delete client; + + return nullptr; + } + + if (addrLen == sizeof(sockaddr_in6)) + { + char tmpAddr[INET6_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) + { + Int_32 code = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + "."); + + delete client; + + return nullptr; + } + + client->remoteAddr = tmpAddr; + client->remotePort = ntohs(remote.sin6_port); + } + else if (addrLen == sizeof(sockaddr_in)) + { + char tmpAddr[INET_ADDRSTRLEN]; + + if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN)) + { + Int_32 code = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + "."); + + delete client; + + return nullptr; + } + + client->remoteAddr = tmpAddr; + client->remotePort = ntohs(((sockaddr_in*)&remote)->sin_port); + } + + return client; + } + + void TCP::Connect(const Str_8& address, const unsigned short port) + { + if (connection || !IsValid() || listening) + return; + + remoteHostName = address; + remoteAddr = DNS::Resolve(addrType, address); + remotePort = port; + + if (addrType == AddrType::IPV6) + Connect_v6(remoteAddr, port); + else if (addrType == AddrType::IPV4) + Connect_v4(remoteAddr, port); + + connected = true; + } + + UInt_64 TCP::Send(const Byte *const buffer, const UInt_32 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to send while socket is not initialized."); + return 0; + } + + if ((!connection && !connected)) + { + EHS_LOG_INT("Error", 1, "Attempted to send while socket is not connected or a connection."); + return 0; + } + + SInt_64 sent = (SInt_64)send(hdl, (char*)buffer, (int)size, 0); + if (sent == SOCKET_ERROR) + { + int err = WSAGetLastError(); + if (err == WSAECONNRESET) + { + Release(); + EHS_LOG_INT("Information", 0, "Connection dropped."); + } + else + EHS_LOG_INT("Error", 1, "Failed to send with error #" + Str_8::FromNum(err) + "."); + + return 0; + } + + return (UInt_64)sent; + } + + UInt_64 TCP::Receive(Byte* const buffer, const UInt_32 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized."); + return 0; + } + + if ((!connection && !connected)) + { + EHS_LOG_INT("Error", 1, "Attempted to receive while socket is not connected or a connection."); + return 0; + } + + SInt_64 received = (SInt_64)recv(hdl, (char*)buffer, (int)size, 0); + if (received == SOCKET_ERROR) + { + int err = WSAGetLastError(); + if (err == WSAECONNRESET) + { + Release(); + EHS_LOG_INT("Information", 0, "Connection dropped."); + } + else if (err == WSAECONNABORTED) + { + EHS_LOG_INT("Information", 1, "Receiving timed-out."); + } + else if (err != WSAEWOULDBLOCK) + { + EHS_LOG_INT("Error", 2, "Failed to receive with error #" + Str_8::FromNum(err) + "."); + } + + return 0; + } + + return (UInt_64)received; + } + + void TCP::SetBlocking(const bool blocking) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized."); + return; + } + + u_long r = (u_long)!blocking; + + int result = ioctlsocket(hdl, FIONBIO, &r); + if (result != NO_ERROR) + EHS_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(result) + "."); + } + + bool TCP::IsBlocking() const + { + u_long r = 0; + if (ioctlsocket(hdl, FIONREAD, &r) == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to retrieve socket info."); + + return (bool)r; + } + + bool TCP::IsValid() const + { + return hdl != EHS_INVALID_SOCKET; + } + + void TCP::Bind_v6(const Str_8& address, unsigned short port) + { + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + if (address.Size()) + { + int code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin6_addr = in6addr_any; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + + return; + } + } + + void TCP::Bind_v4(const Str_8& address, unsigned short port) + { + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + if (address.Size()) + { + int code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin_addr.S_un.S_addr = INADDR_ANY; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + + return; + } + } + + void TCP::Connect_v6(const Str_8& address, unsigned short port) + { + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + int code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + + code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (code == SOCKET_ERROR) + { + int err = WSAGetLastError(); + if (err == WSAETIMEDOUT) + { + EHS_LOG_INT("Information", 2, "Connection attempt timed-out."); + } + else + { + EHS_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + "."); + } + + return; + } + } + + void TCP::Connect_v4(const Str_8& address, unsigned short port) + { + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + int code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + + code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); + if (code == SOCKET_ERROR) + { + int err = WSAGetLastError(); + if (err == WSAETIMEDOUT) + { + EHS_LOG_INT("Information", 2, "Connection attempt timed-out."); + } + else + { + EHS_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + "."); + } + + return; + } + } +} \ No newline at end of file diff --git a/src/io/socket/UDP_BSD.cpp b/src/io/socket/UDP_BSD.cpp new file mode 100644 index 0000000..479bbfe --- /dev/null +++ b/src/io/socket/UDP_BSD.cpp @@ -0,0 +1,362 @@ +#include "ehs/io/socket/UDP_BSD.h" +#include "ehs/Log.h" + +#include +#include +#include +#include +#include +#include + +namespace ehs +{ + UDP::~UDP() + { + if (hdl == EHS_INVALID_SOCKET) + return; + + Int_32 code = close(hdl); + if (code == -1) + EHS_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + "."); + } + + UDP::UDP() + : hdl(EHS_INVALID_SOCKET) + { + } + + UDP::UDP(const AddrType addrType) + : BaseUDP(addrType), hdl(EHS_INVALID_SOCKET) + { + if (addrType == AddrType::IPV6) + hdl = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + else if (addrType == AddrType::IPV4) + hdl = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + else + return; + + if (hdl == EHS_INVALID_SOCKET) + { + UInt_32 code = errno; + + EHS_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + "."); + + return; + } + } + + UDP::UDP(UDP&& udp) noexcept + : BaseUDP(std::move(udp)), hdl(udp.hdl) + { + udp.hdl = EHS_INVALID_SOCKET; + } + + UDP::UDP(const UDP& udp) + : BaseUDP(udp), hdl(EHS_INVALID_SOCKET) + { + } + + UDP& UDP::operator=(UDP&& udp) noexcept + { + if (this == &udp) + return *this; + + BaseUDP::operator=(std::move(udp)); + + hdl = udp.hdl; + + udp.hdl = EHS_INVALID_SOCKET; + + return *this; + } + + UDP& UDP::operator=(const UDP& udp) + { + if (this == &udp) + return *this; + + BaseUDP::operator=(udp); + + hdl = EHS_INVALID_SOCKET; + + return *this; + } + + void UDP::Release() + { + if (!IsValid()) + return; + + Int_32 code = close(hdl); + if (code == -1) + EHS_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + "."); + + hdl = EHS_INVALID_SOCKET; + + bound = false; + } + + void UDP::Bind(const AddrType type, const Str_8& address, const UInt_16 port) + { + if (!IsValid() || bound) + return; + + if (type == AddrType::IPV6) + Bind_v6(address, port); + else if (type == AddrType::IPV4) + Bind_v4(address, port); + + this->address = address; + this->port = port; + + bound = true; + } + + UInt_64 UDP::Send(const AddrType type, const Str_8& address, const UInt_16 port, const Byte *const data, const UInt_64 size) + { + if (type == AddrType::IPV6) + return Send_v6(address, port, data, size); + else if (type == AddrType::IPV4) + return Send_v4(address, port, data, size); + + return 0; + } + + UInt_64 UDP::Receive(AddrType* const type, Str_8* const address, UInt_16* const port, Byte* const data, const UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized."); + return 0; + } + + sockaddr_in6 remote = {}; + UInt_32 addrLen = sizeof(sockaddr_in6); + SInt_64 received; + + received = recvfrom(hdl, data, size, 0, (sockaddr*)&remote, &addrLen); + if (received == -1) + { + int code = errno; + if (code != ECONNRESET && code != EWOULDBLOCK) + { + Release(); + + EHS_LOG_INT("Error", 1, "Failed to receive with error #" + Str_8::FromNum(code) + "."); + } + + return 0; + } + + if (addrLen == sizeof(sockaddr_in6)) + { + char tmpAddr[INET6_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) + { + Int_32 code = errno; + + EHS_LOG_INT("Error", 2, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + "."); + + return received; + } + + *type = AddrType::IPV6; + *address = tmpAddr; + *port = ntohs(remote.sin6_port); + } + else if (addrLen == sizeof(sockaddr_in)) + { + char tmpAddr[INET_ADDRSTRLEN]; + + if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN)) + { + Int_32 code = errno; + + EHS_LOG_INT("Error", 2, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + "."); + + return received; + } + + *type = AddrType::IPV4; + *address = tmpAddr; + *port = ntohs(((sockaddr_in*)&remote)->sin_port); + } + + return received; + } + + void UDP::SetBlocking(const bool blocking) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized."); + return; + } + + if (fcntl(hdl, F_SETFL, O_NONBLOCK, blocking) == -1) + EHS_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + "."); + } + + bool UDP::IsBlocking() const + { + return (bool)fcntl(hdl, F_GETFL, O_NONBLOCK); + } + + bool UDP::IsValid() const + { + return hdl != EHS_INVALID_SOCKET; + } + + void UDP::Bind_v6(const Str_8& address, const UInt_16 port) const + { + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + if (address.Size()) + { + Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin6_addr = in6addr_any; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); + return; + } + } + + void UDP::Bind_v4(const Str_8& address, const UInt_16 port) const + { + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + if (address.Size()) + { + int code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin_addr.s_addr = INADDR_ANY; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); + if (code == -1) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + "."); + return; + } + } + + UInt_64 UDP::Send_v6(const Str_8& address, const UInt_16 port, const Byte* const data, const UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Info", 0, "Attempted to send while socket is not initialized."); + return 0; + } + + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid."); + return 0; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return 0; + } + + SInt_64 sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (sent == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + "."); + + Release(); + + return 0; + } + + return sent; + } + + UInt_64 UDP::Send_v4(const Str_8& address, const UInt_16 port, const Byte* const data, const UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Info", 0, "Attempted to send while socket is not initialized."); + return 0; + } + + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + int code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid."); + return 0; + } + else if (code == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return 0; + } + + SInt_64 sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in)); + if (sent == -1) + { + Int_32 dCode = errno; + + EHS_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + "."); + + Release(); + + return 0; + } + + return sent; + } +} \ No newline at end of file diff --git a/src/io/socket/UDP_W32.cpp b/src/io/socket/UDP_W32.cpp new file mode 100644 index 0000000..96d799a --- /dev/null +++ b/src/io/socket/UDP_W32.cpp @@ -0,0 +1,380 @@ +#include "io/socket/UDP_W32.h" +#include "ehs/Log.h" + +#include +#include + +namespace ehs +{ + UDP::~UDP() + { + if (hdl == EHS_INVALID_SOCKET) + return; + + Int_32 code = closesocket(hdl); + if (code == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + "."); + + if (WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 1, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + } + + UDP::UDP() + : hdl(EHS_INVALID_SOCKET) + { + } + + UDP::UDP(const AddrType addrType) + : BaseUDP(addrType), hdl(EHS_INVALID_SOCKET) + { + WSADATA data = {}; + + int code = WSAStartup(MAKEWORD(2, 2), &data); + if (code) + { + EHS_LOG_INT("Error", 0, "WSAStartup failed with the error #" + Str_8::FromNum(code) + "."); + return; + } + + if (addrType == AddrType::IPV6) + hdl = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + else if (addrType == AddrType::IPV4) + hdl = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + else + return; + + if (hdl == EHS_INVALID_SOCKET) + { + UInt_32 code = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + "."); + + if (WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + + return; + } + } + + UDP::UDP(UDP&& udp) noexcept + : BaseUDP(std::move(udp)), hdl(udp.hdl) + { + udp.hdl = EHS_INVALID_SOCKET; + } + + UDP::UDP(const UDP& udp) + : BaseUDP(udp), hdl(EHS_INVALID_SOCKET) + { + } + + UDP& UDP::operator=(UDP&& udp) noexcept + { + if (this == &udp) + return *this; + + BaseUDP::operator=(std::move(udp)); + + hdl = udp.hdl; + + udp.hdl = EHS_INVALID_SOCKET; + + return *this; + } + + UDP& UDP::operator=(const UDP& udp) + { + if (this == &udp) + return *this; + + BaseUDP::operator=(udp); + + hdl = EHS_INVALID_SOCKET; + + return *this; + } + + void UDP::Release() + { + if (!IsValid()) + return; + + Int_32 code = closesocket(hdl); + if (code == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + "."); + + hdl = EHS_INVALID_SOCKET; + + if (WSACleanup() == SOCKET_ERROR) + EHS_LOG_INT("Error", 1, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + + bound = false; + } + + void UDP::Bind(const Str_8& address, const UInt_16 port) + { + if (!IsValid() || bound) + return; + + if (addrType == AddrType::IPV6) + Bind_v6(address, port); + else if (addrType == AddrType::IPV4) + Bind_v4(address, port); + + this->address = address; + this->port = port; + + bound = true; + } + + UInt_64 UDP::Send(const Str_8& address, const UInt_16 port, const Byte *const data, const UInt_64 size) + { + if (addrType == AddrType::IPV6) + return Send_v6(address, port, data, size); + else if (addrType == AddrType::IPV4) + return Send_v4(address, port, data, size); + + return 0; + } + + UInt_64 UDP::Receive(Str_8* const address, UInt_16* const port, Byte* const data, const UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized."); + return 0; + } + + sockaddr_in6 remote = {}; + UInt_32 addrLen = sizeof(sockaddr_in); + + SInt_64 received = recvfrom(hdl, (char*)data, (int)size, 0, (sockaddr*)&remote, (int*)&addrLen); + if (received == SOCKET_ERROR) + { + int code = WSAGetLastError(); + if (code != WSAECONNRESET && code != WSAEWOULDBLOCK) + { + Release(); + + EHS_LOG_INT("Error", 1, "Failed to receive with error #" + Str_8::FromNum(code) + "."); + } + + return 0; + } + + if (addrLen == sizeof(sockaddr_in6)) + { + char tmpAddr[INET6_ADDRSTRLEN]; + + if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN)) + { + Int_32 code = WSAGetLastError(); + + EHS_LOG_INT("Error", 2, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + "."); + + return received; + } + + *address = tmpAddr; + *port = ntohs(remote.sin6_port); + } + else if (addrLen == sizeof(sockaddr_in)) + { + char tmpAddr[INET_ADDRSTRLEN]; + + if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN)) + { + Int_32 code = WSAGetLastError(); + + EHS_LOG_INT("Error", 2, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + "."); + + return received; + } + + *address = tmpAddr; + *port = ntohs(((sockaddr_in*)&remote)->sin_port); + } + + return received; + } + + void UDP::SetBlocking(const bool blocking) + { + if (!IsValid()) + { + EHS_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized."); + return; + } + + u_long r = (u_long)!blocking; + + int result = ioctlsocket(hdl, FIONBIO, &r); + if (result != NO_ERROR) + EHS_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(result) + "."); + } + + bool UDP::IsBlocking() const + { + u_long r = 0; + if (ioctlsocket(hdl, FIONREAD, &r) == SOCKET_ERROR) + EHS_LOG_INT("Error", 0, "Failed to retrieve socket info."); + + return (bool)r; + } + + bool UDP::IsValid() const + { + return hdl != EHS_INVALID_SOCKET; + } + + void UDP::Bind_v6(const Str_8& address, const UInt_16 port) + { + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + if (address.Size()) + { + Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin6_addr = in6addr_any; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (code == SOCKET_ERROR) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + return; + } + } + + void UDP::Bind_v4(const Str_8& address, const UInt_16 port) + { + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + if (address.Size()) + { + int code = inet_pton(AF_INET, address, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid."); + return; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return; + } + } + else + { + result.sin_addr.S_un.S_addr = INADDR_ANY; + } + + int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in)); + if (code == SOCKET_ERROR) + { + EHS_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + "."); + return; + } + } + + UInt_64 UDP::Send_v6(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Info", 0, "Attempted to send while socket is not initialized."); + return 0; + } + + sockaddr_in6 result = {}; + result.sin6_family = AF_INET6; + result.sin6_port = htons(port); + + Int_32 code = inet_pton(AF_INET6, addr, &result.sin6_addr); + if (!code) + { + EHS_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid."); + return 0; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return 0; + } + + Int_32 sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in6)); + if (sent == SOCKET_ERROR) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + "."); + + Release(); + + return 0; + } + + return sent; + } + + UInt_64 UDP::Send_v4(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size) + { + if (!IsValid()) + { + EHS_LOG_INT("Info", 0, "Attempted to send while socket is not initialized."); + return 0; + } + + sockaddr_in result = {}; + result.sin_family = AF_INET; + result.sin_port = htons(port); + + int code = inet_pton(AF_INET, addr, &result.sin_addr); + if (!code) + { + EHS_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid."); + return 0; + } + else if (code == -1) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + "."); + return 0; + } + + int sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in)); + if (sent == SOCKET_ERROR) + { + Int_32 dCode = WSAGetLastError(); + + EHS_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + "."); + + Release(); + + return 0; + } + + return sent; + } +} \ No newline at end of file diff --git a/src/io/socket/rest/Spotify.cpp b/src/io/socket/rest/Spotify.cpp new file mode 100644 index 0000000..044c503 --- /dev/null +++ b/src/io/socket/rest/Spotify.cpp @@ -0,0 +1,653 @@ +#include "ehs/io/socket/rest/Spotify.h" +#include "ehs/io/socket/DNS.h" +#include "ehs/system/System.h" +#include "ehs/URI.h" + +namespace ehs +{ + const Str_8 Spotify::trackUriPrefix = "https://open.spotify.com/track/"; + + Spotify::~Spotify() + { + client.Release(); + } + + Spotify::Spotify() + : forceVerify(false) + { + } + + Spotify::Spotify(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array& scopes, const bool forceVerify) + : client(AddrType::IPV4), clientId(clientId), secret(secret), redURI(redURI), scopes(scopes), forceVerify(forceVerify) + { + } + + bool Spotify::Authorize() + { + Str_8 scopesFinal; + + for (UInt_64 i = 0; i < scopes.Size(); ++i) + { + scopesFinal += scopes[i]; + + if (i < scopes.Size() - 1) + scopesFinal += "%20"; + } + + Str_8 rURI = URI::Encode(redURI); + + Str_8 uri = "https://accounts.spotify.com/authorize?client_id=" + clientId + "&redirect_uri=" + rURI + + "&response_type=code&show_dialog=" + (forceVerify ? "true" : "false") + "&scope=" + + scopesFinal; + + TCP server(AddrType::IPV4); + server.Initialize(); + server.Bind(DNS::Resolve(server.GetAddressType(), "localhost"), 65534); + server.Listen(); + + System::OpenURI(uri); + + TCP* cbClient = server.Accept(); + + Request cbReq = cbClient->RecvReq(); + + if (cbReq.GetResource() != "/callback") + { + Response resp(423, "Event Horizon"); + resp.SetContentType(ContentType::TEXT_HTML); + resp.SetBody("LWE ResponseHostile Information Received"); + + cbClient->SendRes(resp); + cbClient->Release(); + + return false; + } + + Response resp(200, "Event Horizon"); + resp.SetContentType(ContentType::TEXT_HTML); + resp.SetBody("LWE ResponseAuthentication Successful"); + + cbClient->SendRes(resp); + cbClient->Release(); + + server.Release(); + + SSL accounts(AddrType::IPV4); + accounts.Initialize(); + accounts.Connect("accounts.spotify.com", SSL::HTTPS_Port); + + Request authReq(Verb::POST, "/api/token"); + authReq.SetContentType(ContentType::APP_FORMURLENCODED); + authReq.BasicAuth(clientId, secret); + authReq.AddToBody("grant_type", "authorization_code"); + authReq.AddToBody("code", cbReq.GetQuery("code")); + authReq.AddToBody("redirect_uri", rURI); + + accounts.SendReq(authReq); + + Response authRes = accounts.RecvRes(); + + accounts.Release(); + + if (authRes.GetCode() == 400) + { + EHS_LOG_INT("Error", 0, "Could not authorize with Spotify because the client id was invalid."); + + return false; + } + else if (authRes.GetCode() == 403) + { + EHS_LOG_INT("Error", 1, "Could not authorize with Spotify because the secret was invalid."); + + return false; + } + else if (authRes.GetCode() != 200) + { + EHS_LOG_INT("Error", 2, "Could not authorize with Spotify with code " + Str_8::FromNum(authRes.GetCode()) + "."); + + return false; + } + + Json authResJson = authRes.GetJson(); + + JsonObj* value = (JsonObj*)authResJson.GetValue(); + if (!value) + return false; + + JsonVar* tokenVar = value->GetVar("access_token"); + if (!tokenVar) + return false; + + JsonVar* rTokenVar = value->GetVar("refresh_token"); + if (!rTokenVar) + return false; + + token = ((JsonStr*)tokenVar->GetValue())->value; + rToken = ((JsonStr*)rTokenVar->GetValue())->value; + + return true; + } + + UInt_32 Spotify::SetVolume(const UInt_8 level) + { + StartConnection(); + + Request req(Verb::PUT, "/v1/me/player/volume"); + req.AddQuery("volume_percent", Str_8::FromNum(level)); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Previous(); + } + + return res.GetCode(); + } + + UInt_32 Spotify::Play() + { + StartConnection(); + + Request req(Verb::PUT, "/v1/me/player/play"); + req.BearerAuth(token); + + client.Release(); + client.Initialize(); + client.Connect("", SSL::HTTPS_Port); + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Previous(); + } + + return res.GetCode(); + } + + UInt_32 Spotify::Pause() + { + StartConnection(); + + Request req(Verb::PUT, "/v1/me/player/pause"); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Previous(); + } + + return res.GetCode(); + } + + UInt_32 Spotify::SetRepeat(const SpotifyState state) + { + StartConnection(); + + Str_8 result; + switch (state) + { + case SpotifyState::TRACK: + result = "track"; + break; + case SpotifyState::CONTEXT: + result = "context"; + break; + case SpotifyState::OFF: + result = "off"; + break; + } + + Request req(Verb::PUT, "/v1/me/player/repeat"); + req.AddQuery("state", result); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Previous(); + } + + return res.GetCode(); + } + + UInt_32 Spotify::SetShuffle(const bool state) + { + StartConnection(); + + Request req(Verb::PUT, "/v1/me/player/repeat"); + req.AddQuery("state", state ? "true" : "false"); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Previous(); + } + + return res.GetCode(); + } + + UInt_32 Spotify::SearchTrack(Vector& artists, Str_8& id, Str_8& name) + { + StartConnection(); + + Request req(Verb::GET, "/v1/search"); + + Str_8 q = "artist%3A"; + + for (UInt_64 i = 0; i < artists.Size(); ++i) + { + q += artists[i].ReplaceAll(" ", "+"); + if (i != artists.Size() - 1) + q += "%2C+"; + } + + q += "+track%3A" + name.ReplaceAll(" ", "+"); + + req.AddQuery("q", q); + req.AddQuery("type", "track"); + req.AddQuery("limit", "1"); + req.AddQuery("offset", "0"); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return SearchTrack(artists, name, id); + } + + Json body = res.GetJson(); + + JsonNum* total = (JsonNum*)body.RetrieveValue("tracks.total"); + if (!total || total->value == 0.0f) + return 0; + + JsonObj* item = (JsonObj*)body.RetrieveValue("tracks.items[0]"); + if (!item) + return 0; + + JsonVar* artistsVar = item->GetVar("artists"); + if (!artistsVar) + return 0; + + JsonArray* artistsArray = (JsonArray*)artistsVar->GetValue(); + if (!artistsArray) + return 0; + + JsonVar* trackIdVar = item->GetVar("id"); + if (!trackIdVar) + return 0; + + JsonStr* trackIdValue = (JsonStr*)trackIdVar->GetValue(); + if (!trackIdValue) + return 0; + + JsonVar* trackNameVar = item->GetVar("name"); + if (!trackNameVar) + return 0; + + JsonStr* trackNameValue = (JsonStr*)trackNameVar->GetValue(); + if (!trackNameValue) + return 0; + + artists.Resize(artistsArray->Size()); + + for (UInt_64 i = 0; i < artistsArray->Size(); ++i) + { + JsonObj* artistObj = (JsonObj*)(*artistsArray)[i]; + + JsonVar* artistNameVar = artistObj->GetVar("name"); + + JsonStr* artistNameValue = (JsonStr*)artistNameVar->GetValue(); + + artists[i] = artistNameValue->value; + } + + id = trackIdValue->value; + + name = trackNameValue->value; + + return res.GetCode(); + } + + UInt_32 Spotify::GetPlayingTrack(Vector& artists, Str_8& id, Str_8& name) + { + StartConnection(); + + Request req(Verb::GET, "/v1/me/player/currently-playing"); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + + return GetPlayingTrack(artists, id, name); + } + + Json result = res.GetJson(); + + JsonObj* itemObj = (JsonObj*)result.RetrieveValue("item"); + if (!itemObj) + return {}; + + JsonVar* artistsVar = itemObj->GetVar("artists"); + if (!artistsVar) + return 0; + + JsonArray* artistsArray = (JsonArray*)artistsVar->GetValue(); + if (!artistsArray) + return 0; + + JsonVar* trackIdVar = itemObj->GetVar("id"); + if (!trackIdVar) + return 0; + + JsonStr* trackIdValue = (JsonStr*)trackIdVar->GetValue(); + if (!trackIdValue) + return 0; + + JsonVar* trackNameVar = itemObj->GetVar("name"); + if (!trackNameVar) + return 0; + + JsonStr* trackNameValue = (JsonStr*)trackNameVar->GetValue(); + if (!trackNameValue) + return 0; + + artists.Resize(artistsArray->Size()); + for (UInt_64 i = 0; i < artists.Size(); ++i) + { + JsonObj* artistObj = (JsonObj*)(*artistsArray)[i]; + + JsonVar* artistNameVar = artistObj->GetVar("name"); + + JsonStr* artistNameValue = (JsonStr*)artistNameVar->GetValue(); + + artists[i] = artistNameValue->value; + } + + id = trackIdValue->value; + + name = trackNameValue->value; + + return res.GetCode(); + } + + UInt_32 Spotify::GetQueue(Array& tracks) + { + StartConnection(); + + Request req(Verb::GET, "/v1/me/player/queue"); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return GetQueue(tracks); + } + + Json json = res.GetJson(); + + JsonObj* root = (JsonObj*)json.GetValue(); + + JsonVar* currentVar = root->GetVar("currently_playing"); + if (!currentVar->GetValue()) + return res.GetCode(); + + JsonObj* currentObj = (JsonObj*)currentVar->GetValue(); + + JsonArray* cArtists = (JsonArray*)currentObj->GetVar("artists")->GetValue(); + + JsonArray* queue = (JsonArray*)root->GetVar("queue")->GetValue(); + + tracks.Resize(queue->Size() + 1); + tracks[0].artists.Resize(cArtists->Size()); + + for (UInt_64 i = 0; i < cArtists->Size(); ++i) + tracks[0].artists[i] = ((JsonStr*)((JsonObj*)(*cArtists)[i])->GetVar("name")->GetValue())->value; + + tracks[0].id = ((JsonStr*)currentObj->GetVar("id")->GetValue())->value; + tracks[0].name = ((JsonStr*)currentObj->GetVar("name")->GetValue())->value; + + for (UInt_64 i = 1; i < queue->Size(); ++i) + { + JsonObj* trackObj = (JsonObj*)(*queue)[i - 1]; + + JsonArray* artists = (JsonArray*)trackObj->GetVar("artists")->GetValue(); + tracks[i].artists.Resize(artists->Size()); + + for (UInt_64 a = 0; a < artists->Size(); ++a) + tracks[i].artists[a] = ((JsonStr*)((JsonObj*)(*artists)[a])->GetVar("name")->GetValue())->value; + + tracks[i].id = ((JsonStr*)trackObj->GetVar("id")->GetValue())->value; + tracks[i].name = ((JsonStr*)trackObj->GetVar("name")->GetValue())->value; + } + + return res.GetCode(); + } + + UInt_32 Spotify::QueueTrack(const Str_8& id) + { + StartConnection(); + + Request req(Verb::POST, "/v1/me/player/queue"); + req.AddQuery("uri", "spotify:track:" + id); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return QueueTrack(id); + } + + return res.GetCode(); + } + + UInt_32 Spotify::AddTracks(const Str_8& playlistId, const Array& trackIds, const UInt_32 pos) + { + StartConnection(); + + JsonObj obj(0); + + JsonArray tracks(trackIds.Size(), 0); + for (UInt_64 i = 0; i < trackIds.Size(); ++i) + tracks[i] = (JsonBase*)new JsonStr("spotify:track:" + trackIds[i]); + obj.AddVar({"uris", tracks}); + obj.AddVar({"position", (float)pos}); + + Json json(obj); + + Request req(Verb::POST, "/v1/playlists/" + playlistId + "/tracks"); + req.BearerAuth(token); + req.SetContentType(ContentType::APP_JSON); + req.SetBody(json.ToStr(true)); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return AddTracks(playlistId, trackIds, pos); + } + + return res.GetCode(); + } + + UInt_32 Spotify::AddTrack(const Str_8& playlistId, const Str_8& trackId, const UInt_32 pos) + { + StartConnection(); + + Request req(Verb::POST, "/v1/playlists/" + playlistId + "/tracks"); + req.AddQuery("position", Str_8::FromNum(pos)); + req.AddQuery("uris", "spotify:track:" + trackId); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return AddTrack(playlistId, trackId, pos); + } + + return res.GetCode(); + } + + UInt_32 Spotify::Skip() + { + StartConnection(); + + Request req(Verb::POST, "/v1/me/player/next"); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Skip(); + } + + return res.GetCode(); + } + + UInt_32 Spotify::Previous() + { + StartConnection(); + + Request req(Verb::POST, "/v1/me/player/previous"); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Previous(); + } + + return res.GetCode(); + } + + UInt_32 Spotify::Seek(const UInt_32 pos) + { + StartConnection(); + + Request req(Verb::PUT, "/v1/me/player/seek"); + req.AddQuery("position_ms", Str_8::FromNum(pos)); + req.BearerAuth(token); + + client.SendReq(req); + + Response res = client.RecvRes(); + if (res.GetCode() == 401) + { + ReAuthorize(); + return Seek(pos); + } + + return res.GetCode(); + } + + void Spotify::StartConnection() + { + client.Release(); + client.Initialize(); + client.Connect("api.spotify.com", SSL::HTTPS_Port); + } + + bool Spotify::ReAuthorize() + { + SSL accounts; + accounts.Initialize(); + accounts.Connect("accounts.spotify.com", SSL::HTTPS_Port); + + Request reAuth(Verb::POST, "/api/token"); + reAuth.SetContentType(ContentType::APP_FORMURLENCODED); + reAuth.BasicAuth(clientId, secret); + reAuth.AddToBody("grant_type", "refresh_token"); + reAuth.AddToBody("refresh_token", rToken); + + accounts.SendReq(reAuth); + + Response res = accounts.RecvRes(); + + accounts.Release(); + + if (res.GetCode() != 200) + { + EHS_LOG_INT("Error", 0, "Failed to reauthorize with Spotify with code #" + Str_8::FromNum(res.GetCode()) + "."); + client.Release(); + return false; + } + + Json json = res.GetJson(); + + JsonObj* obj = (JsonObj*)json.GetValue(); + + JsonVar* tokenVar = obj->GetVar("access_token"); + if (!tokenVar) + return false; + + token = ((JsonStr*)tokenVar->GetValue())->value; + + return true; + } + + bool Spotify::IsActive() const + { + return client.IsConnected(); + } + + Str_8 Spotify::GetClientId() const + { + return clientId; + } + + Str_8 Spotify::GetSecret() const + { + return secret; + } + + Str_8 Spotify::GetRedURI() const + { + return redURI; + } + + bool Spotify::IsVerificationForced() const + { + return forceVerify; + } +} \ No newline at end of file diff --git a/src/io/socket/rest/Twitch.cpp b/src/io/socket/rest/Twitch.cpp new file mode 100644 index 0000000..9145f75 --- /dev/null +++ b/src/io/socket/rest/Twitch.cpp @@ -0,0 +1,150 @@ +#include "ehs/io/socket/rest/Twitch.h" +#include "ehs/io/socket/DNS.h" +#include "ehs/system/System.h" +#include "ehs/URI.h" + +namespace ehs +{ + Twitch::~Twitch() + { + client.Release(); + } + + Twitch::Twitch() + : forceVerify(false) + { + } + + Twitch::Twitch(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array& scopes, const bool forceVerify) + : client(AddrType::IPV4), clientId(clientId), secret(secret), redURI(redURI), scopes(scopes), forceVerify(forceVerify) + { + } + + bool Twitch::Authorize() + { + Str_8 scopesFinal; + + for (UInt_64 i = 0; i < scopes.Size(); ++i) + { + scopesFinal += scopes[i]; + + if (i < scopes.Size() - 1) + scopesFinal += "%20"; + } + + Str_8 rURI = URI::Encode(redURI); + + Str_8 uri = "https://id.twitch.tv/oauth2/authorize?client_id=" + clientId + "&redirect_uri=" + rURI + + "&response_type=code&force_verify=" + (forceVerify ? "true" : "false") + "&scope=" + + scopesFinal; + + TCP server(AddrType::IPV4); + server.Bind(DNS::Resolve(client.GetAddressType(), "localhost"), 65535); + server.Listen(); + + System::OpenURI(uri); + + TCP* cbClient = server.Accept(); + + Request cbReq = cbClient->RecvReq(); + + if (cbReq.GetResource() != "/callback") + { + Response resp(423, "Event Horizon"); + resp.SetContentType(ContentType::TEXT_HTML); + resp.SetBody( + "LWE ResponseHostile Information Received"); + + cbClient->SendRes(resp); + cbClient->Release(); + + return false; + } + + Response resp(200, "Event Horizon"); + resp.SetContentType(ContentType::TEXT_HTML); + resp.SetBody( + "LWE ResponseAuthentication Successful"); + + cbClient->SendRes(resp); + cbClient->Release(); + + server.Release(); + + client.Initialize(); + client.Connect("id.twitch.tv", SSL::HTTPS_Port); + + Request authReq(Verb::POST, "/oauth2/token"); + authReq.SetContentType(ContentType::APP_FORMURLENCODED); + authReq.AddToBody("client_id", clientId); + authReq.AddToBody("client_secret", secret); + authReq.AddToBody("code", cbReq.GetQuery("code")); + authReq.AddToBody("grant_type", "authorization_code"); + authReq.AddToBody("redirect_uri", redURI); + + client.SendReq(authReq); + + Response authRes = client.RecvRes(); + if (authRes.GetCode() == 400) + { + client.Release(); + + EHS_LOG_INT("Error", 0, "Could not authorize with Twitch because the client id was invalid."); + + return false; + } else if (authRes.GetCode() == 403) + { + client.Release(); + + EHS_LOG_INT("Error", 1, "Could not authorize with Twitch because the secret was invalid."); + + return false; + } else if (authRes.GetCode() != 200) + { + client.Release(); + + EHS_LOG_INT("Error", 2, "Could not authorize with Twitch."); + + return false; + } + + Json authResJson = authRes.GetJson(); + + JsonObj* value = (JsonObj*) authResJson.GetValue(); + if (!value) + return false; + + JsonVar* var = value->GetVar("access_token"); + if (!var) + return false; + + token = ((JsonStr*) var->GetValue())->value; + + return true; + } + + Str_8 Twitch::GetClientId() const + { + return clientId; + } + + Str_8 Twitch::GetSecret() const + { + return secret; + } + + Str_8 Twitch::GetRedURI() const + { + return redURI; + } + + bool Twitch::IsVerificationForced() const + { + return forceVerify; + } + + Str_8 Twitch::GetToken() const + { + return token; + } +} \ No newline at end of file diff --git a/src/io/socket/rest/TwitchChat.cpp b/src/io/socket/rest/TwitchChat.cpp new file mode 100644 index 0000000..49aa3e2 --- /dev/null +++ b/src/io/socket/rest/TwitchChat.cpp @@ -0,0 +1,177 @@ +#include "ehs/io/socket/rest/TwitchChat.h" +#include "ehs/io/socket/DNS.h" +#include "ehs/io/Console.h" + +namespace ehs +{ + TwitchChat::~TwitchChat() + { + UnInitialize(); + } + + TwitchChat::TwitchChat() + : initialized(false) + { + } + + TwitchChat::TwitchChat(const Str_8& username) + : username(username), initialized(false) + { + } + + TwitchChat::TwitchChat(const Str_8& username, const Str_8& token) + : username(username), token(token), initialized(false) + { + } + + TwitchChat::TwitchChat(const TwitchChat& chat) + : username(chat.username), token(chat.token), initialized(false) + { + } + + TwitchChat& TwitchChat::operator=(const TwitchChat& chat) + { + if (this == &chat) + return *this; + + client = TCP(); + username = chat.username; + token = chat.token; + channel = Str_8(); + initialized = false; + + return *this; + } + + void TwitchChat::SetToken(const Str_8& newToken) + { + token = newToken; + } + + void TwitchChat::Initialize() + { + if (initialized) + return; + + client = TCP(ehs::AddrType::IPV4); + client.Connect(DNS::Resolve(AddrType::IPV4, "irc.chat.twitch.tv"), 6667); + client.SetBlocking(false); + + Str_8 r("PASS oauth:" + token + "\r\n"); + + Console::Write_8("< " + r, false); + + client.Send(r.ToBytes(), (int) r.Size()); + + r = "NICK " + username + "\r\n"; + + Console::Write_8("< " + r, false); + + client.Send(r.ToBytes(), (int) r.Size()); + + initialized = true; + } + + void TwitchChat::UnInitialize() + { + if (!initialized) + return; + + client.Release(); + + initialized = false; + } + + void TwitchChat::JoinChannel(const Str_8& newChannel) + { + if (!initialized || channel == newChannel) + return; + + channel = newChannel; + + Str_8 r("Join #" + newChannel + "\r\n"); + + Console::Write_8("< " + r, false); + + client.Send(r.ToBytes(), (int) r.Size()); + } + + void TwitchChat::LeaveChannel() + { + if (!initialized) + return; + + Str_8 r("PART #" + channel + "\r\n"); + + Console::Write_8("< " + r, false); + + client.Send(r.ToBytes(), (int) r.Size()); + } + + void TwitchChat::SendPong() + { + if (!initialized) + return; + + Str_8 r("PONG :tmi.twitch.tv\r\n"); + + Console::Write_8("< " + r, false); + + client.Send(r.ToBytes(), (int) r.Size()); + } + + void TwitchChat::SendMsg(const Str_8& msg) + { + if (!initialized) + return; + + Str_8 r("PRIVMSG #" + channel + " :" + msg + "\r\n"); + + Console::Write_8("< " + r, false); + + client.Send(r.ToBytes(), (int) r.Size()); + + //irc.SendStr(":" + username + "!" + username + "@" + username + ".tmi.twitch.tv PRIVMSG #" + username + " :" + msg + "\r\n"); + } + + void TwitchChat::WhisperMsg(const Str_8& user, const Str_8& msg) + { + if (!initialized) + return; + + Str_8 r("PRIVMSG #jtv :/w " + user + " " + msg + "\r\n"); + + Console::Write_8("< " + r, false); + + client.Send(r.ToBytes(), (int) r.Size()); + } + + Str_8 TwitchChat::RecvMsg() + { + Str_8 result; + + Byte received[1024]; + UInt_64 recvSize = 0; + + do + { + recvSize = client.Receive(received, 1024); + if (recvSize) + result.Push((Char_8*) received, recvSize); + else + break; + } while (!result.Find("\r\n", nullptr, SearchPattern::RIGHT_LEFT)); + + return result; + } + + Str_8 TwitchChat::GetUsername() const + { + return username; + } + + Str_8 TwitchChat::GetChannel() const + { + return channel; + } +} \ No newline at end of file diff --git a/src/io/xdg-shell-protocol.c b/src/io/xdg-shell-protocol.c new file mode 100644 index 0000000..03826cd --- /dev/null +++ b/src/io/xdg-shell-protocol.c @@ -0,0 +1,183 @@ +/* Generated by wayland-scanner 1.22.0 */ + +/* + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * Copyright © 2015-2017 Samsung Electronics Co., Ltd + * Copyright © 2015-2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface xdg_positioner_interface; +extern const struct wl_interface xdg_surface_interface; +extern const struct wl_interface xdg_toplevel_interface; + +static const struct wl_interface *xdg_shell_types[] = { + NULL, + NULL, + NULL, + NULL, + &xdg_positioner_interface, + &xdg_surface_interface, + &wl_surface_interface, + &xdg_toplevel_interface, + &xdg_popup_interface, + &xdg_surface_interface, + &xdg_positioner_interface, + &xdg_toplevel_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &xdg_positioner_interface, + NULL, +}; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 6, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 6, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_surface_interface = { + "xdg_surface", 6, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, + { "configure_bounds", "4ii", xdg_shell_types + 0 }, + { "wm_capabilities", "5a", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 6, + 14, xdg_toplevel_requests, + 4, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_popup_interface = { + "xdg_popup", 6, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + diff --git a/src/json/Json.cpp b/src/json/Json.cpp new file mode 100644 index 0000000..c355106 --- /dev/null +++ b/src/json/Json.cpp @@ -0,0 +1,487 @@ +#include "ehs/json/Json.h" +#include "ehs/Log.h" + +namespace ehs +{ + Json::~Json() + { + delete value; + } + + Json::Json() + : value(nullptr) + { + } + + Json::Json(const JsonBase& value) + : value(new JsonBase(value)) + { + } + + Json::Json(const JsonObj& value) + : value((JsonBase*)new JsonObj(value)) + { + } + + Json::Json(const JsonArray& value) + : value((JsonBase*)new JsonArray(value)) + { + } + + Json::Json(const JsonBool& value) + : value((JsonBase*)new JsonBool(value)) + { + } + + Json::Json(const JsonNum& value) + : value((JsonBase*)new JsonNum(value)) + { + } + + Json::Json(const JsonStr& value) + : value((JsonBase*)new JsonStr(value)) + { + } + + Json::Json(const char* data, const UInt_64 size, const UInt_64 extra) + : value(nullptr) + { + Parse({data, size}, extra); + } + + Json::Json(const Str_8& data, const UInt_64 extra) + : value(nullptr) + { + Parse(data, extra); + } + + Json::Json(Json&& json) noexcept + : value(json.value) + { + json.value = nullptr; + } + + Json::Json(const Json& json) + : value(nullptr) + { + if (!json.value) + return; + + switch (json.value->GetType()) + { + case JsonType::OBJ: + value = (JsonBase*)new JsonObj(*(JsonObj*)json.value); + break; + case JsonType::ARRAY: + value = (JsonBase*)new JsonArray(*(JsonArray*)json.value); + break; + case JsonType::BOOL: + value = (JsonBase*)new JsonBool(*(JsonBool*)json.value); + break; + case JsonType::NUM: + value = (JsonBase*)new JsonNum(*(JsonNum*)json.value); + break; + case JsonType::STR: + value = (JsonBase*)new JsonStr(*(JsonStr*)json.value); + break; + default: + value = new JsonBase(); + break; + } + } + + Json& Json::operator=(Json&& json) noexcept + { + if (this == &json) + return *this; + + delete value; + value = json.value; + + json.value = nullptr; + + return *this; + } + + Json& Json::operator=(const Json& json) + { + if (this == &json) + return *this; + + delete value; + value = nullptr; + + if (!json.value) + return *this; + + switch (json.value->GetType()) + { + case JsonType::OBJ: + value = (JsonBase*)new JsonObj(*(JsonObj*)json.value); + break; + case JsonType::ARRAY: + value = (JsonBase*)new JsonArray(*(JsonArray*)json.value); + break; + case JsonType::BOOL: + value = (JsonBase*)new JsonBool(*(JsonBool*)json.value); + break; + case JsonType::NUM: + value = (JsonBase*)new JsonNum(*(JsonNum*)json.value); + break; + case JsonType::STR: + value = (JsonBase*)new JsonStr(*(JsonStr*)json.value); + break; + default: + value = new JsonBase(); + break; + } + + return *this; + } + + JsonBase* Json::GetValue() + { + return value; + } + + JsonBase* Json::RetrieveValue(const Str_8& access) + { + if (!value || !access.Size()) + return nullptr; + + Vector levels = ParseAccess(access); + + JsonBase* hierarchy = value; + + for (UInt_64 i = 0; i < levels.Size(); ++i) + { + if (hierarchy->GetType() == JsonType::ARRAY) + { + if (levels[i][0] >= '0' && levels[i][0] <= '9') + { + hierarchy = (*((JsonArray*)hierarchy))[levels[i].ToDecimal()]; + } + else + return nullptr; + } + else if (hierarchy->GetType() == JsonType::OBJ) + { + if (levels[i][0] < '0' || levels[i][0] > '9') + { + JsonVar* var = ((JsonObj*)hierarchy)->GetVar(levels[i]); + if (!var || !var->GetValue()) + return nullptr; + + hierarchy = var->GetValue(); + } + else + return nullptr; + } + else + return nullptr; + } + + return hierarchy; + } + + Str_8 Json::ToStr(const bool compact) const + { + return value->ToStr(0, compact); + } + + Vector Json::ParseAccess(const Str_8& access) + { + bool isIndex = false; + const Char_8* start = &access[0]; + const Char_8* end = &access[access.Size() - 1]; + + Vector levels(0, 5); + + for (const Char_8* i = &access[0]; i <= end; ++i) + { + if (*i == '[') + { + if (i - start) + levels.Push(Str_8(start, i - start)); + + start = i + 1; + isIndex = true; + } + else if (*i == ']') + { + if (!isIndex) + { + EHS_LOG_INT("Error", 0, "Index has ended, but never started."); + return levels; + } + + if (i - start) + levels.Push(Str_8(start, i - start)); + else + { + EHS_LOG_INT("Error", 1, "Index has no value."); + return levels; + } + + start = i + 1; + isIndex = false; + } + else if (isIndex && (*i < '0' || *i > '9')) + { + EHS_LOG_INT("Error", 2, "Index has an invalid character, \"" + Str_8(i, 1) + "\". Index must be a whole number."); + return levels; + } + else if (*i == '.') + { + if (i - start) + levels.Push(Str_8(start, i - start)); + + start = i + 1; + } + else if (!isIndex && *i != '-' && *i != '_' && (*i < 'A' || *i > 'Z') && (*i < 'a' || *i > 'z')) + { + EHS_LOG_INT("Error", 3, "Member variable has an invalid character, \"" + Str_8(i, 1) + "\"."); + return levels; + } + } + + if (end + 1 - start) + levels.Push(Str_8(start, end + 1 - start)); + + return levels; + } + + void Json::ParseValue(JsonVar* var, const Char_8** begin, const Char_8* end, const UInt_64 extra) + { + const Char_8* start = nullptr; + bool isStr = false; + + for (; *begin <= end; ++(*begin)) + { + if (**begin == '\"') + { + if (start) + { + if (*begin - start) + ((JsonStr*)var->GetValue())->value = Str_8(start, *begin - start); + + ++(*begin); + return; + } + else + { + start = *begin + 1; + isStr = true; + var->SetValue(JsonStr()); + } + } + else if (!start && **begin == '{') + { + ++(*begin); + JsonObj obj(extra); + ParseObject(&obj, begin, end, extra); + var->SetValue(obj); + return; + } + else if (!start && **begin == '[') + { + ++(*begin); + JsonArray arr(extra); + ParseArray(&arr, begin, end, extra); + var->SetValue(arr); + return; + } + else if (!start && **begin != ' ' && **begin != ',' && **begin != '\r' && **begin != '\n' && **begin != '\t') + { + start = *begin; + } + else if (start && !isStr && (**begin == '\t' || **begin == '\r' || **begin == '\n' || (!var->GetValue() && **begin == ' ') || **begin == ',' || **begin == '}' || **begin == ']')) + { + Str_8 result(start, *begin - start); + if (result == "true") + var->SetValue(true); + else if (result == "false") + var->SetValue(false); + else if (result == "null") + var->SetValue(JsonBase()); + else if (result.IsNum()) + var->SetValue(result.ToFloat()); + + return; + } + } + } + + JsonVar Json::ParseVar(const Char_8** begin, const Char_8* end, const UInt_64 extra) + { + JsonVar var; + + const Char_8* start = nullptr; + + for (; *begin <= end; ++(*begin)) + { + if (**begin == '}' || **begin == ']') + { + ++(*begin); + return var; + } + else if (**begin == '"') + { + if (start) + { + var = JsonVar(Str_8(start, *begin - start)); + start = nullptr; + } + else + start = *begin + 1; + } + else if (!start && **begin == ':') + { + if (var.GetHashId()) + { + ++(*begin); + + ParseValue(&var, begin, end, extra); + + return var; + } + } + } + + return var; + } + + void Json::ParseObject(JsonObj* obj, const Char_8** begin, const Char_8* end, const UInt_64 extra) + { + JsonVar var; + do + { + var = ParseVar(begin, end, extra); + } + while(obj->AddVar(var)); + } + + void Json::ParseArray(JsonArray* arr, const Char_8** begin, const Char_8* end, const UInt_64 extra) + { + const Char_8* start = nullptr; + + for (; *begin <= end; ++(*begin)) + { + if (**begin == '}' || **begin == ']') + { + ++(*begin); + return; + } + else if (**begin == '\"') + { + if (start) + { + if (*begin - start) + arr->Push(start, *begin - start); + + start = nullptr; + ++(*begin); + continue; + } + else + start = *begin + 1; + } + else if (!start && **begin == '{') + { + ++(*begin); + JsonObj obj(extra); + ParseObject(&obj, begin, end, extra); + arr->Push(obj); + continue; + } + else if (!start && **begin == '[') + { + ++(*begin); + JsonArray array(extra); + ParseArray(&array, begin, end, extra); + arr->Push(array); + continue; + } + else if (!start && **begin != ' ' && **begin != ',' && **begin != '\r' && **begin != '\n' && **begin != '\t') + { + start = *begin; + } + else if (start && *start != '\"' && (**begin == '\t' || **begin == '\r' || **begin == '\n' || **begin == ' ' || **begin == ',' || **begin == '}' || **begin == ']')) + { + Str_8 result(start, *begin - start); + if (result == "true") + arr->Push(true); + else if (result == "false") + arr->Push(false); + else if (result == "null") + arr->Push(JsonBase()); + else if (result.IsNum()) + arr->Push(result.ToFloat()); + + continue; + } + } + } + + void Json::Parse(const Str_8& data, const UInt_64 extra) + { + const Char_8* start = nullptr; + const Char_8* end = &data[data.Size() - 1]; + + for (const Char_8* i = &data[0]; i <= end; ++i) + { + if (*i == '}' || *i == ']') + { + ++i; + return; + } + else if (*i == '\"') + { + if (start) + { + if (i - start) + value = new JsonStr(start, i - start); + + ++i; + return; + } + else + start = i + 1; + } + else if (!start && *i == '{') + { + ++i; + JsonObj* obj = new JsonObj(extra); + ParseObject(obj, &i, end, extra); + value = obj; + return; + } + else if (!start && *i == '[') + { + ++i; + JsonArray* arr = new JsonArray(extra); + ParseArray(arr, &i, end, extra); + value = arr; + return; + } + else if (!start && *i != ' ' && *i != ',' && *i != '\r' && *i != '\n' && *i != '\t') + { + start = i; + } + else if (start && *start != '\"' && (*i == '\t' || *i == '\r' || *i == '\n' || *i == ' ' || *i == ',' || *i == '}' || *i == ']')) + { + Str_8 result(start, i - start); + if (result == "true") + value = new JsonBool(true); + else if (result == "false") + value = new JsonBool(false); + else if (result == "null") + value = new JsonBase(); + else if (result.IsNum()) + value = new JsonNum(result.ToFloat()); + + return; + } + } + } +} \ No newline at end of file diff --git a/src/json/JsonArray.cpp b/src/json/JsonArray.cpp new file mode 100644 index 0000000..3bf60f8 --- /dev/null +++ b/src/json/JsonArray.cpp @@ -0,0 +1,528 @@ +#include "ehs/json/JsonArray.h" +#include "ehs/json/JsonObj.h" +#include "ehs/json/JsonBool.h" +#include "ehs/json/JsonNum.h" +#include "ehs/json/JsonStr.h" + +namespace ehs +{ + JsonArray::~JsonArray() + { + for (UInt_64 i = 0; i < size; ++i) + delete data[i]; + + delete[] data; + } + + JsonArray::JsonArray() + : JsonBase(JsonType::ARRAY), size(0), extra(0), rawSize(0), data(nullptr) + { + } + + JsonArray::JsonArray(const UInt_64 extra) + : JsonBase(JsonType::ARRAY), size(0), extra(extra), rawSize(extra), data(new JsonBase*[rawSize]) + { + } + + JsonArray::JsonArray(const UInt_64 size, const UInt_64 extra) + : JsonBase(JsonType::ARRAY), size(size), extra(extra), rawSize(size + extra), data(new JsonBase*[rawSize]) + { + } + + JsonArray::JsonArray(JsonArray&& ja) noexcept + : JsonBase(ja), size(ja.size), extra(ja.extra), rawSize(ja.rawSize), data(ja.data) + { + ja.size = 0; + ja.extra = 0; + ja.rawSize = 0; + ja.data = nullptr; + } + + JsonArray::JsonArray(const JsonArray& ja) + : JsonBase(ja), size(ja.size), extra(ja.extra), rawSize(ja.rawSize), data(new JsonBase*[ja.rawSize]) + { + for (UInt_64 i = 0; i < size; ++i) + { + switch (ja.data[i]->GetType()) + { + case JsonType::OBJ: + data[i] = (JsonBase*)new JsonObj(*(JsonObj*)ja.data[i]); + break; + case JsonType::ARRAY: + data[i] = (JsonBase*)new JsonArray(*(JsonArray*)ja.data[i]); + break; + case JsonType::BOOL: + data[i] = (JsonBase*)new JsonBool(*(JsonBool*)ja.data[i]); + break; + case JsonType::NUM: + data[i] = (JsonBase*)new JsonNum(*(JsonNum*)ja.data[i]); + break; + case JsonType::STR: + data[i] = (JsonBase*)new JsonStr(*(JsonStr*)ja.data[i]); + break; + default: + data[i] = new JsonBase(); + break; + } + } + } + + JsonArray& JsonArray::operator=(JsonArray&& ja) noexcept + { + if (this == &ja) + return *this; + + JsonBase::operator=(ja); + + size = ja.size; + extra = ja.extra; + rawSize = ja.rawSize; + delete[] data; + data = ja.data; + + ja.size = 0; + ja.extra = 0; + ja.rawSize = 0; + ja.data = nullptr; + + return *this; + } + + JsonArray& JsonArray::operator=(const JsonArray& ja) + { + if (this == &ja) + return *this; + + JsonBase::operator=(ja); + + size = ja.size; + extra = ja.size; + rawSize = ja.rawSize; + + delete[] data; + data = new JsonBase*[rawSize]; + for (UInt_64 i = 0; i < ja.size; ++i) + { + switch (ja.data[i]->GetType()) + { + case JsonType::OBJ: + data[i] = (JsonBase*)new JsonObj(*(JsonObj*)ja.data[i]); + break; + case JsonType::ARRAY: + data[i] = (JsonBase*)new JsonArray(*(JsonArray*)ja.data[i]); + break; + case JsonType::BOOL: + data[i] = (JsonBase*)new JsonBool(*(JsonBool*)ja.data[i]); + break; + case JsonType::NUM: + data[i] = (JsonBase*)new JsonNum(*(JsonNum*)ja.data[i]); + break; + case JsonType::STR: + data[i] = (JsonBase*)new JsonStr(*(JsonStr*)ja.data[i]); + break; + default: + data[i] = new JsonBase(JsonType::NULLOBJ); + break; + } + } + + return *this; + } + + JsonArray::operator JsonBase* const *() const + { + return data; + } + + JsonArray::operator JsonBase**() + { + return data; + } + + UInt_64 JsonArray::RawSize() const + { + return rawSize; + } + + UInt_64 JsonArray::Extra() const + { + return extra; + } + + UInt_64 JsonArray::Size() const + { + return size; + } + + void JsonArray::Insert(const UInt_64 index, const JsonBase* const value) + { + if (size + 1 >= rawSize) + rawSize = size + extra; + + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < index; ++i) + result[i] = data[i]; + + switch (value->GetType()) + { + case JsonType::OBJ: + result[index] = (JsonBase*)new JsonObj(*(JsonObj*)value); + break; + case JsonType::ARRAY: + result[index] = (JsonBase*)new JsonArray(*(JsonArray*)value); + break; + case JsonType::BOOL: + result[index] = (JsonBase*)new JsonBool(*(JsonBool*)value); + break; + case JsonType::NUM: + result[index] = (JsonBase*)new JsonNum(*(JsonNum*)value); + break; + case JsonType::STR: + result[index] = (JsonBase*)new JsonStr(*(JsonStr*)value); + break; + default: + result[index] = new JsonBase(JsonType::NULLOBJ); + break; + } + + for (UInt_64 i = index; i < size; ++i) + result[i + 1] = data[i]; + + delete[] data; + + data = result; + ++size; + } + + void JsonArray::Push(const JsonBase* const value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + switch (value->GetType()) + { + case JsonType::OBJ: + result[size++] = (JsonBase*)new JsonObj(*(JsonObj*)value); + break; + case JsonType::ARRAY: + result[size++] = (JsonBase*)new JsonArray(*(JsonArray*)value); + break; + case JsonType::BOOL: + result[size++] = (JsonBase*)new JsonBool(*(JsonBool*)value); + break; + case JsonType::NUM: + result[size++] = (JsonBase*)new JsonNum(*(JsonNum*)value); + break; + case JsonType::STR: + result[size++] = (JsonBase*)new JsonStr(*(JsonStr*)value); + break; + default: + result[size++] = new JsonBase(JsonType::NULLOBJ); + break; + } + + delete[] data; + + data = result; + } + else + { + switch (value->GetType()) + { + case JsonType::OBJ: + data[size++] = (JsonBase*)new JsonObj(*(JsonObj*)value); + break; + case JsonType::ARRAY: + data[size++] = (JsonBase*)new JsonArray(*(JsonArray*)value); + break; + case JsonType::BOOL: + data[size++] = (JsonBase*)new JsonBool(*(JsonBool*)value); + break; + case JsonType::NUM: + data[size++] = (JsonBase*)new JsonNum(*(JsonNum*)value); + break; + case JsonType::STR: + data[size++] = (JsonBase*)new JsonStr(*(JsonStr*)value); + break; + default: + data[size++] = new JsonBase(JsonType::NULLOBJ); + break; + } + } + } + + void JsonArray::Push(const JsonBase& value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = new JsonBase(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = new JsonBase(value); + } + } + + void JsonArray::Push(const JsonObj& value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = (JsonBase*)new JsonObj(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = (JsonBase*)new JsonObj(value); + } + } + + void JsonArray::Push(const JsonArray& value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = (JsonBase*)new JsonArray(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = (JsonBase*)new JsonArray(value); + } + } + + void JsonArray::Push(const JsonBool& value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = (JsonBase*)new JsonBool(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = (JsonBase*)new JsonBool(value); + } + } + + void JsonArray::Push(const bool value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = (JsonBase*)new JsonBool(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = (JsonBase*)new JsonBool(value); + } + } + + void JsonArray::Push(const JsonNum& value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = (JsonBase*)new JsonNum(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = (JsonBase*)new JsonNum(value); + } + } + + void JsonArray::Push(const float value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = (JsonBase*)new JsonNum(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = (JsonBase*)new JsonNum(value); + } + } + + void JsonArray::Push(const JsonStr& value) + { + if (size + 1 > rawSize) + { + rawSize = size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + result[size++] = (JsonBase*)new JsonStr(value); + + delete[] data; + + data = result; + } + else + { + data[size++] = (JsonBase*)new JsonStr(value); + } + } + + void JsonArray::Push(const Char_8* value, const UInt_64 size) + { + if (this->size + 1 > rawSize) + { + rawSize = this->size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < this->size; ++i) + result[i] = data[i]; + + result[this->size++] = (JsonBase*)new JsonStr(Str_8(value, size)); + + delete[] data; + + data = result; + } + else + { + data[this->size++] = (JsonBase*)new JsonStr(Str_8(value, size)); + } + } + + void JsonArray::Push(const Str_8& value) + { + if (this->size + 1 > rawSize) + { + rawSize = this->size + extra + 1; + JsonBase** result = new JsonBase*[rawSize]; + + for (UInt_64 i = 0; i < this->size; ++i) + result[i] = data[i]; + + result[this->size++] = (JsonBase*)new JsonStr(value); + + delete[] data; + + data = result; + } + else + { + data[this->size++] = (JsonBase*)new JsonStr(value); + } + } + + void JsonArray::Pop() + { + if (rawSize - --size >= extra * 2) + rawSize -= extra; + + JsonBase** result = new JsonBase*[rawSize]; + for (UInt_64 i = 0; i < size; ++i) + result[i] = data[i]; + + delete[] data; + + data = result; + } + + Str_8 JsonArray::ToStr(const UInt_64 level, const bool compact) const + { + Str_8 result = "["; + + if (!compact) + result += "\r\n"; + + for (UInt_64 i = 0; i < size; ++i) + { + if (!compact && (data[i]->GetType() == JsonType::OBJ || data[i]->GetType() == JsonType::ARRAY)) + { + for (UInt_64 l = 0; l < level + 1; ++l) + result += "\t"; + } + + result += data[i]->ToStr(level + 1, compact); + + if (i < size - 1) + result += ","; + + if (!compact) + result += "\r\n"; + } + + if (!compact) + { + for (UInt_64 l = 0; l < level; ++l) + result += "\t"; + } + + result += "]"; + + return result; + } +} \ No newline at end of file diff --git a/src/json/JsonBase.cpp b/src/json/JsonBase.cpp new file mode 100644 index 0000000..0204e5a --- /dev/null +++ b/src/json/JsonBase.cpp @@ -0,0 +1,24 @@ +#include "ehs/json/JsonBase.h" + +namespace ehs +{ + JsonBase::JsonBase() + : type(JsonType::NULLOBJ) + { + } + + JsonBase::JsonBase(const JsonType type) + : type(type) + { + } + + JsonType JsonBase::GetType() const + { + return type; + } + + Str_8 JsonBase::ToStr(const UInt_64 level, const bool compact) const + { + return {}; + } +} \ No newline at end of file diff --git a/src/json/JsonBool.cpp b/src/json/JsonBool.cpp new file mode 100644 index 0000000..9f67930 --- /dev/null +++ b/src/json/JsonBool.cpp @@ -0,0 +1,39 @@ +#include "ehs/json/JsonBool.h" + +namespace ehs +{ + JsonBool::JsonBool() + : JsonBase(JsonType::BOOL), value(false) + { + } + + JsonBool::JsonBool(const bool value) + : JsonBase(JsonType::BOOL), value(value) + { + } + + JsonBool::operator bool() const + { + return value; + } + + JsonBool::operator bool&() + { + return value; + } + + Str_8 JsonBool::ToStr(const UInt_64 level, const bool compact) const + { + Str_8 result; + + if (!compact) + { + for (UInt_64 l = 0; l < level; ++l) + result += "\t"; + } + + result += value ? "true" : "false"; + + return result; + } +} \ No newline at end of file diff --git a/src/json/JsonNum.cpp b/src/json/JsonNum.cpp new file mode 100644 index 0000000..ab8ca1f --- /dev/null +++ b/src/json/JsonNum.cpp @@ -0,0 +1,93 @@ +#include "ehs/json/JsonNum.h" + +namespace ehs +{ + JsonNum::JsonNum() + : JsonBase(JsonType::NUM), value(0.0f) + { + } + + JsonNum::JsonNum(const SInt_64 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const UInt_64 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const SInt_32 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const UInt_32 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const SInt_16 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const UInt_16 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const SInt_8 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const UInt_8 value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const double value) + : JsonBase(JsonType::NUM), value((float)value) + { + + } + + JsonNum::JsonNum(const float value) + : JsonBase(JsonType::NUM), value(value) + { + } + + JsonNum::operator float() const + { + return value; + } + + JsonNum::operator float&() + { + return value; + } + + Str_8 JsonNum::ToStr(const UInt_64 level, const bool compact) const + { + Str_8 result; + + if (!compact) + { + for (UInt_64 l = 0; l < level; ++l) + result += "\t"; + } + + result += Str_8::FromNum(value); + + return result; + } +} \ No newline at end of file diff --git a/src/json/JsonObj.cpp b/src/json/JsonObj.cpp new file mode 100644 index 0000000..c7bcd6c --- /dev/null +++ b/src/json/JsonObj.cpp @@ -0,0 +1,201 @@ +#include "ehs/json/JsonObj.h" +#include "ehs/json/JsonVar.h" + +namespace ehs +{ + JsonObj::~JsonObj() + { + delete[] vars; + } + + JsonObj::JsonObj() + : JsonBase(JsonType::OBJ), size(0), extra(0), rawSize(0), vars(nullptr) + { + } + + JsonObj::JsonObj(const UInt_64 size, const UInt_64 extra) + : JsonBase(JsonType::OBJ), size(size), extra(extra), rawSize(size + extra), vars(new JsonVar[size + extra]) + { + } + + JsonObj::JsonObj(const UInt_64 extra) + : JsonBase(JsonType::OBJ), size(0), extra(extra), rawSize(extra), vars(new JsonVar[extra]) + { + } + + JsonObj::JsonObj(JsonObj&& value) noexcept + : JsonBase(value), size(value.size), extra(value.extra), rawSize(value.rawSize), vars(value.vars) + { + value.size = 0; + value.extra = 0; + value.rawSize = 0; + value.vars = nullptr; + } + + JsonObj::JsonObj(const JsonObj& value) + : JsonBase(value), size(value.size), extra(value.extra), rawSize(value.rawSize), vars(new JsonVar[value.rawSize]) + { + for (UInt_64 i = 0; i < size; ++i) + vars[i] = value.vars[i]; + } + + JsonObj& JsonObj::operator=(JsonObj&& value) noexcept + { + if (this == &value) + return *this; + + JsonBase::operator=(value); + + size = value.size; + extra = value.extra; + rawSize = value.rawSize; + delete[] vars; + vars = value.vars; + + value.size = 0; + value.extra = 0; + value.rawSize = 0; + value.vars = nullptr; + + return *this; + } + + JsonObj& JsonObj::operator=(const JsonObj& value) + { + if (this == &value) + return *this; + + JsonBase::operator=(value); + + size = value.size; + extra = value.extra; + rawSize = value.rawSize; + + delete[] vars; + vars = new JsonVar[value.rawSize]; + for (UInt_64 i = 0; i < value.rawSize; ++i) + vars[i] = value.vars[i]; + + return *this; + } + + JsonObj::operator const JsonVar*() const + { + return vars; + } + + JsonObj::operator JsonVar*() + { + return vars; + } + + UInt_64 JsonObj::Size() const + { + return size; + } + + UInt_64 JsonObj::Extra() const + { + return extra; + } + + UInt_64 JsonObj::RawSize() const + { + return rawSize; + } + + bool JsonObj::HasVar(const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < size; ++i) + if (vars[i].GetHashId() == hashId) + return true; + + return false; + } + + bool JsonObj::HasVar(const Str_8& identifier) const + { + return HasVar(identifier.Hash_64()); + } + + bool JsonObj::AddVar(const JsonVar& var) + { + if (!var.GetHashId() || HasVar(var.GetHashId())) + return false; + + if (size + 1 > rawSize) + { + rawSize += extra ? extra : 1; + JsonVar* result = new JsonVar[rawSize]; + + for (UInt_64 i = 0; i < size; ++i) + result[i] = vars[i]; + + delete[] vars; + vars = result; + } + + vars[size++] = var; + + return true; + } + + const JsonVar* JsonObj::GetVar(const UInt_64 hashId) const + { + for (UInt_64 i = 0; i < size; ++i) + if (vars[i].GetHashId() == hashId) + return &vars[i]; + + return nullptr; + } + + const JsonVar* JsonObj::GetVar(const Str_8& identifier) const + { + return GetVar(identifier.Hash_64()); + } + + JsonVar* JsonObj::GetVar(const UInt_64 hashId) + { + for (UInt_64 i = 0; i < size; ++i) + if (vars[i].GetHashId() == hashId) + return &vars[i]; + + return nullptr; + } + + JsonVar* JsonObj::GetVar(const Str_8& identifier) + { + return GetVar(identifier.Hash_64()); + } + + Str_8 JsonObj::ToStr(const UInt_64 level, const bool compact) const + { + Str_8 result; + + result += "{"; + + if (!compact) + result += "\r\n"; + + for (UInt_64 i = 0; i < size; ++i) + { + result += vars[i].ToStr(level + 1, compact); + + if (i < size - 1) + result += ","; + + if (!compact) + result += "\r\n"; + } + + if (!compact) + { + for (UInt_64 l = 0; l < level; ++l) + result += "\t"; + } + + result += "}"; + + return result; + } +} \ No newline at end of file diff --git a/src/json/JsonStr.cpp b/src/json/JsonStr.cpp new file mode 100644 index 0000000..9839b90 --- /dev/null +++ b/src/json/JsonStr.cpp @@ -0,0 +1,62 @@ +#include "ehs/json/JsonStr.h" + +namespace ehs +{ + JsonStr::JsonStr() + : JsonBase(JsonType::STR) + { + } + + JsonStr::JsonStr(Str_8 value) + : JsonBase(JsonType::STR), value(std::move(value)) + { + + } + + JsonStr::JsonStr(const Char_8* value, const UInt_64 size) + : JsonBase(JsonType::STR), value(value, size) + { + } + + JsonStr::JsonStr(JsonStr&& js) noexcept + : JsonBase(js), value(std::move(js.value)) + { + } + + JsonStr& JsonStr::operator=(JsonStr&& js) noexcept + { + if (this == &js) + return *this; + + JsonBase::operator=(js); + + value = std::move(js.value); + + return *this; + } + + JsonStr::operator Str_8() const + { + return value; + } + + JsonStr::operator Str_8&() + { + return value; + } + + Str_8 JsonStr::ToStr(const UInt_64 level, const bool compact) const + { + Str_8 result; + + if (!compact) + { + for (UInt_64 l = 0; l < level; ++l) + result += "\t"; + } + + result += "\"" + value + "\""; + + return result; + } +} \ No newline at end of file diff --git a/src/json/JsonVar.cpp b/src/json/JsonVar.cpp new file mode 100644 index 0000000..9e17324 --- /dev/null +++ b/src/json/JsonVar.cpp @@ -0,0 +1,369 @@ +#include "ehs/json/JsonVar.h" +#include "ehs/json/JsonBase.h" +#include "ehs/json/JsonObj.h" +#include "ehs/json/JsonArray.h" +#include "ehs/json/JsonBool.h" +#include "ehs/json/JsonNum.h" +#include "ehs/json/JsonStr.h" + +namespace ehs +{ + JsonVar::~JsonVar() + { + delete value; + } + + JsonVar::JsonVar() + : hashId(0), value(nullptr) + { + } + + JsonVar::JsonVar(Str_8 id) + : hashId(id.Hash_64()), id(std::move(id)), value(nullptr) + { + } + + JsonVar::JsonVar(Str_8 id, const JsonBase* const value) + : hashId(id.Hash_64()), id(std::move(id)) + { + switch (value->GetType()) + { + case JsonType::OBJ: + this->value = (JsonBase*)new JsonObj(*(JsonObj*)value); + break; + case JsonType::ARRAY: + this->value = (JsonBase*)new JsonArray(*(JsonArray*)value); + break; + case JsonType::BOOL: + this->value = (JsonBase*)new JsonBool(*(JsonBool*)value); + break; + case JsonType::NUM: + this->value = (JsonBase*)new JsonNum(*(JsonNum*)value); + break; + case JsonType::STR: + this->value = (JsonBase*)new JsonStr(*(JsonStr*)value); + break; + default: + this->value = new JsonBase(JsonType::NULLOBJ); + break; + } + } + + JsonVar::JsonVar(Str_8 id, const JsonBase& value) + : hashId(id.Hash_64()), id(std::move(id)), value(new JsonBase(value)) + { + } + + JsonVar::JsonVar(Str_8 id, const JsonObj& value) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonObj(value)) + { + } + + JsonVar::JsonVar(Str_8 id, const JsonArray& value) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonArray(value)) + { + } + + JsonVar::JsonVar(Str_8 id, const JsonBool& value) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonBool(value)) + { + } + + JsonVar::JsonVar(Str_8 id, const bool boolean) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonBool(boolean)) + { + } + + JsonVar::JsonVar(Str_8 id, const JsonNum& value) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(value)) + { + } + + JsonVar::JsonVar(Str_8 id, const SInt_64 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const UInt_64 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const SInt_32 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const UInt_32 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const SInt_16 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const UInt_16 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const SInt_8 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const UInt_8 num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const double num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const float num) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonNum(num)) + { + } + + JsonVar::JsonVar(Str_8 id, const JsonStr& value) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonStr(value)) + { + } + + JsonVar::JsonVar(Str_8 id, const Char_8* str, const UInt_64 size) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonStr(str, size)) + { + } + + JsonVar::JsonVar(Str_8 id, Str_8 str) + : hashId(id.Hash_64()), id(std::move(id)), value((JsonBase*)new JsonStr(std::move(str))) + { + } + + JsonVar::JsonVar(JsonVar&& var) noexcept + : hashId(var.hashId), id(std::move(var.id)), value(var.value) + { + var.hashId = 0; + var.value = nullptr; + } + + JsonVar::JsonVar(const JsonVar& var) + : hashId(var.hashId), id(var.id), value(nullptr) + { + if (!var.value) + return; + + switch (var.value->GetType()) + { + case JsonType::OBJ: + value = (JsonBase*)new JsonObj(*(JsonObj*)var.value); + break; + case JsonType::ARRAY: + value = (JsonBase*)new JsonArray(*(JsonArray*)var.value); + break; + case JsonType::BOOL: + value = (JsonBase*)new JsonBool(*(JsonBool*)var.value); + break; + case JsonType::NUM: + value = (JsonBase*)new JsonNum(*(JsonNum*)var.value); + break; + case JsonType::STR: + value = (JsonBase*)new JsonStr(*(JsonStr*)var.value); + break; + default: + value = new JsonBase(); + break; + } + } + + JsonVar& JsonVar::operator=(JsonVar&& var) noexcept + { + if (this == &var) + return *this; + + hashId = var.hashId; + id = std::move(var.id); + delete value; + value = var.value; + + var.hashId = 0; + var.value = nullptr; + + return *this; + } + + JsonVar& JsonVar::operator=(const JsonVar& var) + { + if (this == &var) + return *this; + + hashId = var.hashId; + id = var.id; + delete value; + value = nullptr; + + if (!var.value) + return *this; + + switch (var.value->GetType()) + { + case JsonType::OBJ: + value = (JsonBase*)new JsonObj(*(JsonObj*)var.value); + break; + case JsonType::ARRAY: + value = (JsonBase*)new JsonArray(*(JsonArray*)var.value); + break; + case JsonType::BOOL: + value = (JsonBase*)new JsonBool(*(JsonBool*)var.value); + break; + case JsonType::NUM: + value = (JsonBase*)new JsonNum(*(JsonNum*)var.value); + break; + case JsonType::STR: + value = (JsonBase*)new JsonStr(*(JsonStr*)var.value); + break; + default: + value = new JsonBase(); + break; + } + + return *this; + } + + UInt_64 JsonVar::GetHashId() const + { + return hashId; + } + + Str_8 JsonVar::GetId() const + { + return id; + } + + void JsonVar::SetValue(const JsonBase* const newValue) + { + if (!newValue) + return; + + delete value; + + switch (newValue->GetType()) + { + case JsonType::OBJ: + value = (JsonBase*)new JsonObj(*(JsonObj*)newValue); + break; + case JsonType::ARRAY: + value = (JsonBase*)new JsonArray(*(JsonArray*)newValue); + break; + case JsonType::BOOL: + value = (JsonBase*)new JsonBool(*(JsonBool*)newValue); + break; + case JsonType::NUM: + value = (JsonBase*)new JsonNum(*(JsonNum*)newValue); + break; + case JsonType::STR: + value = (JsonBase*)new JsonStr(*(JsonStr*)newValue); + break; + default: + value = new JsonBase(); + break; + } + } + + void JsonVar::SetValue(const JsonBase& newValue) + { + delete value; + value = new JsonBase(newValue); + } + + void JsonVar::SetValue(const JsonObj& newValue) + { + delete value; + value = (JsonBase*)new JsonObj(newValue); + } + + void JsonVar::SetValue(const JsonArray& newValue) + { + delete value; + value = (JsonBase*)new JsonArray(newValue); + } + + void JsonVar::SetValue(const JsonBool& newValue) + { + delete value; + value = (JsonBase*)new JsonBool(newValue); + } + + void JsonVar::SetValue(const bool newValue) + { + delete value; + value = (JsonBase*)new JsonBool(newValue); + } + + void JsonVar::SetValue(const JsonNum& newValue) + { + delete value; + value = (JsonBase*)new JsonNum(newValue); + } + + void JsonVar::SetValue(const float newValue) + { + delete value; + value = (JsonBase*)new JsonNum(newValue); + } + + void JsonVar::SetValue(const JsonStr& newValue) + { + delete value; + value = (JsonBase*)new JsonStr(newValue); + } + + void JsonVar::SetValue(const Char_8* newValue, const UInt_64 size) + { + delete value; + value = (JsonBase*)new JsonStr(newValue, size); + } + + void JsonVar::SetValue(const Str_8& newValue) + { + delete value; + value = (JsonBase*)new JsonStr(newValue); + } + + const JsonBase* JsonVar::GetValue() const + { + return value; + } + + JsonBase* JsonVar::GetValue() + { + return value; + } + + Str_8 JsonVar::ToStr(const UInt_64 level, const bool compact) const + { + Str_8 result; + + if (!compact) + { + for (UInt_64 l = 0; l < level; ++l) + result += "\t"; + } + + result += "\"" + id + "\":"; + + if (!compact) + result += " "; + + if (value->GetType() == JsonType::OBJ || value->GetType() == JsonType::ARRAY) + result += value->ToStr(level, compact); + else + result += value->ToStr(0, compact); + + return result; + } +} \ No newline at end of file diff --git a/src/system/BaseMutex.cpp b/src/system/BaseMutex.cpp new file mode 100644 index 0000000..ac0d85a --- /dev/null +++ b/src/system/BaseMutex.cpp @@ -0,0 +1,55 @@ +#include "ehs/system/BaseMutex.h" + +namespace ehs +{ + BaseMutex::~BaseMutex() + { + } + + BaseMutex::BaseMutex() + : initialized(false), locked(false) + { + } + + BaseMutex::BaseMutex(const BaseMutex& mutex) + : initialized(false), locked(false) + { + } + + BaseMutex& BaseMutex::operator=(const BaseMutex& mutex) + { + if (this == &mutex) + return *this; + + initialized = mutex.initialized; + locked = mutex.locked; + + return *this; + } + + void BaseMutex::Initialize() + { + } + + void BaseMutex::UnInitialize() + { + } + + bool BaseMutex::IsInitialized() const + { + return initialized; + } + + void BaseMutex::Lock() + { + } + + void BaseMutex::Unlock() + { + } + + bool BaseMutex::IsLocked() const + { + return locked; + } +} \ No newline at end of file diff --git a/src/system/BaseOpen.cpp b/src/system/BaseOpen.cpp new file mode 100644 index 0000000..4ba6a5f --- /dev/null +++ b/src/system/BaseOpen.cpp @@ -0,0 +1,48 @@ +#include "ehs/system/BaseOpen.h" + +namespace ehs +{ + BaseOpen::BaseOpen() + { + } + + BaseOpen::BaseOpen(Str_8 filePath) + : filePath((Str_8&&)filePath) + { + } + + BaseOpen::BaseOpen(BaseOpen&& bo) noexcept + : filePath((Str_8&&)bo.filePath) + { + } + + BaseOpen::BaseOpen(const BaseOpen& bo) + : filePath(bo.filePath) + { + } + + BaseOpen& BaseOpen::operator=(BaseOpen&& bo) noexcept + { + if (this == &bo) + return *this; + + filePath = (Str_8&&)bo.filePath; + + return *this; + } + + BaseOpen& BaseOpen::operator=(const BaseOpen& bo) + { + if (this == &bo) + return *this; + + filePath = bo.filePath; + + return *this; + } + + Str_8 BaseOpen::GetFilePath() const + { + return filePath; + } +} \ No newline at end of file diff --git a/src/system/BaseSemaphore.cpp b/src/system/BaseSemaphore.cpp new file mode 100644 index 0000000..99ca636 --- /dev/null +++ b/src/system/BaseSemaphore.cpp @@ -0,0 +1,61 @@ +#include "ehs/system/BaseSemaphore.h" + +namespace ehs +{ + BaseSemaphore::BaseSemaphore() + : initial(0) + { + } + + BaseSemaphore::BaseSemaphore(Str_8 name, const UInt_32 initial) + : name(std::move(name)), initial(initial) + { + } + + BaseSemaphore::BaseSemaphore(const UInt_32 initial) + : initial(initial) + { + } + + BaseSemaphore::BaseSemaphore(BaseSemaphore&& sem) noexcept + : name(std::move(sem.name)), initial(sem.initial) + { + } + + BaseSemaphore::BaseSemaphore(const BaseSemaphore& sem) + : name(sem.name), initial(sem.initial) + { + } + + BaseSemaphore& BaseSemaphore::operator=(const BaseSemaphore& sem) + { + if (this == &sem) + return *this; + + name = sem.name; + initial = sem.initial; + + return *this; + } + + BaseSemaphore& BaseSemaphore::operator=(BaseSemaphore&& sem) noexcept + { + if (this == &sem) + return *this; + + name = std::move(sem.name); + initial = sem.initial; + + return *this; + } + + Str_8 BaseSemaphore::GetName() const + { + return name; + } + + UInt_32 BaseSemaphore::GetInitial() const + { + return initial; + } +} \ No newline at end of file diff --git a/src/system/BaseSystem.cpp b/src/system/BaseSystem.cpp new file mode 100644 index 0000000..a874d1d --- /dev/null +++ b/src/system/BaseSystem.cpp @@ -0,0 +1,8 @@ +#include "ehs/system/BaseSystem.h" + +namespace ehs +{ + void BaseSystem::OpenURI(const Str_8& uri) + { + } +} \ No newline at end of file diff --git a/src/system/CPU.cpp b/src/system/CPU.cpp new file mode 100644 index 0000000..ccab72e --- /dev/null +++ b/src/system/CPU.cpp @@ -0,0 +1,561 @@ +#include "ehs/system/CPU.h" +#include "ehs/Log.h" +#include "ehs/io/File.h" +#include "ehs/json/Json.h" +#include "ehs/system/Thread.h" + +namespace ehs +{ + #ifdef EHS_OS_LINUX + UInt_64 CPU::TSC_Freq = 0; + #endif + + Architecture CPU::GetArchitecture() + { + #if defined(EHS_ARCH_X64) + return Architecture::X64; + #elif defined(EHS_ARCH_ARM64) + return Architecture::ARM64; + #else + return Architecture::UNKNOWN; + #endif + } + + UInt_8 CPU::PointerSize() + { + return sizeof(void*); + } + + Endianness CPU::GetEndianness() + { + #if defined(EHS_LITTLE_ENDIAN) + return Endianness::LE; + #elif defined(EHS_BIG_ENDIAN) + return Endianness::BE; + #else + UInt_16 tmp = 1; + if (((Byte*)&tmp)[0] == 1) + return Endianness::LE; + + return Endianness::BE; + #endif + } + + UInt_64 CPU::GetTSC_Freq() + { + #if defined(EHS_OS_WINDOWS) + LARGE_INTEGER frequency = {}; + QueryPerformanceFrequency(&frequency); + + return frequency.QuadPart; + #elif defined(EHS_OS_LINUX) + if (!TSC_Freq) + TSC_Freq = RetrieveTSC_Freq(); + + return TSC_Freq; + #endif + + return 0; + } + + UInt_64 CPU::GetTSC() + { + #if defined(EHS_OS_WINDOWS) + LARGE_INTEGER count = {}; + QueryPerformanceCounter(&count); + + return count.QuadPart; + #elif defined(EHS_OS_LINUX) + TSC tsc; + RDTSCP(&tsc); + + #if defined(EHS_ARCH_X64) + UInt_64 result = 0; + + #if defined(EHS_LITTLE_ENDIAN) + ((UInt_32*)&result)[0] = tsc.lowCount; + ((UInt_32*)&result)[1] = tsc.highCount; + #elif defined(EHS_BIG_ENDIAN) + ((UInt_32*)&result)[0] = tsc.highCount; + ((UInt_32*)&result)[1] = tsc.lowCount; + #endif + + return result; + #elif defined(EHS_ARCH_X86) + return tsc.lowPart; + #endif + #endif + + return 0; + } + + UInt_8 CPU::GetSteppingId() + { + return (UInt_8)GetInfoBits() & 0x00001111; + } + + UInt_8 CPU::GetModelId() + { + return (UInt_8)GetInfoBits() & 0x11110000; + } + + UInt_8 CPU::GetFamilyId() + { + return (UInt_8)(GetInfoBits() >> 8) & 0x00001111; + } + + UInt_8 CPU::GetProcessorTypeId() + { + return (UInt_8)(GetInfoBits() >> 12) & 0x00000011; + } + + UInt_8 CPU::GetExtModelId() + { + return (UInt_8)(GetInfoBits() >> 16) & 0x00001111; + } + + UInt_8 CPU::GetExtFamilyId() + { + return (UInt_8)(GetInfoBits() >> 20); + } + + bool CPU::HasFPU() + { + return GetFeatureBits_1() & 0b00000000000000000000000000000001; + } + + bool CPU::HasVME() + { + return GetFeatureBits_1() & 0b00000000000000000000000000000010; + } + + bool CPU::HasDE() + { + return GetFeatureBits_1() & 0b00000000000000000000000000000100; + } + + bool CPU::HasPSE() + { + return GetFeatureBits_1() & 0b00000000000000000000000000001000; + } + + bool CPU::HasTSC() + { + return GetFeatureBits_1() & 0b00000000000000000000000000010000; + } + + bool CPU::HasMSR() + { + return GetFeatureBits_1() & 0b00000000000000000000000000100000; + } + + bool CPU::HasPAE() + { + return GetFeatureBits_1() & 0b00000000000000000000000001000000; + } + + bool CPU::HasMCE() + { + return GetFeatureBits_1() & 0b00000000000000000000000010000000; + } + + bool CPU::HasCX8() + { + return GetFeatureBits_1() & 0b00000000000000000000000100000000; + } + + bool CPU::HasAPIC() + { + return GetFeatureBits_1() & 0b00000000000000000000001000000000; + } + + bool CPU::HasSEP() + { + return GetFeatureBits_1() & 0b00000000000000000000100000000000; + } + + bool CPU::HasMTRR() + { + return GetFeatureBits_1() & 0b00000000000000000001000000000000; + } + + bool CPU::HasPGE() + { + return GetFeatureBits_1() & 0b00000000000000000010000000000000; + } + + bool CPU::HasMCA() + { + return GetFeatureBits_1() & 0b00000000000000000100000000000000; + } + + bool CPU::HasCMOV() + { + return GetFeatureBits_1() & 0b00000000000000001000000000000000; + } + + bool CPU::HasPAT() + { + return GetFeatureBits_1() & 0b00000000000000010000000000000000; + } + + bool CPU::HasPSE_36() + { + return GetFeatureBits_1() & 0b00000000000000100000000000000000; + } + + bool CPU::HasPSN() + { + return GetFeatureBits_1() & 0b00000000000001000000000000000000; + } + + bool CPU::HasCLFSH() + { + return GetFeatureBits_1() & 0b00000000000010000000000000000000; + } + + bool CPU::HasDS() + { + return GetFeatureBits_1() & 0b00000000001000000000000000000000; + } + + bool CPU::HasACPI() + { + return GetFeatureBits_1() & 0b00000000010000000000000000000000; + } + + bool CPU::HasMMX() + { + return GetFeatureBits_1() & 0b00000000100000000000000000000000; + } + + bool CPU::HasFXSR() + { + return GetFeatureBits_1() & 0b00000001000000000000000000000000; + } + + bool CPU::HasSSE() + { + return GetFeatureBits_1() & 0b00000010000000000000000000000000; + } + + bool CPU::HasSSE2() + { + return GetFeatureBits_1() & 0b00000100000000000000000000000000; + } + + bool CPU::HasSS() + { + return GetFeatureBits_1() & 0b00001000000000000000000000000000; + } + + bool CPU::HasHTT() + { + return GetFeatureBits_1() & 0b00010000000000000000000000000000; + } + + bool CPU::HasTM() + { + return GetFeatureBits_1() & 0b00100000000000000000000000000000; + } + + bool CPU::HasIA64() + { + return GetFeatureBits_1() & 0b01000000000000000000000000000000; + } + + bool CPU::HasPBE() + { + return GetFeatureBits_1() & 0b10000000000000000000000000000000; + } + + bool CPU::HasSSE3() + { + return GetFeatureBits_2() & 0b00000000000000000000000000000001; + } + + bool CPU::HasPCLMULQDQ() + { + return GetFeatureBits_2() & 0b00000000000000000000000000000010; + } + + bool CPU::HasDTES64() + { + return GetFeatureBits_2() & 0b00000000000000000000000000000100; + } + + bool CPU::HasMONITOR() + { + return GetFeatureBits_2() & 0b00000000000000000000000000001000; + } + + bool CPU::HasDS_CPL() + { + return GetFeatureBits_2() & 0b00000000000000000000000000010000; + } + + bool CPU::HasVMX() + { + return GetFeatureBits_2() & 0b00000000000000000000000000100000; + } + + bool CPU::HasSMX() + { + return GetFeatureBits_2() & 0b00000000000000000000000001000000; + } + + bool CPU::HasEST() + { + return GetFeatureBits_2() & 0b00000000000000000000000010000000; + } + + bool CPU::HasTM2() + { + return GetFeatureBits_2() & 0b00000000000000000000000100000000; + } + + bool CPU::HasSSSE3() + { + return GetFeatureBits_2() & 0b00000000000000000000001000000000; + } + + bool CPU::HasCNXT_ID() + { + return GetFeatureBits_2() & 0b00000000000000000000010000000000; + } + + bool CPU::HasSDBG() + { + return GetFeatureBits_2() & 0b00000000000000000000100000000000; + } + + bool CPU::HasFMA() + { + return GetFeatureBits_2() & 0b00000000000000000001000000000000; + } + + bool CPU::HasCX16() + { + return GetFeatureBits_2() & 0b00000000000000000010000000000000; + } + + bool CPU::HasXTPR() + { + return GetFeatureBits_2() & 0b00000000000000000100000000000000; + } + + bool CPU::HasPDCM() + { + return GetFeatureBits_2() & 0b00000000000000001000000000000000; + } + + bool CPU::HasPCID() + { + return GetFeatureBits_2() & 0b00000000000000100000000000000000; + } + + bool CPU::HasDCA() + { + return GetFeatureBits_2() & 0b00000000000001000000000000000000; + } + + bool CPU::HasSSE4_1() + { + return GetFeatureBits_2() & 0b00000000000010000000000000000000; + } + + bool CPU::HasSSE4_2() + { + return GetFeatureBits_2() & 0b00000000000100000000000000000000; + } + + bool CPU::HasX2APIC() + { + return GetFeatureBits_2() & 0b00000000001000000000000000000000; + } + + bool CPU::HasMOVBE() + { + return GetFeatureBits_2() & 0b00000000010000000000000000000000; + } + + bool CPU::HasPOPCNT() + { + return GetFeatureBits_2() & 0b00000000100000000000000000000000; + } + + bool CPU::HasTSC_DEADLINE() + { + return GetFeatureBits_2() & 0b00000001000000000000000000000000; + } + + bool CPU::HasAES() + { + return GetFeatureBits_2() & 0b00000010000000000000000000000000; + } + + bool CPU::HasXSAVE() + { + return GetFeatureBits_2() & 0b00000100000000000000000000000000; + } + + bool CPU::HasOSXSAVE() + { + return GetFeatureBits_2() & 0b00001000000000000000000000000000; + } + + bool CPU::HasAVX() + { + return GetFeatureBits_2() & 0b00010000000000000000000000000000; + } + + bool CPU::HasF16C() + { + return GetFeatureBits_2() & 0b00100000000000000000000000000000; + } + + bool CPU::HasRDRND() + { + return GetFeatureBits_2() & 0b01000000000000000000000000000000; + } + + bool CPU::HasHYPERVISOR() + { + return GetFeatureBits_2() & 0b10000000000000000000000000000000; + } + + bool CPU::HasAVX2() + { + return GetExtFeatureBits_1() & 0b00000000000000000000000000100000; + } + + bool CPU::HasRDSEED() + { + return GetExtFeatureBits_1() & 0b00000000000001000000000000000000; + } + + bool CPU::HasADX() + { + return GetExtFeatureBits_1() & 0b00000000000010000000000000000000; + } + + /* + Str_8 CPU::ToStr() + { + return "Manufacturer: " + GetManufacturer() + "\r\n" + + "Brand: " + GetBrand() + "\r\n" + + "Stepping Id: " + Str_8::FromNum(GetSteppingId()) + "\r\n" + + "GpuModel Id: " + Str_8::FromNum(GetModelId()) + "\r\n" + + "Family Id: " + Str_8::FromNum(GetFamilyId()) + "\r\n" + + "Processor Type Id: " + Str_8::FromNum(GetProcessorTypeId()) + "\r\n" + + "Extended GpuModel Id: " + Str_8::FromNum(GetExtModelId()) + "\r\n" + + "Extended Family Id: " + Str_8::FromNum(GetExtFamilyId()) + "\r\n" + + "Has FPU: " + Str_8::FromNum((UInt_8)HasFPU()) + "\r\n" + + "Has SSE: " + Str_8::FromNum((UInt_8)HasSSE()) + "\r\n" + + "Has SSE 2: " + Str_8::FromNum((UInt_8)HasSSE2()) + "\r\n" + + "Has SSE 3: " + Str_8::FromNum((UInt_8)HasSSE3()) + "\r\n" + + "Has SSSE 3: " + Str_8::FromNum((UInt_8)HasSSSE3()) + "\r\n" + + "Has SSE 4.1: " + Str_8::FromNum((UInt_8)HasSSE4_1()) + "\r\n" + + "Has SSE 4.2: " + Str_8::FromNum((UInt_8)HasSSE4_2()) + "\r\n" + + "Has AVX: " + Str_8::FromNum((UInt_8)HasAVX()) + "\r\n" + + "Has RDRND: " + Str_8::FromNum((UInt_8)HasRDRND()) + "\r\n" + + "Has AVX 2: " + Str_8::FromNum((UInt_8)HasAVX2()) + "\r\n" + + "Has ADX: " + Str_8::FromNum((UInt_8)HasADX()) + "\r\n" + + "Has RDSEED: " + Str_8::FromNum((UInt_8)HasRDSEED()); + } + */ + + UInt_64 CPU::RetrieveTSC_Freq() + { + File tscDatabase("TSC_Frequencies.json", Mode::READ_WRITE, Disposition::CREATE_PERSISTENT); + if (tscDatabase.Size()) + { + Json json(tscDatabase.ReadStr_8(tscDatabase.Size()), 5); + JsonObj* root = (JsonObj*)json.GetValue(); + + Char_8 manu[13]; + manu[12] = 0; + GetManufacturer(manu); + + JsonVar* jManu = root->GetVar(manu); + if (jManu) + { + JsonObj* joManu = (JsonObj*)jManu->GetValue(); + + Char_8 brand[48]; + GetBrand(brand); + + JsonVar* jBrand = joManu->GetVar(brand); + if (jBrand) + { + tscDatabase.Release(); + + return (UInt_64)*(JsonNum*)jBrand->GetValue(); + } + else + { + UInt_64 tscFreq = CalculateTSC_Freq(); + + joManu->AddVar({brand, tscFreq}); + + tscDatabase.SeekBeginning(); + tscDatabase.WriteStr_8(json.ToStr(false)); + tscDatabase.Release(); + + return tscFreq; + } + } + else + { + UInt_64 tscFreq = CalculateTSC_Freq(); + + Char_8 brand[48]; + GetBrand(brand); + + JsonObj cpus(1, 0); + cpus[0] = JsonVar(brand, tscFreq); + + JsonVar tmp({brand, cpus}); + root->AddVar(tmp); + + tscDatabase.SeekBeginning(); + tscDatabase.WriteStr_8(json.ToStr(false)); + tscDatabase.Release(); + + return tscFreq; + } + } + + UInt_64 tscFreq = CalculateTSC_Freq(); + + Char_8 manu[13]; + manu[12] = 0; + GetManufacturer(manu); + + Char_8 brand[49]; + brand[48] = 0; + GetBrand(brand); + + JsonObj jManu(1, 0); + jManu[0] = JsonVar(brand, tscFreq); + + JsonObj root(1, 0); + root[0] = JsonVar(manu, jManu); + + Json json(root); + + tscDatabase.WriteStr_8(json.ToStr(false)); + tscDatabase.Release(); + + return tscFreq; + } + + UInt_64 CPU::CalculateTSC_Freq() + { + UInt_64 result = GetTSC(); + + Thread::SleepFor(10000); + + return (GetTSC() - result) / 10; + } +} \ No newline at end of file diff --git a/src/system/CPU_ARM64.cpp b/src/system/CPU_ARM64.cpp new file mode 100644 index 0000000..cf09004 --- /dev/null +++ b/src/system/CPU_ARM64.cpp @@ -0,0 +1,46 @@ +#include "ehs/system/CPU.h" + +namespace ehs +{ + void CPU::RDTSCP(TSC* tsc) + { + } + + void CPU::GetManufacturer(Char_8* input) + { + } + + UInt_32 CPU::GetInfoBits() + { + return 0; + } + + UInt_32 CPU::GetFeatureBits_1() + { + return 0; + } + + UInt_32 CPU::GetFeatureBits_2() + { + return 0; + } + + UInt_32 CPU::GetExtFeatureBits_1() + { + return 0; + } + + UInt_32 CPU::GetExtFeatureBits_2() + { + return 0; + } + + UInt_32 CPU::GetExtFeatureBits_3() + { + return 0; + } + + void CPU::GetBrand(Char_8* input) + { + } +} \ No newline at end of file diff --git a/src/system/CPU_GCC_AMD64.asm b/src/system/CPU_GCC_AMD64.asm new file mode 100644 index 0000000..8b2592a --- /dev/null +++ b/src/system/CPU_GCC_AMD64.asm @@ -0,0 +1,132 @@ +global _ZN3ehs3CPU6RDTSCPEPNS_3TSCE +global _ZN3ehs3CPU15GetManufacturerEPc +global _ZN3ehs3CPU11GetInfoBitsEv +global _ZN3ehs3CPU16GetFeatureBits_1Ev +global _ZN3ehs3CPU16GetFeatureBits_2Ev +global _ZN3ehs3CPU19GetExtFeatureBits_1Ev +global _ZN3ehs3CPU19GetExtFeatureBits_2Ev +global _ZN3ehs3CPU19GetExtFeatureBits_3Ev +global _ZN3ehs3CPU8GetBrandEPc + +section .text + _ZN3ehs3CPU6RDTSCPEPNS_3TSCE: + RDTSCP + MOV DWORD [RDI], ECX + MOV DWORD [RDI + 4], EDX + MOV DWORD [RDI + 8], EAX + RET + + _ZN3ehs3CPU15GetManufacturerEPc: + PUSH RBX + + XOR EAX, EAX + CPUID + + MOV DWORD [RDI], EBX + MOV DWORD [RDI + 4], EDX + MOV DWORD [RDI + 8], ECX + + POP RBX + + RET + + _ZN3ehs3CPU11GetInfoBitsEv: + PUSH RBX + + MOV EAX, 1 + CPUID + + POP RBX + + RET + + _ZN3ehs3CPU16GetFeatureBits_1Ev: + PUSH RBX + + MOV EAX, 1 + CPUID + + MOV EAX, EDX + + POP RBX + + RET + + _ZN3ehs3CPU16GetFeatureBits_2Ev: + PUSH RBX + + MOV EAX, 1 + CPUID + + MOV EAX, ECX + + POP RBX + + RET + + _ZN3ehs3CPU19GetExtFeatureBits_1Ev: + PUSH RBX + + MOV EAX, 7 + XOR ECX, ECX + CPUID + + MOV EAX, EBX + + POP RBX + + RET + + _ZN3ehs3CPU19GetExtFeatureBits_2Ev: + PUSH RBX + + MOV EAX, 7 + XOR ECX, ECX + CPUID + + MOV EAX, ECX + + POP RBX + + RET + + _ZN3ehs3CPU19GetExtFeatureBits_3Ev: + PUSH RBX + + MOV EAX, 7 + XOR ECX, ECX + CPUID + + MOV EAX, EDX + + POP RBX + + RET + + _ZN3ehs3CPU8GetBrandEPc: + PUSH RBX + + MOV EAX, 80000002h + CPUID + MOV DWORD [RDI], EAX + MOV DWORD [RDI + 4], EBX + MOV DWORD [RDI + 8], ECX + MOV DWORD [RDI + 12], EDX + + MOV EAX, 80000003h + CPUID + MOV DWORD [RDI + 16], EAX + MOV DWORD [RDI + 20], EBX + MOV DWORD [RDI + 24], ECX + MOV DWORD [RDI + 28], EDX + + MOV EAX, 80000004h + CPUID + MOV DWORD [RDI + 32], EAX + MOV DWORD [RDI + 36], EBX + MOV DWORD [RDI + 40], ECX + MOV DWORD [RDI + 44], EDX + + POP RBX + + RET \ No newline at end of file diff --git a/src/system/CPU_MSVC_AMD64.asm b/src/system/CPU_MSVC_AMD64.asm new file mode 100644 index 0000000..67faf9c --- /dev/null +++ b/src/system/CPU_MSVC_AMD64.asm @@ -0,0 +1,127 @@ +global ?GetManufacturer@CPU@ehs@@SAXPEAD@Z +global ?GetInfoBits@CPU@ehs@@SAIXZ +global ?GetFeatureBits_1@CPU@ehs@@SAIXZ +global ?GetFeatureBits_2@CPU@ehs@@SAIXZ +global ?GetExtFeatureBits_1@CPU@ehs@@SAIXZ +global ?GetExtFeatureBits_2@CPU@ehs@@SAKXZ +global ?GetExtFeatureBits_3@CPU@ehs@@SAKXZ +global ?GetBrand@CPU@ehs@@SAXPEAD@Z + +section .text + ?GetManufacturer@CPU@ehs@@SAXPEAD@Z: + PUSH RBX + + XOR EAX, EAX + MOV R8, RCX + CPUID + + MOV DWORD [R8], EBX + MOV DWORD [R8 + 4], EDX + MOV DWORD [R8 + 8], ECX + + POP RBX + + RET + + ?GetInfoBits@CPU@ehs@@SAIXZ: + PUSH RBX + + MOV EAX, 1 + CPUID + + POP RBX + + RET + + ?GetFeatureBits_1@CPU@ehs@@SAIXZ: + PUSH RBX + + MOV EAX, 1 + CPUID + + MOV EAX, EDX + + POP RBX + + RET + + ?GetFeatureBits_2@CPU@ehs@@SAIXZ: + PUSH RBX + + MOV EAX, 1 + CPUID + + MOV EAX, ECX + + POP RBX + + RET + + ?GetExtFeatureBits_1@CPU@ehs@@SAIXZ: + PUSH RBX + + MOV EAX, 7 + XOR ECX, ECX + CPUID + + MOV EAX, EBX + + POP RBX + + RET + + ?GetExtFeatureBits_2@CPU@ehs@@SAKXZ: + PUSH RBX + + MOV EAX, 7 + XOR ECX, ECX + CPUID + + MOV EAX, ECX + + POP RBX + + RET + + ?GetExtFeatureBits_3@CPU@ehs@@SAKXZ: + PUSH RBX + + MOV EAX, 7 + XOR ECX, ECX + CPUID + + MOV EAX, EDX + + POP RBX + + RET + + ?GetBrand@CPU@ehs@@SAXPEAD@Z: + PUSH RBX + + MOV R8, RCX + + MOV EAX, 80000002h + CPUID + MOV DWORD [R8], EAX + MOV DWORD [R8 + 4], EBX + MOV DWORD [R8 + 8], ECX + MOV DWORD [R8 + 12], EDX + + MOV EAX, 80000003h + CPUID + MOV DWORD [R8 + 16], EAX + MOV DWORD [R8 + 20], EBX + MOV DWORD [R8 + 24], ECX + MOV DWORD [R8 + 28], EDX + + MOV EAX, 80000004h + CPUID + MOV DWORD [R8 + 32], EAX + MOV DWORD [R8 + 36], EBX + MOV DWORD [R8 + 40], ECX + MOV DWORD [R8 + 44], EDX + + POP RBX + + RET \ No newline at end of file diff --git a/src/system/FileSystem.cpp b/src/system/FileSystem.cpp new file mode 100644 index 0000000..aee552e --- /dev/null +++ b/src/system/FileSystem.cpp @@ -0,0 +1,32 @@ +#include "ehs/system/FileSystem.h" +#include "ehs/Log.h" + +#include +#include +#include + +namespace ehs +{ + void FileSystem::SetWorkingDir(const Str_8& dir) + { + int code = chdir(dir); + if (code == -1) + EHS_LOG_INT("Error", 0, strerror(errno)); + } + + Str_8 FileSystem::GetWorkingDir() + { + char result[EHS_MAX_PATH]; + + if (!getcwd(result, EHS_MAX_PATH)) + EHS_LOG_INT("Error", 0, strerror(errno)); + + return result; + } + + void FileSystem::SetOwner(const Str_8& dir, const UInt_32 userId, const UInt_32 groupId) + { + if (chown(dir, userId, groupId) == -1) + EHS_LOG_INT("Error", 0, strerror(errno)); + } +} \ No newline at end of file diff --git a/src/system/Mutex_PT.cpp b/src/system/Mutex_PT.cpp new file mode 100644 index 0000000..82ec5b5 --- /dev/null +++ b/src/system/Mutex_PT.cpp @@ -0,0 +1,76 @@ +#include "ehs/system/Mutex_PT.h" + +namespace ehs +{ + Mutex::~Mutex() + { + if (!initialized) + return; + + pthread_mutex_destroy(&hdl); + } + + Mutex::Mutex() + : hdl{} + { + } + + Mutex::Mutex(const Mutex& mutex) + : BaseMutex(mutex), hdl{} + { + } + + Mutex& Mutex::operator=(const Mutex& mutex) + { + if (this == &mutex) + return *this; + + BaseMutex::operator=(mutex); + + hdl = {}; + + return *this; + } + + void Mutex::Initialize() + { + if (initialized) + return; + + pthread_mutex_t hdl = {}; + + int code = pthread_mutex_init(&hdl, nullptr); + + initialized = true; + } + + void Mutex::UnInitialize() + { + if (!initialized) + return; + + pthread_mutex_destroy(&hdl); + + initialized = false; + } + + void Mutex::Lock() + { + if (locked) + return; + + pthread_mutex_lock(&hdl); + + locked = true; + } + + void Mutex::Unlock() + { + if (!locked) + return; + + pthread_mutex_unlock(&hdl); + + locked = false; + } +} \ No newline at end of file diff --git a/src/system/Mutex_W32.cpp b/src/system/Mutex_W32.cpp new file mode 100644 index 0000000..2d28131 --- /dev/null +++ b/src/system/Mutex_W32.cpp @@ -0,0 +1,88 @@ +#include "system/Mutex_W32.h" +#include "ehs/Str.h" +#include "ehs/Log.h" + +namespace ehs +{ + Mutex::~Mutex() + { + if (!initialized) + return; + + if (!CloseHandle(hdl)) + EHS_LOG_INT("Error", 0, "Failed to uninitialize mutex with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + Mutex::Mutex() + : hdl(nullptr) + { + } + + Mutex::Mutex(const Mutex& mutex) + : BaseMutex(mutex), hdl(nullptr) + { + } + + Mutex& Mutex::operator=(const Mutex& mutex) + { + if (this == &mutex) + return *this; + + BaseMutex::operator=(mutex); + + hdl = nullptr; + + return *this; + } + + void Mutex::Initialize() + { + if (initialized) + return; + + hdl = CreateMutexW(nullptr, FALSE, nullptr); + if (!hdl) + { + EHS_LOG_INT("Error", 0, "Failed to create mutex with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + initialized = true; + } + + void Mutex::UnInitialize() + { + if (!initialized) + return; + + if (!CloseHandle(hdl)) + EHS_LOG_INT("Error", 0, "Failed to uninitialize mutex with error #" + Str_8::FromNum(GetLastError()) + "."); + + initialized = false; + } + + void Mutex::Lock() + { + if (locked) + return; + + if (WaitForSingleObject(hdl, EHS_INFINITE) == WAIT_FAILED) + { + EHS_LOG_INT("Error", 0, "Failed to lock mutex with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + + locked = true; + } + + void Mutex::Unlock() + { + if (!locked) + return; + + if (!ReleaseMutex(hdl)) + EHS_LOG_INT("Error", 0, "Failed to unlock mutex with error #" + Str_8::FromNum(GetLastError()) + "."); + + locked = false; + } +} \ No newline at end of file diff --git a/src/system/Open_UNX.cpp b/src/system/Open_UNX.cpp new file mode 100644 index 0000000..cd4f805 --- /dev/null +++ b/src/system/Open_UNX.cpp @@ -0,0 +1,117 @@ +#include "ehs/system/Open_UNX.h" +#include "ehs/Log.h" + +#include + +namespace ehs +{ + Open::~Open() + { + if (!Open::IsInitialize()) + return; + + if (dlclose(hdl)) + EHS_LOG_INT("Error", 0, "Failed to close."); + } + + Open::Open() + : hdl(nullptr) + { + } + + Open::Open(Str_8 filePath) + : BaseOpen((Str_8&&)filePath), hdl(nullptr) + { + Open::Initialize(); + } + + Open::Open(Open&& o) noexcept + : BaseOpen((BaseOpen&&)o), hdl(o.hdl) + { + o.hdl = nullptr; + } + + Open::Open(const Open& o) + : BaseOpen(o), hdl(nullptr) + { + Open::Initialize(); + } + + Open& Open::operator=(Open&& o) noexcept + { + if (this == &o) + return *this; + + Open::Release(); + + BaseOpen::operator=((BaseOpen&&)o); + + hdl = o.hdl; + + o.hdl = nullptr; + + return *this; + } + + Open& Open::operator=(const Open& o) + { + if (this == &o) + return *this; + + Open::Release(); + + BaseOpen::operator=(o); + + hdl = nullptr; + + Open::Initialize(); + + return *this; + } + + void Open::Initialize() + { + if (IsInitialize()) + return; + + hdl = dlopen(filePath, RTLD_LAZY); + if (!hdl) + { + EHS_LOG_INT("Error", 0, dlerror()); + return; + } + } + + void Open::Release() + { + if (!IsInitialize()) + return; + + if (dlclose(hdl)) + EHS_LOG_INT("Error", 0, "Failed to close."); + + hdl = nullptr; + } + + void* Open::Retrieve(Str_8 symbol) + { + if (!IsInitialize()) + return nullptr; + + void* func = dlsym(hdl, symbol); + if (!func) + { + dlerror(); + EHS_LOG_INT("Error", 0, "Undefined symbol, \"" + symbol + "\"."); + Release(); + return nullptr; + } + + return func; + } + + bool Open::IsInitialize() const + { + return hdl; + } +} \ No newline at end of file diff --git a/src/system/Open_W32.cpp b/src/system/Open_W32.cpp new file mode 100644 index 0000000..b2b579c --- /dev/null +++ b/src/system/Open_W32.cpp @@ -0,0 +1,5 @@ +#include "system/Open_W32.h" + +namespace ehs +{ +} \ No newline at end of file diff --git a/src/system/Semaphore_P.cpp b/src/system/Semaphore_P.cpp new file mode 100644 index 0000000..2841e0b --- /dev/null +++ b/src/system/Semaphore_P.cpp @@ -0,0 +1,163 @@ +#include "ehs/system/Semaphore_P.h" +#include "ehs/Log.h" + +#include +#include +#include + +namespace ehs +{ + Semaphore::~Semaphore() + { + if (!Semaphore::IsValid()) + return; + + if (sem_destroy(&hdl) == -1) + EHS_LOG_INT("Error", 0, "Failed to release semaphore with error #" + Str_8::FromNum(errno) + "."); + + valid = false; + } + + Semaphore::Semaphore() + : hdl{}, valid(false) + { + } + + Semaphore::Semaphore(const Str_8& name, const UInt_32 initial) + : BaseSemaphore(name, initial), hdl{}, valid(false) + { + Semaphore::Initialize(); + } + + Semaphore::Semaphore(const UInt_32 initial) + : BaseSemaphore(initial), hdl{}, valid(false) + { + Semaphore::Initialize(); + } + + Semaphore::Semaphore(Semaphore&& sem) noexcept + : BaseSemaphore((Semaphore&&)sem), hdl(sem.hdl), valid(sem.valid) + { + sem.hdl = {}; + sem.valid = false; + } + + Semaphore::Semaphore(const Semaphore& sem) + : BaseSemaphore(sem), hdl{}, valid(false) + { + Semaphore::Initialize(); + } + + Semaphore& Semaphore::operator=(Semaphore&& sem) noexcept + { + if (this == &sem) + return *this; + + Release(); + + BaseSemaphore::operator=((Semaphore&&)sem); + + hdl = sem.hdl; + valid = sem.valid; + + sem.hdl = {}; + sem.valid = false; + + return *this; + } + + Semaphore& Semaphore::operator=(const Semaphore& sem) + { + if (this == &sem) + return *this; + + Release(); + + BaseSemaphore::operator=(sem); + + hdl = {}; + valid = false; + + Initialize(); + + return *this; + } + + void Semaphore::Initialize() + { + if (IsValid()) + return; + + if (GetName().Size()) + { + sem_t* result = sem_open("/" + GetName(), O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, GetInitial()); + if (!result) + EHS_LOG_INT("Error", 0, "Failed to create semaphore with error #" + Str_8::FromNum(errno) + "."); + + hdl = *result; + } + else + { + if (sem_init(&hdl, 0, GetInitial()) == -1) + EHS_LOG_INT("Error", 0, "Failed to create semaphore with error #" + Str_8::FromNum(errno) + "."); + } + + valid = true; + } + + void Semaphore::Release() + { + if (!IsValid()) + return; + + if (sem_destroy(&hdl) == -1) + EHS_LOG_INT("Error", 0, "Failed to release semaphore with error #" + Str_8::FromNum(errno) + "."); + + hdl = {}; + + valid = false; + } + + void Semaphore::Signal(const UInt_32 inc) + { + if (!IsValid()) + return; + + if (sem_post(&hdl) == -1) + EHS_LOG_INT("Error", 0, "Failed to signal semaphore with error #" + Str_8::FromNum(errno) + "."); + } + + bool Semaphore::Wait(const UInt_32 timeout) + { + if (!IsValid()) + return false; + + int result; + + if (timeout == EHS_INFINITE) + { + result = sem_wait(&hdl); + } + else + { + timespec time = {timeout / 1000, timeout % 1000 * 1000000}; + result = sem_timedwait(&hdl, &time); + } + + if (result == -1) + { + int code = errno; + if (code != ETIMEDOUT) + EHS_LOG_INT("Error", 0, "Failed to wait for semaphore with error #" + Str_8::FromNum(errno) + "."); + + return false; + } + + return true; + } + + bool Semaphore::IsValid() const + { + return valid; + } +} \ No newline at end of file diff --git a/src/system/Semaphore_W32.cpp b/src/system/Semaphore_W32.cpp new file mode 100644 index 0000000..5f001b2 --- /dev/null +++ b/src/system/Semaphore_W32.cpp @@ -0,0 +1,136 @@ +#include "system/Semaphore_W32.h" +#include "ehs/UTF.h" +#include "ehs/Log.h" + +namespace ehs +{ + Semaphore::~Semaphore() + { + if (!hdl) + return; + + if (!CloseHandle(hdl)) + { + DWORD code = GetLastError(); + EHS_LOG_INT("Error", 0, Str_8("Failed to free semaphore with error #") + code + "."); + } + } + + Semaphore::Semaphore() + : hdl(nullptr) + { + Semaphore::Initialize(); + } + + Semaphore::Semaphore(Str_8 name, const UInt_32 initial) + : BaseSemaphore(std::move(name), initial), hdl(nullptr) + { + Semaphore::Initialize(); + } + + Semaphore::Semaphore(const UInt_32 initial) + : BaseSemaphore(initial), hdl(nullptr) + { + Semaphore::Initialize(); + } + + Semaphore::Semaphore(Semaphore&& sem) noexcept + : BaseSemaphore(std::move(sem)), hdl(sem.hdl) + { + sem.hdl = nullptr; + } + + Semaphore::Semaphore(const Semaphore& sem) + : BaseSemaphore(sem), hdl(nullptr) + { + } + + Semaphore& Semaphore::operator=(Semaphore&& sem) noexcept + { + if (this == &sem) + return *this; + + Release(); + + BaseSemaphore::operator=(std::move(sem)); + + hdl = sem.hdl; + + sem.hdl = nullptr; + + return *this; + } + + Semaphore& Semaphore::operator=(const Semaphore& sem) + { + if (this == &sem) + return *this; + + Release(); + + BaseSemaphore::operator=(sem); + + hdl = nullptr; + + return *this; + } + + void Semaphore::Initialize() + { + if (IsValid()) + return; + + hdl = CreateSemaphoreW(nullptr, (LONG)GetInitial(), EHS_SINT_32_MAX, UTF::To_16(GetName())); + if (!hdl) + EHS_LOG_INT("Error", 0, Str_8("Failed to create semaphore with error #") + GetLastError() + "."); + } + + void Semaphore::Release() + { + if (!IsValid()) + return; + + if (!CloseHandle(hdl)) + { + DWORD code = GetLastError(); + EHS_LOG_INT("Error", 0, Str_8("Failed to free semaphore with error #") + code + "."); + } + hdl = nullptr; + } + + void Semaphore::Signal(const UInt_32 inc) + { + if (!IsValid()) + return; + + if (!ReleaseSemaphore(hdl, (LONG)inc, nullptr)) + EHS_LOG_INT("Error", 0, Str_8("Failed to release semaphore with error #") + GetLastError() + "."); + } + + bool Semaphore::Wait(const UInt_32 timeout) + { + if (!IsValid()) + return false; + + DWORD result = WaitForSingleObject(hdl, timeout); + if (result == WAIT_ABANDONED) + { + EHS_LOG_INT("Error", 0, "Wait abandoned."); + return false; + } + else if (result == WAIT_FAILED) + { + EHS_LOG_INT("Error", 1, Str_8("Wait failed with error #") + GetLastError() + "."); + return false; + } + else if (result == WAIT_TIMEOUT) + return false; + + return true; + } + + bool Semaphore::IsValid() const + { + return hdl; + } +} \ No newline at end of file diff --git a/src/system/System_LNX.cpp b/src/system/System_LNX.cpp new file mode 100644 index 0000000..49e3c98 --- /dev/null +++ b/src/system/System_LNX.cpp @@ -0,0 +1,22 @@ +#include "ehs/system/System_LNX.h" +#include "ehs/system/Thread.h" +#include "ehs/Log.h" + +namespace ehs +{ + UInt_32 XDG_Thread(void* args) + { + Str_8* uri = (Str_8*)args; + + system("xdg-open \"" + *uri + "\""); + + return 0; + } + + void System::OpenURI(const Str_8& uri) + { + Thread xdg; + xdg.Start(XDG_Thread, (void*)&uri); + xdg.Detach(); + } +} \ No newline at end of file diff --git a/src/system/System_W32.cpp b/src/system/System_W32.cpp new file mode 100644 index 0000000..0cb4dd0 --- /dev/null +++ b/src/system/System_W32.cpp @@ -0,0 +1,11 @@ +#include "system/System_W32.h" + +#include + +namespace ehs +{ + void System::OpenURI(const Str_8& uri) + { + ShellExecuteA(nullptr, "open", uri, nullptr, nullptr, SW_SHOW); + } +} \ No newline at end of file diff --git a/src/system/Thread.cpp b/src/system/Thread.cpp new file mode 100644 index 0000000..5b902ad --- /dev/null +++ b/src/system/Thread.cpp @@ -0,0 +1,284 @@ +#include "ehs/system/Thread.h" +#include "ehs/system/CPU.h" + +#if defined(EHS_OS_WINDOWS) + #include +#elif defined(EHS_OS_LINUX) + #include + #include +#endif + +namespace ehs +{ + UInt_32 Thread::mainId = GetCurrentId(); + + #ifdef EHS_OS_WINDOWS + Handle Thread::mainTaskHdl = nullptr; + UInt_32 Thread::mainTaskIndex = 0; + #endif + + Thread::~Thread() + { + Join(); + } + + Thread::Thread(const UInt_64 stackSize) + : stackSize(stackSize), hdl(EHS_INVALID_THREAD), id(0) + #ifdef EHS_OS_WINDOWS + ,taskHdl(nullptr), taskIndex(0) + #endif + { + } + + Thread::Thread(const Thread& thread) + : stackSize(thread.stackSize), hdl(EHS_INVALID_THREAD), id(0) + #ifdef EHS_OS_WINDOWS + , taskHdl(nullptr), taskIndex(0) + #endif + { + } + + Thread& Thread::operator=(const Thread& thread) + { + if (this == &thread) + return *this; + + stackSize = thread.stackSize; + hdl = EHS_INVALID_THREAD; + id = 0; + + #ifdef EHS_OS_WINDOWS + taskHdl = nullptr; + taskIndex = 0; + #endif + + return* this; + } + + void Thread::Start(UInt_32 (*cb)(void*), void* args) + { + #if defined(EHS_OS_WINDOWS) + hdl = CreateThread(nullptr, stackSize, (LPTHREAD_START_ROUTINE)cb, args, 0, (DWORD*)&id); + if (!hdl) + EHS_LOG_INT("Error", 0, "Failed to start thread with error #" + Str_8::FromNum(GetLastError()) + "."); + #elif defined(EHS_OS_LINUX) + UInt_64* rArgs = new UInt_64[sizeof(UInt_64) * 2]; + rArgs[0] = (UInt_64)cb; + rArgs[1] = (UInt_64)args; + + pthread_create((pthread_t*)&hdl, nullptr, Redirect, (void*)rArgs); + #endif + } + + bool Thread::Join(const unsigned int timeout) + { + if (hdl == EHS_INVALID_THREAD) + return false; + + #if defined(EHS_WINDOWS) + unsigned int r = WaitForSingleObject(hdl, timeout); + if (r == WAIT_ABANDONED) + { + EHS_LOG_INT("Error", 0, "Abandoned wait because a mutex was not released."); + return false; + } + else if (r == WAIT_TIMEOUT) + { + return false; + } + else if (r == WAIT_FAILED) + { + EHS_LOG_INT("Error", 1, "Failed to wait for thread with error #" + Str_8::FromNum(GetLastError()) + "."); + return false; + } + + mainTaskIndex = 0; + #elif defined(EHS_OS_LINUX) + int code = pthread_join((pthread_t)hdl, nullptr); + if (code != 0) + EHS_LOG_INT("Error", 1, "Failed to wait for thread with error #" + Str_8::FromNum(code) + "."); + + hdl = EHS_INVALID_THREAD; + #endif + + return true; + } + + void Thread::Detach() + { + if (!hdl) + return; + + #if defined(EHS_OS_WINDOWS) + if (!CloseHandle(hdl)) + { + EHS_LOG_INT("Error", 0, "Failed to detach thread with error #" + Str_8::FromNum(GetLastError()) + "."); + return; + } + #elif defined(EHS_OS_LINUX) + pthread_detach(hdl); + + hdl = EHS_INVALID_THREAD; + #endif + } + + UInt_64 Thread::GetStackSize() const + { + return stackSize; + } + + THandle Thread::GetHandle() const + { + return hdl; + } + + UInt_32 Thread::GetId() const + { + return id; + } + + bool Thread::IsCurrent() const + { + return id == GetCurrentId(); + } + + bool Thread::IsValid() const + { + return hdl; + } + + #ifdef EHS_OS_WINDOWS + void Thread::SetTaskType_32(const Str_32& task) + { + if (!IsCurrent()) + return; + + taskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&taskIndex); + if (!taskHdl) + EHS_LOG_INT("Error", 0, "Failed to set the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void Thread::SetTaskType_16(const Str_16& task) + { + if (!IsCurrent()) + return; + + taskHdl = AvSetMmThreadCharacteristicsW(task, (LPDWORD)&taskIndex); + if (!taskHdl) + EHS_LOG_INT("Error", 0, "Failed to set the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void Thread::SetTaskType_8(const Str_8& task) + { + if (!IsCurrent()) + return; + + taskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&taskIndex); + if (!taskHdl) + EHS_LOG_INT("Error", 0, "Failed to set the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void Thread::RevertTaskType() + { + if (!IsCurrent()) + return; + + if (!AvRevertMmThreadCharacteristics(taskHdl)) + EHS_LOG_INT("Error", 0, "Failed to revert the thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + + taskIndex = 0; + } + #endif + + UInt_32 Thread::GetMainId() + { + return mainId; + } + + UInt_64 Thread::GetCurrentId() + { + #if defined(EHS_OS_WINDOWS) + return GetCurrentThreadId(); + #elif defined(EHS_OS_LINUX) + return pthread_self(); + #endif + } + + #ifdef EHS_OS_WINDOWS + void Thread::SetMainTaskType_32(const Str_32& task) + { + if (GetCurrentId() != mainId) + return; + + mainTaskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&mainTaskIndex); + if (!mainTaskHdl) + EHS_LOG_INT("Error", 0, "Failed to set the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void Thread::SetMainTaskType_16(const Str_16& task) + { + if (GetCurrentId() != mainId) + return; + + mainTaskHdl = AvSetMmThreadCharacteristicsW(task, (LPDWORD)&mainTaskIndex); + if (!mainTaskHdl) + EHS_LOG_INT("Error", 0, "Failed to set the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void Thread::SetMainTaskType_8(const Str_8& task) + { + if (GetCurrentId() != mainId) + return; + + mainTaskHdl = AvSetMmThreadCharacteristicsW(UTF::To_16(task), (LPDWORD)&mainTaskIndex); + if (!mainTaskHdl) + EHS_LOG_INT("Error", 0, "Failed to set the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + } + + void Thread::RevertMainTaskType() + { + if (GetCurrentId() != mainId) + return; + + if (!AvRevertMmThreadCharacteristics(mainTaskHdl)) + EHS_LOG_INT("Error", 0, "Failed to revert the main thread's characteristics with error #" + Str_8::FromNum(GetLastError()) + "."); + + mainTaskIndex = 0; + } + #endif + + float Thread::HardSleepFor(const float seconds) + { + UInt_64 freq = CPU::GetTSC_Freq(); + UInt_64 start = CPU::GetTSC(); + + float elapsed = 0.0f; + while ((elapsed = (float)(CPU::GetTSC() - start) / (float)freq) <= seconds); + + return elapsed; + } + + void Thread::SleepFor(const UInt_32 miliseconds) + { + #if defined(EHS_OS_WINDOWS) + Sleep(miliseconds); + #elif defined(EHS_OS_LINUX) + timespec req = {miliseconds / 1000, miliseconds % 1000 * 1000000}; + nanosleep(&req, nullptr); + #endif + } + + void* Thread::Redirect(void *args) + { + UInt_64* rArgs = (UInt_64*)args; + UInt_32 (*cb)(void*) = (UInt_32 (*)(void*))rArgs[0]; + void* params = (void*)rArgs[1]; + + UInt_32* code = new UInt_32(); + *code = cb(params); + + delete[] rArgs; + + return code; + } +} \ No newline at end of file diff --git a/src/system/User.cpp b/src/system/User.cpp new file mode 100644 index 0000000..9065cb1 --- /dev/null +++ b/src/system/User.cpp @@ -0,0 +1,40 @@ +#include "ehs/system/User.h" +#include "ehs/Log.h" + +#include +#include +#include + +namespace ehs +{ + void User::GetId(UInt_32* const real, UInt_32* const effective, UInt_32* const saved) + { + if (getresuid(real, effective, saved) == -1) + EHS_LOG_INT("Error", 0, strerror(errno)); + } + + Str_8 User::GetName() + { + SInt_64 max = sysconf(_SC_LOGIN_NAME_MAX); + if (max == -1) + { + EHS_LOG_INT("Error", 0, strerror(errno)); + return {}; + } + + Char_8* name = new Char_8[max]; + + if (getlogin_r(name, max) == -1) + { + delete[] name; + EHS_LOG_INT("Error", 1, strerror(errno)); + return {}; + } + + Str_8 result(name); + + delete[] name; + + return result; + } +} \ No newline at end of file